bpf: Add __uninit kfunc annotation

This patch adds __uninit as a kfunc annotation.

This will be useful for scenarios such as for example in dynptrs,
indicating whether the dynptr should be checked by the verifier as an
initialized or an uninitialized dynptr.

Without this annotation, the alternative would be needing to hard-code
in the verifier the specific kfunc to indicate that arg should be
treated as an uninitialized arg.

Signed-off-by: Joanne Koong <joannelkoong@gmail.com>
Link: https://lore.kernel.org/r/20230301154953.641654-7-joannelkoong@gmail.com
Signed-off-by: Alexei Starovoitov <ast@kernel.org>
This commit is contained in:
Joanne Koong 2023-03-01 07:49:49 -08:00 committed by Alexei Starovoitov
parent 485ec51ef9
commit d96d937d7c
2 changed files with 33 additions and 2 deletions

View File

@ -100,6 +100,23 @@ Hence, whenever a constant scalar argument is accepted by a kfunc which is not a
size parameter, and the value of the constant matters for program safety, __k size parameter, and the value of the constant matters for program safety, __k
suffix should be used. suffix should be used.
2.2.2 __uninit Annotation
--------------------
This annotation is used to indicate that the argument will be treated as
uninitialized.
An example is given below::
__bpf_kfunc int bpf_dynptr_from_skb(..., struct bpf_dynptr_kern *ptr__uninit)
{
...
}
Here, the dynptr will be treated as an uninitialized dynptr. Without this
annotation, the verifier will reject the program if the dynptr passed in is
not initialized.
.. _BPF_kfunc_nodef: .. _BPF_kfunc_nodef:
2.3 Using an existing kernel function 2.3 Using an existing kernel function

View File

@ -8727,6 +8727,11 @@ static bool is_kfunc_arg_alloc_obj(const struct btf *btf, const struct btf_param
return __kfunc_param_match_suffix(btf, arg, "__alloc"); return __kfunc_param_match_suffix(btf, arg, "__alloc");
} }
static bool is_kfunc_arg_uninit(const struct btf *btf, const struct btf_param *arg)
{
return __kfunc_param_match_suffix(btf, arg, "__uninit");
}
static bool is_kfunc_arg_scalar_with_name(const struct btf *btf, static bool is_kfunc_arg_scalar_with_name(const struct btf *btf,
const struct btf_param *arg, const struct btf_param *arg,
const char *name) const char *name)
@ -9662,17 +9667,26 @@ static int check_kfunc_args(struct bpf_verifier_env *env, struct bpf_kfunc_call_
return ret; return ret;
break; break;
case KF_ARG_PTR_TO_DYNPTR: case KF_ARG_PTR_TO_DYNPTR:
{
enum bpf_arg_type dynptr_arg_type = ARG_PTR_TO_DYNPTR;
if (reg->type != PTR_TO_STACK && if (reg->type != PTR_TO_STACK &&
reg->type != CONST_PTR_TO_DYNPTR) { reg->type != CONST_PTR_TO_DYNPTR) {
verbose(env, "arg#%d expected pointer to stack or dynptr_ptr\n", i); verbose(env, "arg#%d expected pointer to stack or dynptr_ptr\n", i);
return -EINVAL; return -EINVAL;
} }
ret = process_dynptr_func(env, regno, insn_idx, if (reg->type == CONST_PTR_TO_DYNPTR)
ARG_PTR_TO_DYNPTR | MEM_RDONLY); dynptr_arg_type |= MEM_RDONLY;
if (is_kfunc_arg_uninit(btf, &args[i]))
dynptr_arg_type |= MEM_UNINIT;
ret = process_dynptr_func(env, regno, insn_idx, dynptr_arg_type);
if (ret < 0) if (ret < 0)
return ret; return ret;
break; break;
}
case KF_ARG_PTR_TO_LIST_HEAD: case KF_ARG_PTR_TO_LIST_HEAD:
if (reg->type != PTR_TO_MAP_VALUE && if (reg->type != PTR_TO_MAP_VALUE &&
reg->type != (PTR_TO_BTF_ID | MEM_ALLOC)) { reg->type != (PTR_TO_BTF_ID | MEM_ALLOC)) {