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