mirror of
https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
synced 2026-07-21 21:38:04 -04:00
bpf: Reject offset refcount acquire arguments
bpf_refcount_acquire() increments the refcount at the caller-supplied
pointer plus the refcount field offset, then returns the caller-supplied
pointer unchanged.
The verifier records the return value as a base pointer to the refcounted
object.
bpf_list_pop_front() and bpf_rbtree_remove() can return embedded
graph-node pointers as PTR_TO_BTF_ID | MEM_ALLOC with a fixed offset equal
to the node field offset. Passing such a pointer directly to
bpf_refcount_acquire() currently passes the refcounted-kptr type check.
That makes the runtime operation start from base + node_off while the
verifier models the returned pointer as the object base.
Require refcount-acquire arguments to have zero fixed offset by carrying
the requirement through check_func_arg_reg_off() to __check_ptr_off_reg().
Programs can still acquire a refcount from a graph-node-derived pointer
after normalizing it with container_of().
Fixes: 7c50b1cb76 ("bpf: Add bpf_refcount_acquire kfunc")
Signed-off-by: Yiyang Chen <chenyy23@mails.tsinghua.edu.cn>
Acked-by: Eduard Zingerman <eddyz87@gmail.com>
Acked-by: Yonghong Song <yonghong.song@linux.dev>
Link: https://lore.kernel.org/r/2f894647f56f71838fdddeb97a3e057ed35ea92e.1782192383.git.chenyy23@mails.tsinghua.edu.cn
Signed-off-by: Alexei Starovoitov <ast@kernel.org>
This commit is contained in:
committed by
Alexei Starovoitov
parent
66bb952b44
commit
931a577fc7
@@ -7996,9 +7996,10 @@ reg_find_field_offset(const struct bpf_reg_state *reg, s32 off, u32 fields)
|
||||
return field;
|
||||
}
|
||||
|
||||
static int check_func_arg_reg_off(struct bpf_verifier_env *env,
|
||||
const struct bpf_reg_state *reg, argno_t argno,
|
||||
enum bpf_arg_type arg_type)
|
||||
static int __check_func_arg_reg_off(struct bpf_verifier_env *env,
|
||||
const struct bpf_reg_state *reg, argno_t argno,
|
||||
enum bpf_arg_type arg_type,
|
||||
bool btf_id_fixed_off_ok)
|
||||
{
|
||||
u32 type = reg->type;
|
||||
|
||||
@@ -8055,12 +8056,11 @@ static int check_func_arg_reg_off(struct bpf_verifier_env *env,
|
||||
case PTR_TO_BTF_ID | MEM_ALLOC | NON_OWN_REF | MEM_RCU:
|
||||
/* When referenced PTR_TO_BTF_ID is passed to release function,
|
||||
* its fixed offset must be 0. In the other cases, fixed offset
|
||||
* can be non-zero. This was already checked above. So pass
|
||||
* fixed_off_ok as true to allow fixed offset for all other
|
||||
* cases. var_off always must be 0 for PTR_TO_BTF_ID, hence we
|
||||
* still need to do checks instead of returning.
|
||||
* can be non-zero unless the caller requires otherwise.
|
||||
* var_off always must be 0 for PTR_TO_BTF_ID, hence we still
|
||||
* need to do checks instead of returning.
|
||||
*/
|
||||
return __check_ptr_off_reg(env, reg, argno, true);
|
||||
return __check_ptr_off_reg(env, reg, argno, btf_id_fixed_off_ok);
|
||||
case PTR_TO_CTX:
|
||||
/*
|
||||
* Allow fixed and variable offsets for syscall context, but
|
||||
@@ -8076,6 +8076,13 @@ static int check_func_arg_reg_off(struct bpf_verifier_env *env,
|
||||
}
|
||||
}
|
||||
|
||||
static int check_func_arg_reg_off(struct bpf_verifier_env *env,
|
||||
const struct bpf_reg_state *reg, argno_t argno,
|
||||
enum bpf_arg_type arg_type)
|
||||
{
|
||||
return __check_func_arg_reg_off(env, reg, argno, arg_type, true);
|
||||
}
|
||||
|
||||
static int check_arg_const_str(struct bpf_verifier_env *env,
|
||||
struct bpf_reg_state *reg, argno_t argno)
|
||||
{
|
||||
@@ -11947,6 +11954,7 @@ static int check_kfunc_args(struct bpf_verifier_env *env, struct bpf_kfunc_call_
|
||||
enum bpf_arg_type arg_type = ARG_DONTCARE;
|
||||
argno_t argno = argno_from_arg(i + 1);
|
||||
int regno = reg_from_argno(argno);
|
||||
bool btf_id_fixed_off_ok = true;
|
||||
u32 ref_id, type_size;
|
||||
bool is_ret_buf_sz = false;
|
||||
int kf_arg_type;
|
||||
@@ -12120,7 +12128,6 @@ static int check_kfunc_args(struct bpf_verifier_env *env, struct bpf_kfunc_call_
|
||||
case KF_ARG_PTR_TO_MEM:
|
||||
case KF_ARG_PTR_TO_MEM_SIZE:
|
||||
case KF_ARG_PTR_TO_CALLBACK:
|
||||
case KF_ARG_PTR_TO_REFCOUNTED_KPTR:
|
||||
case KF_ARG_PTR_TO_CONST_STR:
|
||||
case KF_ARG_PTR_TO_WORKQUEUE:
|
||||
case KF_ARG_PTR_TO_TIMER:
|
||||
@@ -12134,6 +12141,10 @@ static int check_kfunc_args(struct bpf_verifier_env *env, struct bpf_kfunc_call_
|
||||
case KF_ARG_PTR_TO_CTX:
|
||||
arg_type = ARG_PTR_TO_CTX;
|
||||
break;
|
||||
case KF_ARG_PTR_TO_REFCOUNTED_KPTR:
|
||||
arg_type = ARG_PTR_TO_BTF_ID;
|
||||
btf_id_fixed_off_ok = false;
|
||||
break;
|
||||
default:
|
||||
verifier_bug(env, "unknown kfunc arg type %d", kf_arg_type);
|
||||
return -EFAULT;
|
||||
@@ -12141,7 +12152,8 @@ static int check_kfunc_args(struct bpf_verifier_env *env, struct bpf_kfunc_call_
|
||||
|
||||
if (regno == meta->release_regno)
|
||||
arg_type |= OBJ_RELEASE;
|
||||
ret = check_func_arg_reg_off(env, reg, argno, arg_type);
|
||||
ret = __check_func_arg_reg_off(env, reg, argno, arg_type,
|
||||
btf_id_fixed_off_ok);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
|
||||
Reference in New Issue
Block a user