mirror of
https://github.com/torvalds/linux.git
synced 2026-09-22 04:34:03 +02:00
bpf: Tag nullable kfunc pointer args with PTR_MAYBE_NULL
Now that get_kfunc_ptr_arg_type() classifies a kfunc pointer argument from its BTF alone, express a nullable argument by OR-ing PTR_MAYBE_NULL into the classified type, and resolve a NULL register after classification instead of before it. Previously check_kfunc_args() short-circuited a nullable argument passed a NULL register with a continue placed before get_kfunc_ptr_arg_type(), so the NULL never reached classification. That kept a register-state decision (bpf_register_is_null()) ahead of the BTF-based classification. This mirrors how helper arguments carry PTR_MAYBE_NULL in their bpf_arg_type and is a step toward describing kfuncs with a bpf_func_proto: the nullability now travels with the per-argument classification, so it is captured when the prototype is generated at add-call time. Signed-off-by: Amery Hung <ameryhung@gmail.com> Acked-by: Eduard Zingerman <eddyz87@gmail.com> Link: https://lore.kernel.org/bpf/20260801074633.1595644-17-ameryhung@gmail.com Signed-off-by: Kumar Kartikeya Dwivedi <memxor@gmail.com>
This commit is contained in:
parent
c9e995ba0d
commit
ba5b99470c
|
|
@ -11353,98 +11353,85 @@ get_kfunc_ptr_arg_type(struct bpf_verifier_env *env, struct bpf_call_arg_meta *m
|
|||
const char *ref_tname, const struct btf_param *args,
|
||||
int arg, int nargs, argno_t argno)
|
||||
{
|
||||
if (meta->func_id == special_kfunc_list[KF_bpf_cast_to_kern_ctx] ||
|
||||
meta->func_id == special_kfunc_list[KF_bpf_session_is_return] ||
|
||||
meta->func_id == special_kfunc_list[KF_bpf_session_cookie])
|
||||
return KF_ARG_PTR_TO_CTX;
|
||||
int arg_type;
|
||||
|
||||
/* In this function, we verify the kfunc's BTF as per the argument type,
|
||||
* leaving the rest of the verification with respect to the register
|
||||
* type to our caller. When a set of conditions hold in the BTF type of
|
||||
* arguments, we resolve it to a known kfunc_ptr_arg_type.
|
||||
*/
|
||||
if (btf_is_prog_ctx_type(&env->log, meta->btf, t, resolve_prog_type(env->prog), arg))
|
||||
return KF_ARG_PTR_TO_CTX;
|
||||
|
||||
if (is_kfunc_arg_alloc_obj(meta->btf, &args[arg]))
|
||||
return KF_ARG_PTR_TO_ALLOC_BTF_ID;
|
||||
|
||||
if (is_kfunc_arg_refcounted_kptr(meta->btf, &args[arg]))
|
||||
return KF_ARG_PTR_TO_REFCOUNTED_KPTR;
|
||||
|
||||
if (is_kfunc_arg_dynptr(meta->btf, &args[arg]))
|
||||
return KF_ARG_PTR_TO_DYNPTR;
|
||||
|
||||
if (is_kfunc_arg_iter(meta, arg, &args[arg]))
|
||||
return KF_ARG_PTR_TO_ITER;
|
||||
|
||||
if (is_kfunc_arg_list_head(meta->btf, &args[arg]))
|
||||
return KF_ARG_PTR_TO_LIST_HEAD;
|
||||
|
||||
if (is_kfunc_arg_list_node(meta->btf, &args[arg]))
|
||||
return KF_ARG_PTR_TO_LIST_NODE;
|
||||
|
||||
if (is_kfunc_arg_rbtree_root(meta->btf, &args[arg]))
|
||||
return KF_ARG_PTR_TO_RB_ROOT;
|
||||
|
||||
if (is_kfunc_arg_rbtree_node(meta->btf, &args[arg]))
|
||||
return KF_ARG_PTR_TO_RB_NODE;
|
||||
|
||||
if (is_kfunc_arg_const_str(meta->btf, &args[arg]))
|
||||
return KF_ARG_PTR_TO_CONST_STR;
|
||||
|
||||
if (is_kfunc_arg_const_map(meta->btf, &args[arg]))
|
||||
return KF_ARG_CONST_MAP_PTR;
|
||||
|
||||
if (is_kfunc_arg_map(meta->btf, &args[arg]))
|
||||
return KF_ARG_PTR_TO_BTF_ID;
|
||||
|
||||
if (is_kfunc_arg_wq(meta->btf, &args[arg]))
|
||||
return KF_ARG_PTR_TO_WORKQUEUE;
|
||||
|
||||
if (is_kfunc_arg_timer(meta->btf, &args[arg]))
|
||||
return KF_ARG_PTR_TO_TIMER;
|
||||
|
||||
if (is_kfunc_arg_task_work(meta->btf, &args[arg]))
|
||||
return KF_ARG_PTR_TO_TASK_WORK;
|
||||
|
||||
if (is_kfunc_arg_irq_flag(meta->btf, &args[arg]))
|
||||
return KF_ARG_PTR_TO_IRQ_FLAG;
|
||||
|
||||
if (is_kfunc_arg_res_spin_lock(meta->btf, &args[arg]))
|
||||
return KF_ARG_PTR_TO_RES_SPIN_LOCK;
|
||||
|
||||
if (is_kfunc_arg_callback(env, meta->btf, &args[arg]))
|
||||
return KF_ARG_PTR_TO_CALLBACK;
|
||||
|
||||
if (arg + 1 < nargs &&
|
||||
(is_kfunc_arg_mem_size(meta->btf, &args[arg + 1]) ||
|
||||
is_kfunc_arg_const_mem_size(meta->btf, &args[arg + 1]))) {
|
||||
if (meta->func_id == special_kfunc_list[KF_bpf_cast_to_kern_ctx] ||
|
||||
meta->func_id == special_kfunc_list[KF_bpf_session_is_return] ||
|
||||
meta->func_id == special_kfunc_list[KF_bpf_session_cookie])
|
||||
arg_type = KF_ARG_PTR_TO_CTX;
|
||||
else if (btf_is_prog_ctx_type(&env->log, meta->btf, t, resolve_prog_type(env->prog), arg))
|
||||
arg_type = KF_ARG_PTR_TO_CTX;
|
||||
else if (is_kfunc_arg_alloc_obj(meta->btf, &args[arg]))
|
||||
arg_type = KF_ARG_PTR_TO_ALLOC_BTF_ID;
|
||||
else if (is_kfunc_arg_refcounted_kptr(meta->btf, &args[arg]))
|
||||
arg_type = KF_ARG_PTR_TO_REFCOUNTED_KPTR;
|
||||
else if (is_kfunc_arg_dynptr(meta->btf, &args[arg]))
|
||||
arg_type = KF_ARG_PTR_TO_DYNPTR;
|
||||
else if (is_kfunc_arg_iter(meta, arg, &args[arg]))
|
||||
arg_type = KF_ARG_PTR_TO_ITER;
|
||||
else if (is_kfunc_arg_list_head(meta->btf, &args[arg]))
|
||||
arg_type = KF_ARG_PTR_TO_LIST_HEAD;
|
||||
else if (is_kfunc_arg_list_node(meta->btf, &args[arg]))
|
||||
arg_type = KF_ARG_PTR_TO_LIST_NODE;
|
||||
else if (is_kfunc_arg_rbtree_root(meta->btf, &args[arg]))
|
||||
arg_type = KF_ARG_PTR_TO_RB_ROOT;
|
||||
else if (is_kfunc_arg_rbtree_node(meta->btf, &args[arg]))
|
||||
arg_type = KF_ARG_PTR_TO_RB_NODE;
|
||||
else if (is_kfunc_arg_const_str(meta->btf, &args[arg]))
|
||||
arg_type = KF_ARG_PTR_TO_CONST_STR;
|
||||
else if (is_kfunc_arg_const_map(meta->btf, &args[arg]))
|
||||
arg_type = KF_ARG_CONST_MAP_PTR;
|
||||
else if (is_kfunc_arg_map(meta->btf, &args[arg]))
|
||||
arg_type = KF_ARG_PTR_TO_BTF_ID;
|
||||
else if (is_kfunc_arg_wq(meta->btf, &args[arg]))
|
||||
arg_type = KF_ARG_PTR_TO_WORKQUEUE;
|
||||
else if (is_kfunc_arg_timer(meta->btf, &args[arg]))
|
||||
arg_type = KF_ARG_PTR_TO_TIMER;
|
||||
else if (is_kfunc_arg_task_work(meta->btf, &args[arg]))
|
||||
arg_type = KF_ARG_PTR_TO_TASK_WORK;
|
||||
else if (is_kfunc_arg_irq_flag(meta->btf, &args[arg]))
|
||||
arg_type = KF_ARG_PTR_TO_IRQ_FLAG;
|
||||
else if (is_kfunc_arg_res_spin_lock(meta->btf, &args[arg]))
|
||||
arg_type = KF_ARG_PTR_TO_RES_SPIN_LOCK;
|
||||
else if (is_kfunc_arg_callback(env, meta->btf, &args[arg]))
|
||||
arg_type = KF_ARG_PTR_TO_CALLBACK;
|
||||
else if (arg + 1 < nargs &&
|
||||
(is_kfunc_arg_mem_size(meta->btf, &args[arg + 1]) ||
|
||||
is_kfunc_arg_const_mem_size(meta->btf, &args[arg + 1]))) {
|
||||
if (!btf_type_is_void(ref_t) && !btf_type_is_scalar(ref_t) &&
|
||||
!__btf_type_is_scalar_struct(env, meta->btf, ref_t, 0)) {
|
||||
verbose(env, "%s pointer type %s %s must point to void, scalar, or struct with scalar\n",
|
||||
reg_arg_name(env, argno), btf_type_str(ref_t), ref_tname);
|
||||
return -EINVAL;
|
||||
}
|
||||
return KF_ARG_PTR_TO_MEM;
|
||||
arg_type = KF_ARG_PTR_TO_MEM;
|
||||
} else if (btf_type_is_struct(ref_t))
|
||||
/* A pointer to a struct without a size argument is classified as KF_ARG_PTR_TO_BTF_ID */
|
||||
arg_type = KF_ARG_PTR_TO_BTF_ID;
|
||||
else {
|
||||
/*
|
||||
* Otherwise this is a fixed-size memory buffer supported by
|
||||
* check_helper_mem_access(): a pointer to a scalar or a struct of
|
||||
* scalars. The access size is derived from the pointed-to BTF type.
|
||||
*/
|
||||
if (!btf_type_is_scalar(ref_t) &&
|
||||
!__btf_type_is_scalar_struct(env, meta->btf, ref_t, 0)) {
|
||||
verbose(env, "%s pointer type %s %s must point to scalar, or struct with scalar\n",
|
||||
reg_arg_name(env, argno), btf_type_str(ref_t), ref_tname);
|
||||
return -EINVAL;
|
||||
}
|
||||
arg_type = KF_ARG_PTR_TO_MEM | MEM_FIXED_SIZE;
|
||||
}
|
||||
|
||||
/* A pointer to a struct without a size argument is classified as KF_ARG_PTR_TO_BTF_ID */
|
||||
if (btf_type_is_struct(ref_t))
|
||||
return KF_ARG_PTR_TO_BTF_ID;
|
||||
if (is_kfunc_arg_nullable(meta->btf, &args[arg]))
|
||||
arg_type |= PTR_MAYBE_NULL;
|
||||
|
||||
/*
|
||||
* Otherwise this is a fixed-size memory buffer supported by
|
||||
* check_helper_mem_access(): a pointer to a scalar or a struct of
|
||||
* scalars. The access size is derived from the pointed-to BTF type.
|
||||
*/
|
||||
if (!btf_type_is_scalar(ref_t) &&
|
||||
!__btf_type_is_scalar_struct(env, meta->btf, ref_t, 0)) {
|
||||
verbose(env, "%s pointer type %s %s must point to scalar, or struct with scalar\n",
|
||||
reg_arg_name(env, argno), btf_type_str(ref_t), ref_tname);
|
||||
return -EINVAL;
|
||||
}
|
||||
return KF_ARG_PTR_TO_MEM | MEM_FIXED_SIZE;
|
||||
return arg_type;
|
||||
}
|
||||
|
||||
static int process_kf_arg_ptr_to_btf_id(struct bpf_verifier_env *env,
|
||||
|
|
@ -12132,14 +12119,14 @@ static int check_kfunc_args(struct bpf_verifier_env *env, struct bpf_call_arg_me
|
|||
ref_t = btf_type_skip_modifiers(btf, t->type, &ref_id);
|
||||
ref_tname = btf_name_by_offset(btf, ref_t->name_off);
|
||||
|
||||
if (is_kfunc_arg_nullable(meta->btf, &args[i]) && bpf_register_is_null(reg))
|
||||
continue;
|
||||
|
||||
kf_arg_type = get_kfunc_ptr_arg_type(env, meta, t, ref_t, ref_tname,
|
||||
args, i, nargs, argno);
|
||||
if (kf_arg_type < 0)
|
||||
return kf_arg_type;
|
||||
|
||||
if (bpf_register_is_null(reg) && type_may_be_null(kf_arg_type))
|
||||
continue;
|
||||
|
||||
if (is_kfunc_arg_map(btf, &args[i])) {
|
||||
ref_id = *reg2btf_ids[CONST_PTR_TO_MAP];
|
||||
ref_t = btf_type_by_id(btf_vmlinux, ref_id);
|
||||
|
|
|
|||
Loading…
Reference in New Issue
Block a user