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 <[email protected]>
---
 include/linux/bpf.h   |  3 +++
 kernel/bpf/verifier.c | 18 +++++++++++-------
 2 files changed, 14 insertions(+), 7 deletions(-)

diff --git a/include/linux/bpf.h b/include/linux/bpf.h
index 7719f6528..b9b7d19cb 100644
--- a/include/linux/bpf.h
+++ b/include/linux/bpf.h
@@ -859,6 +859,9 @@ enum bpf_type_flag {
        /* DYNPTR points to file */
        DYNPTR_TYPE_FILE        = BIT(20 + BPF_BASE_TYPE_BITS),
 
+       /* PTR argument cannot have a fixed offset. */
+       PTR_ZERO_OFF            = BIT(21 + BPF_BASE_TYPE_BITS),
+
        __BPF_TYPE_FLAG_MAX,
        __BPF_TYPE_LAST_FLAG    = __BPF_TYPE_FLAG_MAX - 1,
 };
diff --git a/kernel/bpf/verifier.c b/kernel/bpf/verifier.c
index 2abc79dbf..b41aee8c6 100644
--- a/kernel/bpf/verifier.c
+++ b/kernel/bpf/verifier.c
@@ -7994,8 +7994,11 @@ 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 fixed_off_ok = !(arg_type & PTR_ZERO_OFF);
        u32 type = reg->type;
 
+       arg_type &= ~PTR_ZERO_OFF;
+
        /* When referenced register is passed to release function, its fixed
         * offset must be 0.
         *
@@ -8048,13 +8051,12 @@ static int check_func_arg_reg_off(struct 
bpf_verifier_env *env,
        case PTR_TO_BTF_ID | MEM_ALLOC | NON_OWN_REF:
        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.
+                * or when the argument type requires zero fixed offset, its
+                * fixed offset must be 0. In the other cases, fixed offset can
+                * be non-zero. 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, fixed_off_ok);
        case PTR_TO_CTX:
                /*
                 * Allow fixed and variable offsets for syscall context, but
@@ -12114,7 +12116,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:
@@ -12128,6 +12129,9 @@ 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 | PTR_ZERO_OFF;
+                       break;
                default:
                        verifier_bug(env, "unknown kfunc arg type %d", 
kf_arg_type);
                        return -EFAULT;
-- 
2.34.1


Reply via email to