]> git.ipfire.org Git - thirdparty/linux.git/commitdiff
bpf: Reject offset refcount acquire arguments
authorYiyang Chen <chenyy23@mails.tsinghua.edu.cn>
Tue, 23 Jun 2026 06:11:09 +0000 (06:11 +0000)
committerAlexei Starovoitov <ast@kernel.org>
Fri, 26 Jun 2026 00:51:20 +0000 (17:51 -0700)
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: 7c50b1cb76aca ("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>
kernel/bpf/verifier.c

index 21a365d436a595202dcb392716228617b56ce7ba..3cdc2e90f643eac57d2ab0e6314f2cbef6fee056 100644 (file)
@@ -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;