* [PATCH bpf-next v1 00/22] bpf: Unify helper and kfunc argument checks
@ 2026-09-05 22:00 Amery Hung
2026-09-05 22:00 ` [PATCH bpf-next v1 01/22] bpf: Pass call metadata through shared " Amery Hung
` (22 more replies)
0 siblings, 23 replies; 54+ messages in thread
From: Amery Hung @ 2026-09-05 22:00 UTC (permalink / raw)
To: bpf
Cc: alexei.starovoitov, andrii, daniel, eddyz87, memxor, ameryhung,
kernel-team
Hi,
This is the 3rd and final patchset unifying helepr and kfunc checks.
Helper and kfunc calls currently validate arguments through separate
loops despite enforcing many of the same contracts. This duplicates
type admission, nullability, memory, BTF, packet-access, and resource
ownership handling.
This series complete the generation of helper-style argument metadata
for kfuncs and then routes both call kinds through check_func_args().
Call-specific behavior remains selected through call metadata where the
helper and kfunc contracts intentionally differ.
The series is organized as follows:
- Patches 1-8 prepare shared call metadata, argument indexing, and
argument kinds.
- Patches 9-13 populate kfunc argument flags, BTF IDs, and fixed sizes
while generating the kfunc prototype.
- Patches 14-18 consolidate runtime type resolution, nullability, BTF
matching, and register admission.
- Patches 19-21 consolidate packet-access and release validation, with
coverage for kfunc packet writes.
- Patch 22 replaces the duplicate helper and kfunc loops with the
common check_func_args() path.
[1/3] https://lore.kernel.org/bpf/20260715064047.1793790-1-ameryhung@gmail.com/
[2/3] https://lore.kernel.org/bpf/20260801074633.1595644-1-ameryhung@gmail.com/
Amery Hung (22):
bpf: Pass call metadata through shared argument checks
bpf: Address check_func_arg() arguments by argno
bpf: Only compare func_id against BPF_FUNC_* for helper calls
bpf: Only compare func_id against kfunc BTF IDs for kfunc calls
bpf: Rename ambiguous function argument types
bpf: Unify kfunc argument kinds with enum bpf_arg_type
bpf: Align helper and kfunc ARG_PTR_TO_PROG_AUX handling
bpf: Classify kfunc arguments the verifier ignores
bpf: Set OBJ_RELEASE when generating kfunc argument types
bpf: Set MEM_UNINIT and dynptr subtypes when generating kfunc arg
types
bpf: Set MEM_RCU when generating kfunc argument types
bpf: Resolve BTF ID of ARG_PTR_TO_BTF_ID in kfunc bpf_func_proto
bpf: Resolve ARG_PTR_TO_MEM | MEM_FIXED_SIZE size in kfunc
bpf_func_proto
bpf: Consolidate runtime argument type resolution
bpf: Consolidate nullable argument validation
bpf: Drop redundant BTF pointer helper write rejection
bpf: Consolidate helper and kfunc PTR_TO_BTF_ID argument matching
bpf: Admit kfunc argument registers through check_reg_type()
selftests/bpf: Test kfunc packet memory direct writes
bpf: Consolidate function call pkt_access validation
bpf: Consolidate release argument validation
bpf: Check helper and kfunc arguments in one path
drivers/media/rc/bpf-lirc.c | 10 +-
include/linux/bpf.h | 32 +-
include/linux/bpf_verifier.h | 1 -
kernel/bpf/backtrack.c | 2 +-
kernel/bpf/bpf_cgrp_storage.c | 2 +-
kernel/bpf/bpf_inode_storage.c | 2 +-
kernel/bpf/bpf_iter.c | 6 +-
kernel/bpf/bpf_lsm.c | 2 +-
kernel/bpf/bpf_task_storage.c | 2 +-
kernel/bpf/btf.c | 6 +-
kernel/bpf/cgroup.c | 6 +-
kernel/bpf/core.c | 2 +-
kernel/bpf/diagnostics.c | 29 +
kernel/bpf/diagnostics.h | 1 +
kernel/bpf/fixups.c | 5 +-
kernel/bpf/helpers.c | 47 +-
kernel/bpf/ringbuf.c | 20 +-
kernel/bpf/stackmap.c | 14 +-
kernel/bpf/syscall.c | 6 +-
kernel/bpf/task_iter.c | 4 +-
kernel/bpf/verifier.c | 2032 ++++++++---------
kernel/trace/bpf_trace.c | 46 +-
net/core/bpf_sk_storage.c | 6 +-
net/core/filter.c | 302 +--
net/core/sock_map.c | 16 +-
net/ipv4/bpf_tcp_ca.c | 2 +-
.../testing/selftests/bpf/prog_tests/bpf_nf.c | 14 +-
.../selftests/bpf/prog_tests/cb_refs.c | 4 +-
.../selftests/bpf/prog_tests/kfunc_call.c | 2 +-
.../selftests/bpf/prog_tests/verifier.c | 2 +
.../testing/selftests/bpf/progs/arena_kfunc.c | 2 +-
.../selftests/bpf/progs/cgrp_kfunc_failure.c | 8 +-
.../selftests/bpf/progs/cpumask_failure.c | 4 +-
tools/testing/selftests/bpf/progs/irq.c | 4 +-
tools/testing/selftests/bpf/progs/iters.c | 6 +-
.../selftests/bpf/progs/iters_testmod.c | 7 +-
.../selftests/bpf/progs/map_kptr_fail.c | 10 +-
.../bpf/progs/mem_rdonly_untrusted.c | 3 +-
.../testing/selftests/bpf/progs/rbtree_fail.c | 4 +-
.../bpf/progs/refcounted_kptr_fail.c | 4 +-
.../selftests/bpf/progs/res_spin_lock_fail.c | 2 +-
.../testing/selftests/bpf/progs/stream_fail.c | 2 +-
.../selftests/bpf/progs/task_kfunc_failure.c | 10 +-
.../selftests/bpf/progs/task_work_fail.c | 2 +-
.../bpf/progs/test_kfunc_dynptr_param.c | 2 +-
.../selftests/bpf/progs/verifier_ctx.c | 2 +-
.../bpf/progs/verifier_global_ptr_args.c | 2 +-
.../progs/verifier_helper_access_var_len.c | 4 +-
.../bpf/progs/verifier_helper_packet_access.c | 4 +-
.../bpf/progs/verifier_kfunc_packet_access.c | 47 +
.../selftests/bpf/progs/verifier_live_stack.c | 2 +-
.../selftests/bpf/progs/verifier_map_in_map.c | 3 +-
.../bpf/progs/verifier_map_lookup_refine.c | 2 +-
.../bpf/progs/verifier_ref_tracking.c | 6 +-
.../selftests/bpf/progs/verifier_sock.c | 10 +-
.../selftests/bpf/progs/verifier_vfs_reject.c | 6 +-
.../selftests/bpf/progs/wakeup_source_fail.c | 2 +-
.../testing/selftests/bpf/progs/wq_failures.c | 4 +-
tools/testing/selftests/bpf/verifier/calls.c | 4 +-
.../testing/selftests/bpf/verifier/map_kptr.c | 4 +-
60 files changed, 1380 insertions(+), 1417 deletions(-)
create mode 100644 tools/testing/selftests/bpf/progs/verifier_kfunc_packet_access.c
--
2.52.0
^ permalink raw reply [flat|nested] 54+ messages in thread
* [PATCH bpf-next v1 01/22] bpf: Pass call metadata through shared argument checks
2026-09-05 22:00 [PATCH bpf-next v1 00/22] bpf: Unify helper and kfunc argument checks Amery Hung
@ 2026-09-05 22:00 ` Amery Hung
2026-09-05 22:00 ` [PATCH bpf-next v1 02/22] bpf: Address check_func_arg() arguments by argno Amery Hung
` (21 subsequent siblings)
22 siblings, 0 replies; 54+ messages in thread
From: Amery Hung @ 2026-09-05 22:00 UTC (permalink / raw)
To: bpf
Cc: alexei.starovoitov, andrii, daniel, eddyz87, memxor, ameryhung,
kernel-team
Helper, kfunc, and global subprogram argument paths supply callee
identity and state differently. check_reg_type() receives the call name
separately from bpf_call_arg_meta, while process_dynptr_func() receives
the call name, referenced-object state, and dynptr state as separate
arguments.
Populate meta->func_name for helper and global subprogram calls, keep
one metadata object for the entire global subprogram argument check, and
have both shared checking functions obtain their name and state from the
metadata. This gives dynptr validation one interface for every call kind
in preparation for unifying their argument-checking paths.
No functional change.
Signed-off-by: Amery Hung <ameryhung@gmail.com>
---
kernel/bpf/verifier.c | 67 +++++++++++++++++++++----------------------
1 file changed, 32 insertions(+), 35 deletions(-)
diff --git a/kernel/bpf/verifier.c b/kernel/bpf/verifier.c
index 5b51e7ee1a3f..f49c90beefc5 100644
--- a/kernel/bpf/verifier.c
+++ b/kernel/bpf/verifier.c
@@ -7618,11 +7618,11 @@ __printf(6, 7) static void bpf_diag_call_arg_fmt(struct bpf_verifier_env *env, u
/*
* Validate dynptr arguments for helper, kfunc and subprog.
*
- * @dynptr is both input and output. It is populated when the argument is
- * tagged with MEM_UNINIT (i.e., the dynptr argument that will be constructed)
- * and consumed when the argument is expecting to be an initialized dynptr.
- * @parent_id is used to track the referenced parent object (e.g., file or skb in
- * qdisc program) when constructing a dynptr.
+ * @meta carries the dynptr and referenced-object state. The dynptr is populated
+ * when the argument is tagged with MEM_UNINIT (i.e., the dynptr argument that
+ * will be constructed) and consumed when the argument is expected to be an
+ * initialized dynptr. The reference tracks the parent object (e.g., file or skb
+ * in qdisc program) when constructing a dynptr.
*
* There are two register types representing a bpf_dynptr, one is PTR_TO_STACK
* which points to a stack slot, and the other is CONST_PTR_TO_DYNPTR.
@@ -7639,9 +7639,8 @@ __printf(6, 7) static void bpf_diag_call_arg_fmt(struct bpf_verifier_env *env, u
* and checked dynamically during runtime.
*/
static int process_dynptr_func(struct bpf_verifier_env *env, struct bpf_reg_state *reg,
- argno_t argno, int insn_idx, const char *call_name,
- enum bpf_arg_type arg_type,
- struct ref_obj_desc *ref_obj, struct bpf_dynptr_desc *dynptr)
+ argno_t argno, int insn_idx, enum bpf_arg_type arg_type,
+ struct bpf_call_arg_meta *meta)
{
int spi, err = 0;
@@ -7650,7 +7649,7 @@ static int process_dynptr_func(struct bpf_verifier_env *env, struct bpf_reg_stat
"%s expected pointer to stack or const struct bpf_dynptr\n",
reg_arg_name(env, argno));
bpf_diag_call_arg_fmt(
- env, insn_idx, argno, call_name,
+ env, insn_idx, argno, meta->func_name,
"Pass the address of a stack dynptr object, or use a const dynptr pointer returned by the verifier-supported path.",
"a dynptr argument must be a pointer to a dynptr stack slot or a verifier-provided const struct bpf_dynptr, but %s is %s",
reg_arg_name(env, argno), bpf_diag_reg_type_plain(env, reg->type));
@@ -7691,7 +7690,8 @@ static int process_dynptr_func(struct bpf_verifier_env *env, struct bpf_reg_stat
return err;
}
- err = mark_stack_slots_dynptr(env, reg, arg_type, insn_idx, ref_obj, dynptr);
+ err = mark_stack_slots_dynptr(env, reg, arg_type, insn_idx,
+ &meta->ref_obj, &meta->dynptr);
} else /* OBJ_RELEASE and None case from above */ {
/* For the reg->type == PTR_TO_STACK case, bpf_dynptr is never const */
if (reg->type == CONST_PTR_TO_DYNPTR && (arg_type & OBJ_RELEASE)) {
@@ -7721,7 +7721,7 @@ static int process_dynptr_func(struct bpf_verifier_env *env, struct bpf_reg_stat
verbose(env, "Expected a dynptr of type %s as %s\n",
dynptr_type_str(expected_type), reg_arg_name(env, argno));
bpf_diag_call_arg_fmt(
- env, insn_idx, argno, call_name,
+ env, insn_idx, argno, meta->func_name,
"Use a dynptr constructor that matches this operation, or call an operation that accepts the dynptr's current type.",
"the dynptr is initialized with backing object type %s, but this operation expects dynptr type %s",
dynptr_type_str(actual_type), dynptr_type_str(expected_type));
@@ -7740,11 +7740,9 @@ static int process_dynptr_func(struct bpf_verifier_env *env, struct bpf_reg_stat
reg = &state->stack[spi].spilled_ptr;
}
- if (dynptr) {
- dynptr->type = reg->dynptr.type;
- dynptr->id = reg->id;
- dynptr->parent_id = reg->parent_id;
- }
+ meta->dynptr.type = reg->dynptr.type;
+ meta->dynptr.id = reg->id;
+ meta->dynptr.parent_id = reg->parent_id;
}
return err;
}
@@ -8336,7 +8334,7 @@ static const char *bpf_diag_expected_reg_types(struct bpf_verifier_env *env,
static int check_reg_type(struct bpf_verifier_env *env, struct bpf_reg_state *reg, argno_t argno,
enum bpf_arg_type arg_type, const u32 *arg_btf_id,
- struct bpf_call_arg_meta *meta, const char *call_name)
+ struct bpf_call_arg_meta *meta)
{
enum bpf_reg_type expected, type = reg->type;
const struct bpf_reg_types *compatible;
@@ -8389,7 +8387,7 @@ static int check_reg_type(struct bpf_verifier_env *env, struct bpf_reg_state *re
verbose(env, "%s\n", reg_type_str(env, compatible->types[j]));
actual = bpf_diag_fmt(env, "%s", reg_type_str(env, reg->type));
accepted = bpf_diag_expected_reg_types(env, compatible->types, i);
- bpf_diag_call_arg_fmt(env, env->insn_idx, argno, call_name,
+ bpf_diag_call_arg_fmt(env, env->insn_idx, argno, meta->func_name,
"Pass a value with one of the accepted pointer or scalar types for this call.",
"it has type %s, but this argument accepts %s",
actual, accepted);
@@ -8403,7 +8401,7 @@ static int check_reg_type(struct bpf_verifier_env *env, struct bpf_reg_state *re
if (!(arg_type & MEM_RDONLY)) {
verbose(env,
"%s() may write into memory pointed by %s type=%s\n",
- func_id_name(meta->func_id),
+ meta->func_name,
reg_arg_name(env, argno), reg_type_str(env, reg->type));
return -EACCES;
}
@@ -8430,7 +8428,7 @@ static int check_reg_type(struct bpf_verifier_env *env, struct bpf_reg_state *re
verbose(env, "Possibly NULL pointer passed to helper %s\n",
reg_arg_name(env, argno));
bpf_diag_call_arg(
- env, env->insn_idx, argno, call_name,
+ env, env->insn_idx, argno, meta->func_name,
"the pointer may be NULL, but this call requires a non-NULL pointer",
"Add a NULL check and make the call only on the non-NULL path.");
return -EACCES;
@@ -8819,8 +8817,7 @@ static int check_func_arg(struct bpf_verifier_env *env, u32 arg,
base_type(arg_type) == ARG_PTR_TO_SPIN_LOCK)
arg_btf_id = fn->arg_btf_id[arg];
- err = check_reg_type(env, reg, argno, arg_type, arg_btf_id, meta,
- func_id_name(meta->func_id));
+ err = check_reg_type(env, reg, argno, arg_type, arg_btf_id, meta);
if (err)
return err;
@@ -8832,9 +8829,9 @@ static int check_func_arg(struct bpf_verifier_env *env, u32 arg,
if (arg_type_is_release(arg_type) && !arg_type_is_dynptr(arg_type) &&
!reg_is_referenced(env, reg) && !bpf_register_is_null(reg)) {
verbose(env, "release helper %s expects referenced PTR_TO_BTF_ID passed to %s\n",
- func_id_name(meta->func_id), reg_arg_name(env, argno));
+ meta->func_name, reg_arg_name(env, argno));
bpf_diag_call_arg(
- env, insn_idx, argno, func_id_name(meta->func_id),
+ env, insn_idx, argno, meta->func_name,
"release helpers require a value that owns a live resource returned by a matching acquire helper",
"Pass the resource-owning pointer returned by the matching acquire helper, and avoid calling the release helper after ownership has already been transferred or released.");
return -EINVAL;
@@ -8965,8 +8962,7 @@ static int check_func_arg(struct bpf_verifier_env *env, u32 arg,
true, meta, NULL);
break;
case ARG_PTR_TO_DYNPTR:
- err = process_dynptr_func(env, reg, argno, insn_idx, func_id_name(meta->func_id),
- arg_type, &meta->ref_obj, &meta->dynptr);
+ err = process_dynptr_func(env, reg, argno, insn_idx, arg_type, meta);
if (err)
return err;
break;
@@ -9713,12 +9709,16 @@ static int btf_check_func_arg_match(struct bpf_verifier_env *env, int subprog,
struct bpf_subprog_info *sub = subprog_info(env, subprog);
struct bpf_func_state *caller = cur_func(env);
struct bpf_verifier_log *log = &env->log;
- struct ref_obj_desc ref_obj = {};
const struct btf_param *args;
const struct btf_type *func, *func_proto;
+ struct bpf_call_arg_meta meta;
u32 i;
int ret, err;
+ /* Leave btf and func_id zero: this is neither a helper nor a kfunc. */
+ memset(&meta, 0, sizeof(meta));
+ meta.func_name = bpf_subprog_name(env, subprog);
+
ret = btf_prepare_func_args(env, subprog);
if (ret) {
if (bpf_in_stack_arg_cnt(sub) > 0) {
@@ -9802,20 +9802,16 @@ static int btf_check_func_arg_match(struct bpf_verifier_env *env, int subprog,
return ret;
ret = process_dynptr_func(env, reg, argno, env->insn_idx,
- bpf_subprog_name(env, subprog), arg->arg_type,
- &ref_obj, NULL);
+ arg->arg_type, &meta);
if (ret)
return ret;
} else if (base_type(arg->arg_type) == ARG_PTR_TO_BTF_ID) {
- struct bpf_call_arg_meta meta;
int err;
if (bpf_register_is_null(reg) && type_may_be_null(arg->arg_type))
continue;
- memset(&meta, 0, sizeof(meta)); /* leave func_id as zero */
- err = check_reg_type(env, reg, argno, arg->arg_type, &arg->btf_id, &meta,
- bpf_subprog_name(env, subprog));
+ err = check_reg_type(env, reg, argno, arg->arg_type, &arg->btf_id, &meta);
err = err ?: check_func_arg_reg_off(env, reg, argno, arg->arg_type);
if (err)
return err;
@@ -10929,6 +10925,7 @@ static int check_helper_call(struct bpf_verifier_env *env, struct bpf_insn *insn
env->insn_aux_data[insn_idx].non_sleepable = true;
meta.func_id = func_id;
+ meta.func_name = func_id_name(func_id);
meta.fn = fn;
/* check args */
for (i = 0; i < MAX_BPF_FUNC_REG_ARGS; i++) {
@@ -13078,8 +13075,8 @@ static int check_kfunc_args(struct bpf_verifier_env *env, struct bpf_call_arg_me
dynptr_arg_type |= (unsigned int)get_dynptr_type_flag(parent_type);
}
- ret = process_dynptr_func(env, reg, argno, insn_idx, func_name,
- dynptr_arg_type, &meta->ref_obj, &meta->dynptr);
+ ret = process_dynptr_func(env, reg, argno, insn_idx,
+ dynptr_arg_type, meta);
if (ret < 0)
return ret;
break;
--
2.52.0
^ permalink raw reply related [flat|nested] 54+ messages in thread
* [PATCH bpf-next v1 02/22] bpf: Address check_func_arg() arguments by argno
2026-09-05 22:00 [PATCH bpf-next v1 00/22] bpf: Unify helper and kfunc argument checks Amery Hung
2026-09-05 22:00 ` [PATCH bpf-next v1 01/22] bpf: Pass call metadata through shared " Amery Hung
@ 2026-09-05 22:00 ` Amery Hung
2026-09-05 22:44 ` bot+bpf-ci
2026-09-05 22:00 ` [PATCH bpf-next v1 03/22] bpf: Only compare func_id against BPF_FUNC_* for helper calls Amery Hung
` (20 subsequent siblings)
22 siblings, 1 reply; 54+ messages in thread
From: Amery Hung @ 2026-09-05 22:00 UTC (permalink / raw)
To: bpf
Cc: alexei.starovoitov, andrii, daniel, eddyz87, memxor, ameryhung,
kernel-team
check_func_arg() derives every argument from BPF_REG_1 + arg. That works
for helpers, which have at most five arguments, but kfunc arguments
beyond the fifth are passed on the stack.
Switch to the argno_t addressing used by check_kfunc_args(): obtain
argument state with get_func_arg_reg(), use argno for diagnostics, and
call check_reg_arg() only when the argument is held in a register. This
prepares check_func_arg() to be shared with kfunc verification.
There is no functional change for helpers. reg_arg_name() renders their
register arguments as R%d, so the updated diagnostics remain unchanged.
Signed-off-by: Amery Hung <ameryhung@gmail.com>
---
kernel/bpf/verifier.c | 27 +++++++++++++++------------
1 file changed, 15 insertions(+), 12 deletions(-)
diff --git a/kernel/bpf/verifier.c b/kernel/bpf/verifier.c
index f49c90beefc5..30e1f7a7553e 100644
--- a/kernel/bpf/verifier.c
+++ b/kernel/bpf/verifier.c
@@ -8769,10 +8769,12 @@ static int check_func_arg(struct bpf_verifier_env *env, u32 arg,
int insn_idx)
{
const struct bpf_func_proto *fn = meta->fn;
- u32 regno = BPF_REG_1 + arg;
- struct bpf_reg_state *reg = reg_state(env, regno);
+ struct bpf_func_state *caller = cur_func(env);
+ struct bpf_reg_state *regs = cur_regs(env);
+ argno_t argno = argno_from_arg(arg + 1);
+ struct bpf_reg_state *reg = get_func_arg_reg(caller, regs, arg);
enum bpf_arg_type arg_type = fn->arg_type[arg];
- argno_t argno = argno_from_reg(regno);
+ int regno = reg_from_argno(argno);
enum bpf_reg_type type = reg->type;
u32 *arg_btf_id = NULL;
u32 key_size;
@@ -8786,9 +8788,9 @@ static int check_func_arg(struct bpf_verifier_env *env, u32 arg,
return err;
if (arg_type == ARG_ANYTHING) {
- if (is_pointer_value(env, regno)) {
- verbose(env, "R%d leaks addr into helper function\n",
- regno);
+ if (__is_pointer_value(env->allow_ptr_leaks, reg)) {
+ verbose(env, "%s leaks addr into helper function\n",
+ reg_arg_name(env, argno));
return -EACCES;
}
return 0;
@@ -8904,7 +8906,8 @@ static int check_func_arg(struct bpf_verifier_env *env, u32 arg,
break;
case ARG_PTR_TO_PERCPU_BTF_ID:
if (!reg->btf_id) {
- verbose(env, "Helper has invalid btf_id in R%d\n", regno);
+ verbose(env, "Helper has invalid btf_id in %s\n",
+ reg_arg_name(env, argno));
return -EACCES;
}
meta->ret_btf = reg->btf;
@@ -8941,7 +8944,7 @@ static int check_func_arg(struct bpf_verifier_env *env, u32 arg,
* next is_mem_size argument below.
*/
if (arg_type & MEM_FIXED_SIZE) {
- err = check_mem_reg(env, reg, argno_from_reg(regno), fn->arg_size[arg],
+ err = check_mem_reg(env, reg, argno, fn->arg_size[arg],
arg_type & MEM_WRITE ? BPF_WRITE : BPF_READ, meta, NULL);
if (err)
return err;
@@ -8950,14 +8953,14 @@ static int check_func_arg(struct bpf_verifier_env *env, u32 arg,
}
break;
case ARG_MEM_SIZE:
- err = check_mem_size_reg(env, reg_state(env, regno - 1), reg,
- argno_from_reg(regno - 1), argno,
+ err = check_mem_size_reg(env, get_func_arg_reg(caller, regs, arg - 1), reg,
+ argno_from_arg(arg), argno,
fn->arg_type[arg - 1] & MEM_WRITE ? BPF_WRITE : BPF_READ,
false, meta, NULL);
break;
case ARG_MEM_SIZE_OR_ZERO:
- err = check_mem_size_reg(env, reg_state(env, regno - 1), reg,
- argno_from_reg(regno - 1), argno,
+ err = check_mem_size_reg(env, get_func_arg_reg(caller, regs, arg - 1), reg,
+ argno_from_arg(arg), argno,
fn->arg_type[arg - 1] & MEM_WRITE ? BPF_WRITE : BPF_READ,
true, meta, NULL);
break;
--
2.52.0
^ permalink raw reply related [flat|nested] 54+ messages in thread
* [PATCH bpf-next v1 03/22] bpf: Only compare func_id against BPF_FUNC_* for helper calls
2026-09-05 22:00 [PATCH bpf-next v1 00/22] bpf: Unify helper and kfunc argument checks Amery Hung
2026-09-05 22:00 ` [PATCH bpf-next v1 01/22] bpf: Pass call metadata through shared " Amery Hung
2026-09-05 22:00 ` [PATCH bpf-next v1 02/22] bpf: Address check_func_arg() arguments by argno Amery Hung
@ 2026-09-05 22:00 ` Amery Hung
2026-09-05 22:00 ` [PATCH bpf-next v1 04/22] bpf: Only compare func_id against kfunc BTF IDs for kfunc calls Amery Hung
` (19 subsequent siblings)
22 siblings, 0 replies; 54+ messages in thread
From: Amery Hung @ 2026-09-05 22:00 UTC (permalink / raw)
To: bpf
Cc: alexei.starovoitov, andrii, daniel, eddyz87, memxor, ameryhung,
kernel-team
Helpers are identified by enum bpf_func_id, while kfuncs are identified
by a BTF ID. Both values are stored in bpf_call_arg_meta::func_id, and
a kfunc BTF ID can have the same numeric value as a BPF_FUNC_* constant.
Later patches extend check_reg_type() and check_func_arg() to kfuncs.
A bare func_id comparison in those common paths could then mistake a
kfunc for a helper.
Introduce is_helper_call(), which first excludes kfunc metadata through
meta->btf, and use it for every BPF_FUNC_* comparison. This keeps
helper-specific behavior out of the shared path from the start.
Signed-off-by: Amery Hung <ameryhung@gmail.com>
---
kernel/bpf/verifier.c | 32 ++++++++++++++++++++++----------
1 file changed, 22 insertions(+), 10 deletions(-)
diff --git a/kernel/bpf/verifier.c b/kernel/bpf/verifier.c
index 30e1f7a7553e..9a39e46c0745 100644
--- a/kernel/bpf/verifier.c
+++ b/kernel/bpf/verifier.c
@@ -8131,6 +8131,16 @@ static bool arg_type_is_dynptr(enum bpf_arg_type type)
return base_type(type) == ARG_PTR_TO_DYNPTR;
}
+/*
+ * A kfunc is named by a BTF ID, which can take the same numeric value as an
+ * enum bpf_func_id. Only test meta->func_id against a BPF_FUNC_* once the call
+ * is known to be to a helper; meta->btf is set only for a kfunc.
+ */
+static bool is_helper_call(const struct bpf_call_arg_meta *meta, enum bpf_func_id func_id)
+{
+ return !meta->btf && meta->func_id == func_id;
+}
+
static int resolve_map_arg_type(struct bpf_verifier_env *env,
const struct bpf_call_arg_meta *meta,
enum bpf_arg_type *arg_type)
@@ -8152,7 +8162,7 @@ static int resolve_map_arg_type(struct bpf_verifier_env *env,
}
break;
case BPF_MAP_TYPE_BLOOM_FILTER:
- if (meta->func_id == BPF_FUNC_map_peek_elem)
+ if (is_helper_call(meta, BPF_FUNC_map_peek_elem))
*arg_type = ARG_PTR_TO_MAP_VALUE;
break;
default:
@@ -8367,7 +8377,8 @@ static int check_reg_type(struct bpf_verifier_env *env, struct bpf_reg_state *re
type &= ~DYNPTR_TYPE_FLAG_MASK;
/* Local kptr types are allowed as the source argument of bpf_kptr_xchg */
- if (meta->func_id == BPF_FUNC_kptr_xchg && type_is_alloc(type) && reg_from_argno(argno) == BPF_REG_2) {
+ if (is_helper_call(meta, BPF_FUNC_kptr_xchg) && type_is_alloc(type) &&
+ reg_from_argno(argno) == BPF_REG_2) {
type &= ~MEM_ALLOC;
type &= ~MEM_PERCPU;
}
@@ -8421,7 +8432,7 @@ static int check_reg_type(struct bpf_verifier_env *env, struct bpf_reg_state *re
* allows bpf_sk_release to work for multiple socket types.
*/
bool strict_type_match = arg_type_is_release(arg_type) &&
- meta->func_id != BPF_FUNC_sk_release;
+ !is_helper_call(meta, BPF_FUNC_sk_release);
if (type_may_be_null(reg->type) &&
(!type_may_be_null(arg_type) || arg_type_is_release(arg_type))) {
@@ -8442,7 +8453,7 @@ static int check_reg_type(struct bpf_verifier_env *env, struct bpf_reg_state *re
arg_btf_id = compatible->btf_id;
}
- if (meta->func_id == BPF_FUNC_kptr_xchg) {
+ if (is_helper_call(meta, BPF_FUNC_kptr_xchg)) {
if (map_kptr_match_type(env, meta->kptr_field, reg, reg_from_argno(argno)))
return -EACCES;
} else {
@@ -8473,13 +8484,14 @@ static int check_reg_type(struct bpf_verifier_env *env, struct bpf_reg_state *re
case PTR_TO_BTF_ID | MEM_PERCPU | MEM_ALLOC:
case PTR_TO_BTF_ID | MEM_ALLOC | NON_OWN_REF:
case PTR_TO_BTF_ID | MEM_ALLOC | NON_OWN_REF | MEM_RCU:
- if (meta->func_id != BPF_FUNC_spin_lock && meta->func_id != BPF_FUNC_spin_unlock &&
- meta->func_id != BPF_FUNC_kptr_xchg) {
+ if (!is_helper_call(meta, BPF_FUNC_spin_lock) &&
+ !is_helper_call(meta, BPF_FUNC_spin_unlock) &&
+ !is_helper_call(meta, BPF_FUNC_kptr_xchg)) {
verifier_bug(env, "unimplemented handling of MEM_ALLOC");
return -EFAULT;
}
/* Check if local kptr in src arg matches kptr in dst arg */
- if (meta->func_id == BPF_FUNC_kptr_xchg) {
+ if (is_helper_call(meta, BPF_FUNC_kptr_xchg)) {
int regno = reg_from_argno(argno);
if (regno == BPF_REG_2 &&
@@ -8896,7 +8908,7 @@ static int check_func_arg(struct bpf_verifier_env *env, u32 arg,
* Disable raw mode for bpf_map_peek_elem() on a bloom filter. The helper reads
* the value buffer as an input rather than filling it.
*/
- if (meta->func_id == BPF_FUNC_map_peek_elem &&
+ if (is_helper_call(meta, BPF_FUNC_map_peek_elem) &&
meta->map.ptr->map_type == BPF_MAP_TYPE_BLOOM_FILTER)
meta->arg_raw_mem.regno = 0;
@@ -8918,11 +8930,11 @@ static int check_func_arg(struct bpf_verifier_env *env, u32 arg,
verbose(env, "can't spin_{lock,unlock} in rbtree cb\n");
return -EACCES;
}
- if (meta->func_id == BPF_FUNC_spin_lock) {
+ if (is_helper_call(meta, BPF_FUNC_spin_lock)) {
err = process_spin_lock(env, reg, argno, PROCESS_SPIN_LOCK);
if (err)
return err;
- } else if (meta->func_id == BPF_FUNC_spin_unlock) {
+ } else if (is_helper_call(meta, BPF_FUNC_spin_unlock)) {
err = process_spin_lock(env, reg, argno, 0);
if (err)
return err;
--
2.52.0
^ permalink raw reply related [flat|nested] 54+ messages in thread
* [PATCH bpf-next v1 04/22] bpf: Only compare func_id against kfunc BTF IDs for kfunc calls
2026-09-05 22:00 [PATCH bpf-next v1 00/22] bpf: Unify helper and kfunc argument checks Amery Hung
` (2 preceding siblings ...)
2026-09-05 22:00 ` [PATCH bpf-next v1 03/22] bpf: Only compare func_id against BPF_FUNC_* for helper calls Amery Hung
@ 2026-09-05 22:00 ` Amery Hung
2026-09-05 22:44 ` bot+bpf-ci
2026-09-05 22:01 ` [PATCH bpf-next v1 05/22] bpf: Rename ambiguous function argument types Amery Hung
` (18 subsequent siblings)
22 siblings, 1 reply; 54+ messages in thread
From: Amery Hung @ 2026-09-05 22:00 UTC (permalink / raw)
To: bpf
Cc: alexei.starovoitov, andrii, daniel, eddyz87, memxor, ameryhung,
kernel-team
Helpers are identified by enum bpf_func_id, while kfuncs are identified
by a BTF ID. Both values are stored in bpf_call_arg_meta::func_id, and
a helper ID can have the same numeric value as a kfunc BTF ID.
Introduce is_kfunc_call(), which first confirms that the metadata is for
a kfunc through meta->btf, and use it for comparisons against the special
kfunc BTF ID list. This complements is_helper_call() before later patches
move these checks into paths shared by helpers and kfuncs.
Signed-off-by: Amery Hung <ameryhung@gmail.com>
---
kernel/bpf/verifier.c | 68 +++++++++++++++++++++++--------------------
1 file changed, 37 insertions(+), 31 deletions(-)
diff --git a/kernel/bpf/verifier.c b/kernel/bpf/verifier.c
index 9a39e46c0745..e8ae6edf2f58 100644
--- a/kernel/bpf/verifier.c
+++ b/kernel/bpf/verifier.c
@@ -8141,6 +8141,11 @@ static bool is_helper_call(const struct bpf_call_arg_meta *meta, enum bpf_func_i
return !meta->btf && meta->func_id == func_id;
}
+static bool is_kfunc_call(const struct bpf_call_arg_meta *meta, u32 btf_id)
+{
+ return meta->btf && meta->func_id == btf_id;
+}
+
static int resolve_map_arg_type(struct bpf_verifier_env *env,
const struct bpf_call_arg_meta *meta,
enum bpf_arg_type *arg_type)
@@ -12041,27 +12046,27 @@ static bool is_kfunc_ret_null(struct bpf_call_arg_meta *meta)
static bool is_kfunc_bpf_rcu_read_lock(struct bpf_call_arg_meta *meta)
{
- return meta->func_id == special_kfunc_list[KF_bpf_rcu_read_lock];
+ return is_kfunc_call(meta, special_kfunc_list[KF_bpf_rcu_read_lock]);
}
static bool is_kfunc_bpf_rcu_read_unlock(struct bpf_call_arg_meta *meta)
{
- return meta->func_id == special_kfunc_list[KF_bpf_rcu_read_unlock];
+ return is_kfunc_call(meta, special_kfunc_list[KF_bpf_rcu_read_unlock]);
}
static bool is_kfunc_bpf_preempt_disable(struct bpf_call_arg_meta *meta)
{
- return meta->func_id == special_kfunc_list[KF_bpf_preempt_disable];
+ return is_kfunc_call(meta, special_kfunc_list[KF_bpf_preempt_disable]);
}
static bool is_kfunc_bpf_preempt_enable(struct bpf_call_arg_meta *meta)
{
- return meta->func_id == special_kfunc_list[KF_bpf_preempt_enable];
+ return is_kfunc_call(meta, special_kfunc_list[KF_bpf_preempt_enable]);
}
bool bpf_is_kfunc_pkt_changing(struct bpf_call_arg_meta *meta)
{
- return meta->func_id == special_kfunc_list[KF_bpf_xdp_pull_data];
+ return is_kfunc_call(meta, special_kfunc_list[KF_bpf_xdp_pull_data]);
}
static int
@@ -12102,9 +12107,9 @@ get_kfunc_arg_type(struct bpf_verifier_env *env, struct bpf_call_arg_meta *meta,
* 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 (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])
+ if (is_kfunc_call(meta, special_kfunc_list[KF_bpf_cast_to_kern_ctx]) ||
+ is_kfunc_call(meta, special_kfunc_list[KF_bpf_session_is_return]) ||
+ is_kfunc_call(meta, 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;
@@ -12315,15 +12320,15 @@ static int process_irq_flag(struct bpf_verifier_env *env, struct bpf_reg_state *
int err, spi, kfunc_class = IRQ_NATIVE_KFUNC;
bool irq_save;
- if (meta->func_id == special_kfunc_list[KF_bpf_local_irq_save] ||
- meta->func_id == special_kfunc_list[KF_bpf_res_spin_lock_irqsave]) {
+ if (is_kfunc_call(meta, special_kfunc_list[KF_bpf_local_irq_save]) ||
+ is_kfunc_call(meta, special_kfunc_list[KF_bpf_res_spin_lock_irqsave])) {
irq_save = true;
- if (meta->func_id == special_kfunc_list[KF_bpf_res_spin_lock_irqsave])
+ if (is_kfunc_call(meta, special_kfunc_list[KF_bpf_res_spin_lock_irqsave]))
kfunc_class = IRQ_LOCK_KFUNC;
- } else if (meta->func_id == special_kfunc_list[KF_bpf_local_irq_restore] ||
- meta->func_id == special_kfunc_list[KF_bpf_res_spin_unlock_irqrestore]) {
+ } else if (is_kfunc_call(meta, special_kfunc_list[KF_bpf_local_irq_restore]) ||
+ is_kfunc_call(meta, special_kfunc_list[KF_bpf_res_spin_unlock_irqrestore])) {
irq_save = false;
- if (meta->func_id == special_kfunc_list[KF_bpf_res_spin_unlock_irqrestore])
+ if (is_kfunc_call(meta, special_kfunc_list[KF_bpf_res_spin_unlock_irqrestore]))
kfunc_class = IRQ_LOCK_KFUNC;
} else {
verifier_bug(env, "unknown irq flags kfunc");
@@ -13011,7 +13016,7 @@ static int check_kfunc_args(struct bpf_verifier_env *env, struct bpf_call_arg_me
return -EINVAL;
}
- if (meta->func_id == special_kfunc_list[KF_bpf_cast_to_kern_ctx]) {
+ if (is_kfunc_call(meta, special_kfunc_list[KF_bpf_cast_to_kern_ctx])) {
ret = get_kern_ctx_btf_id(&env->log, resolve_prog_type(env->prog));
if (ret < 0)
return -EINVAL;
@@ -13068,17 +13073,17 @@ static int check_kfunc_args(struct bpf_verifier_env *env, struct bpf_call_arg_me
if (is_kfunc_arg_uninit(btf, &args[i]))
dynptr_arg_type |= MEM_UNINIT;
- if (meta->func_id == special_kfunc_list[KF_bpf_dynptr_from_skb]) {
+ if (is_kfunc_call(meta, special_kfunc_list[KF_bpf_dynptr_from_skb])) {
dynptr_arg_type |= DYNPTR_TYPE_SKB;
- } else if (meta->func_id == special_kfunc_list[KF_bpf_dynptr_from_xdp]) {
+ } else if (is_kfunc_call(meta, special_kfunc_list[KF_bpf_dynptr_from_xdp])) {
dynptr_arg_type |= DYNPTR_TYPE_XDP;
- } else if (meta->func_id == special_kfunc_list[KF_bpf_dynptr_from_skb_meta]) {
+ } else if (is_kfunc_call(meta, special_kfunc_list[KF_bpf_dynptr_from_skb_meta])) {
dynptr_arg_type |= DYNPTR_TYPE_SKB_META;
- } else if (meta->func_id == special_kfunc_list[KF_bpf_dynptr_from_file]) {
+ } else if (is_kfunc_call(meta, special_kfunc_list[KF_bpf_dynptr_from_file])) {
dynptr_arg_type |= DYNPTR_TYPE_FILE;
- } else if (meta->func_id == special_kfunc_list[KF_bpf_dynptr_file_discard]) {
+ } else if (is_kfunc_call(meta, special_kfunc_list[KF_bpf_dynptr_file_discard])) {
dynptr_arg_type |= DYNPTR_TYPE_FILE | OBJ_RELEASE;
- } else if (meta->func_id == special_kfunc_list[KF_bpf_dynptr_clone] &&
+ } else if (is_kfunc_call(meta, special_kfunc_list[KF_bpf_dynptr_clone]) &&
(dynptr_arg_type & MEM_UNINIT)) {
enum bpf_dynptr_type parent_type = meta->dynptr.type;
@@ -13097,7 +13102,7 @@ static int check_kfunc_args(struct bpf_verifier_env *env, struct bpf_call_arg_me
break;
}
case KF_ARG_PTR_TO_ITER:
- if (meta->func_id == special_kfunc_list[KF_bpf_iter_css_task_new]) {
+ if (is_kfunc_call(meta, special_kfunc_list[KF_bpf_iter_css_task_new])) {
if (!check_css_task_iter_allowlist(env)) {
verbose(env, "css_task_iter is only allowed in bpf_lsm, bpf_iter and sleepable progs\n");
return -EINVAL;
@@ -13473,11 +13478,12 @@ static int check_kfunc_args(struct bpf_verifier_env *env, struct bpf_call_arg_me
if (!is_bpf_res_spin_lock_kfunc(meta->func_id))
return -EFAULT;
- if (meta->func_id == special_kfunc_list[KF_bpf_res_spin_lock] ||
- meta->func_id == special_kfunc_list[KF_bpf_res_spin_lock_irqsave])
+ if (is_kfunc_call(meta, special_kfunc_list[KF_bpf_res_spin_lock]) ||
+ is_kfunc_call(meta, special_kfunc_list[KF_bpf_res_spin_lock_irqsave]))
flags |= PROCESS_SPIN_LOCK;
- if (meta->func_id == special_kfunc_list[KF_bpf_res_spin_lock_irqsave] ||
- meta->func_id == special_kfunc_list[KF_bpf_res_spin_unlock_irqrestore])
+ if (is_kfunc_call(meta, special_kfunc_list[KF_bpf_res_spin_lock_irqsave]) ||
+ is_kfunc_call(meta,
+ special_kfunc_list[KF_bpf_res_spin_unlock_irqrestore]))
flags |= PROCESS_LOCK_IRQ;
ret = process_spin_lock(env, reg, argno, flags);
if (ret < 0)
@@ -13820,12 +13826,12 @@ static int check_special_kfunc(struct bpf_verifier_env *env, struct bpf_call_arg
struct btf_field *field = meta->arg_rbtree_root.field;
mark_reg_graph_node(regs, BPF_REG_0, &field->graph_root);
- } else if (meta->func_id == special_kfunc_list[KF_bpf_cast_to_kern_ctx]) {
+ } else if (is_kfunc_call(meta, special_kfunc_list[KF_bpf_cast_to_kern_ctx])) {
mark_reg_known_zero(env, regs, BPF_REG_0);
regs[BPF_REG_0].type = PTR_TO_BTF_ID | PTR_TRUSTED;
regs[BPF_REG_0].btf = desc_btf;
regs[BPF_REG_0].btf_id = meta->ret_btf_id;
- } else if (meta->func_id == special_kfunc_list[KF_bpf_rdonly_cast]) {
+ } else if (is_kfunc_call(meta, special_kfunc_list[KF_bpf_rdonly_cast])) {
ret_t = btf_type_by_id(desc_btf, meta->arg_constant.value);
if (!ret_t) {
verbose(env, "Unknown type ID %lld passed to kfunc bpf_rdonly_cast\n",
@@ -13845,8 +13851,8 @@ static int check_special_kfunc(struct bpf_verifier_env *env, struct bpf_call_arg
"kfunc bpf_rdonly_cast type ID argument must be of a struct or void\n");
return -EINVAL;
}
- } else if (meta->func_id == special_kfunc_list[KF_bpf_dynptr_slice] ||
- meta->func_id == special_kfunc_list[KF_bpf_dynptr_slice_rdwr]) {
+ } else if (is_kfunc_call(meta, special_kfunc_list[KF_bpf_dynptr_slice]) ||
+ is_kfunc_call(meta, special_kfunc_list[KF_bpf_dynptr_slice_rdwr])) {
enum bpf_type_flag type_flag = get_dynptr_type_flag(meta->dynptr.type);
mark_reg_known_zero(env, regs, BPF_REG_0);
@@ -13861,7 +13867,7 @@ static int check_special_kfunc(struct bpf_verifier_env *env, struct bpf_call_arg
/* PTR_MAYBE_NULL will be added when is_kfunc_ret_null is checked */
regs[BPF_REG_0].type = PTR_TO_MEM | type_flag;
- if (meta->func_id == special_kfunc_list[KF_bpf_dynptr_slice]) {
+ if (is_kfunc_call(meta, special_kfunc_list[KF_bpf_dynptr_slice])) {
regs[BPF_REG_0].type |= MEM_RDONLY;
} else {
/* this will set env->seen_direct_write to true */
--
2.52.0
^ permalink raw reply related [flat|nested] 54+ messages in thread
* [PATCH bpf-next v1 05/22] bpf: Rename ambiguous function argument types
2026-09-05 22:00 [PATCH bpf-next v1 00/22] bpf: Unify helper and kfunc argument checks Amery Hung
` (3 preceding siblings ...)
2026-09-05 22:00 ` [PATCH bpf-next v1 04/22] bpf: Only compare func_id against kfunc BTF IDs for kfunc calls Amery Hung
@ 2026-09-05 22:01 ` Amery Hung
2026-09-05 23:08 ` bot+bpf-ci
2026-09-05 22:01 ` [PATCH bpf-next v1 06/22] bpf: Unify kfunc argument kinds with enum bpf_arg_type Amery Hung
` (17 subsequent siblings)
22 siblings, 1 reply; 54+ messages in thread
From: Amery Hung @ 2026-09-05 22:01 UTC (permalink / raw)
To: bpf
Cc: alexei.starovoitov, andrii, daniel, eddyz87, memxor, ameryhung,
kernel-team
ARG_ANYTHING does not accept just anything: check_func_arg() accepts
an initialized scalar and rejects pointers. Rename it to ARG_SCALAR.
ARG_DONTCARE is the zero-valued terminator for the fixed-size argument
array rather than an argument whose value is ignored. Rename it to
ARG_UNUSED and stop helper argument iteration explicitly when it
is encountered.
Also pass ARG_PTR_TO_MEM when checking a global subprogram memory
argument instead of using the prototype terminator as a placeholder.
No functional change.
Signed-off-by: Amery Hung <ameryhung@gmail.com>
---
drivers/media/rc/bpf-lirc.c | 10 +-
include/linux/bpf.h | 4 +-
kernel/bpf/backtrack.c | 2 +-
kernel/bpf/bpf_cgrp_storage.c | 2 +-
kernel/bpf/bpf_inode_storage.c | 2 +-
kernel/bpf/bpf_iter.c | 6 +-
kernel/bpf/bpf_lsm.c | 2 +-
kernel/bpf/bpf_task_storage.c | 2 +-
kernel/bpf/btf.c | 6 +-
kernel/bpf/cgroup.c | 6 +-
kernel/bpf/core.c | 2 +-
kernel/bpf/helpers.c | 46 ++---
kernel/bpf/ringbuf.c | 20 +--
kernel/bpf/stackmap.c | 14 +-
kernel/bpf/syscall.c | 6 +-
kernel/bpf/task_iter.c | 4 +-
kernel/bpf/verifier.c | 27 ++-
kernel/trace/bpf_trace.c | 46 ++---
net/core/bpf_sk_storage.c | 6 +-
net/core/filter.c | 302 ++++++++++++++++-----------------
net/core/sock_map.c | 16 +-
net/ipv4/bpf_tcp_ca.c | 2 +-
22 files changed, 266 insertions(+), 267 deletions(-)
diff --git a/drivers/media/rc/bpf-lirc.c b/drivers/media/rc/bpf-lirc.c
index 2f7564f26445..108edf440c89 100644
--- a/drivers/media/rc/bpf-lirc.c
+++ b/drivers/media/rc/bpf-lirc.c
@@ -52,9 +52,9 @@ static const struct bpf_func_proto rc_keydown_proto = {
.gpl_only = true, /* rc_keydown is EXPORT_SYMBOL_GPL */
.ret_type = RET_INTEGER,
.arg1_type = ARG_PTR_TO_CTX,
- .arg2_type = ARG_ANYTHING,
- .arg3_type = ARG_ANYTHING,
- .arg4_type = ARG_ANYTHING,
+ .arg2_type = ARG_SCALAR,
+ .arg3_type = ARG_SCALAR,
+ .arg4_type = ARG_SCALAR,
};
BPF_CALL_3(bpf_rc_pointer_rel, u32*, sample, s32, rel_x, s32, rel_y)
@@ -75,8 +75,8 @@ static const struct bpf_func_proto rc_pointer_rel_proto = {
.gpl_only = true,
.ret_type = RET_INTEGER,
.arg1_type = ARG_PTR_TO_CTX,
- .arg2_type = ARG_ANYTHING,
- .arg3_type = ARG_ANYTHING,
+ .arg2_type = ARG_SCALAR,
+ .arg3_type = ARG_SCALAR,
};
static const struct bpf_func_proto *
diff --git a/include/linux/bpf.h b/include/linux/bpf.h
index 3a7eb2185c35..1574fe2d8cc0 100644
--- a/include/linux/bpf.h
+++ b/include/linux/bpf.h
@@ -874,7 +874,7 @@ enum bpf_type_flag {
/* function argument constraints */
enum bpf_arg_type {
- ARG_DONTCARE = 0, /* unused argument in helper function */
+ ARG_UNUSED = 0, /* unused argument; terminates argument iteration */
/* the following constraints used to prototype
* bpf_map_lookup/update/delete_elem() functions
@@ -893,7 +893,7 @@ enum bpf_arg_type {
ARG_MEM_SIZE_OR_ZERO, /* number of bytes accessed from memory or 0 */
ARG_PTR_TO_CTX, /* pointer to context */
- ARG_ANYTHING, /* any (initialized) argument is ok */
+ ARG_SCALAR, /* any (initialized) scalar is ok */
ARG_PTR_TO_SPIN_LOCK, /* pointer to bpf_spin_lock */
ARG_PTR_TO_SOCK_COMMON, /* pointer to sock_common */
ARG_PTR_TO_SOCKET, /* pointer to bpf_sock (fullsock) */
diff --git a/kernel/bpf/backtrack.c b/kernel/bpf/backtrack.c
index 653db80bcc47..c5c9c5f3b078 100644
--- a/kernel/bpf/backtrack.c
+++ b/kernel/bpf/backtrack.c
@@ -634,7 +634,7 @@ static int backtrack_insn(struct bpf_verifier_env *env, int idx, int subseq_idx,
* The approach of starting with precise=true for all registers and then
* backtrack to mark a register as not precise when the verifier detects
* that program doesn't care about specific value (e.g., when helper
- * takes register as ARG_ANYTHING parameter) is not safe.
+ * takes register as ARG_SCALAR parameter) is not safe.
*
* It's ok to walk single parentage chain of the verifier states.
* It's possible that this backtracking will go all the way till 1st insn.
diff --git a/kernel/bpf/bpf_cgrp_storage.c b/kernel/bpf/bpf_cgrp_storage.c
index c76e9b0fabba..095d5c93e4ba 100644
--- a/kernel/bpf/bpf_cgrp_storage.c
+++ b/kernel/bpf/bpf_cgrp_storage.c
@@ -180,7 +180,7 @@ const struct bpf_func_proto bpf_cgrp_storage_get_proto = {
.arg2_type = ARG_PTR_TO_BTF_ID_OR_NULL,
.arg2_btf_id = &bpf_cgroup_btf_id[0],
.arg3_type = ARG_PTR_TO_MAP_VALUE_OR_NULL,
- .arg4_type = ARG_ANYTHING,
+ .arg4_type = ARG_SCALAR,
};
const struct bpf_func_proto bpf_cgrp_storage_delete_proto = {
diff --git a/kernel/bpf/bpf_inode_storage.c b/kernel/bpf/bpf_inode_storage.c
index f9e81060c1f4..69e1876650dd 100644
--- a/kernel/bpf/bpf_inode_storage.c
+++ b/kernel/bpf/bpf_inode_storage.c
@@ -220,7 +220,7 @@ const struct bpf_func_proto bpf_inode_storage_get_proto = {
.arg2_type = ARG_PTR_TO_BTF_ID_OR_NULL,
.arg2_btf_id = &bpf_inode_storage_btf_ids[0],
.arg3_type = ARG_PTR_TO_MAP_VALUE_OR_NULL,
- .arg4_type = ARG_ANYTHING,
+ .arg4_type = ARG_SCALAR,
};
const struct bpf_func_proto bpf_inode_storage_delete_proto = {
diff --git a/kernel/bpf/bpf_iter.c b/kernel/bpf/bpf_iter.c
index 14a5fdfa0421..1068bf8621f0 100644
--- a/kernel/bpf/bpf_iter.c
+++ b/kernel/bpf/bpf_iter.c
@@ -721,7 +721,7 @@ const struct bpf_func_proto bpf_for_each_map_elem_proto = {
.arg1_type = ARG_CONST_MAP_PTR,
.arg2_type = ARG_PTR_TO_FUNC,
.arg3_type = ARG_PTR_TO_STACK_OR_NULL,
- .arg4_type = ARG_ANYTHING,
+ .arg4_type = ARG_SCALAR,
};
BPF_CALL_4(bpf_loop, u32, nr_loops, void *, callback_fn, void *, callback_ctx,
@@ -754,10 +754,10 @@ const struct bpf_func_proto bpf_loop_proto = {
.func = bpf_loop,
.gpl_only = false,
.ret_type = RET_INTEGER,
- .arg1_type = ARG_ANYTHING,
+ .arg1_type = ARG_SCALAR,
.arg2_type = ARG_PTR_TO_FUNC,
.arg3_type = ARG_PTR_TO_STACK_OR_NULL,
- .arg4_type = ARG_ANYTHING,
+ .arg4_type = ARG_SCALAR,
};
struct bpf_iter_num_kern {
diff --git a/kernel/bpf/bpf_lsm.c b/kernel/bpf/bpf_lsm.c
index 82c5988417a0..9ee39c654dc0 100644
--- a/kernel/bpf/bpf_lsm.c
+++ b/kernel/bpf/bpf_lsm.c
@@ -163,7 +163,7 @@ static const struct bpf_func_proto bpf_bprm_opts_set_proto = {
.ret_type = RET_INTEGER,
.arg1_type = ARG_PTR_TO_BTF_ID,
.arg1_btf_id = &bpf_bprm_opts_set_btf_ids[0],
- .arg2_type = ARG_ANYTHING,
+ .arg2_type = ARG_SCALAR,
};
BPF_CALL_3(bpf_ima_inode_hash, struct inode *, inode, void *, dst, u32, size)
diff --git a/kernel/bpf/bpf_task_storage.c b/kernel/bpf/bpf_task_storage.c
index 4b342be29eac..e002df9d3a14 100644
--- a/kernel/bpf/bpf_task_storage.c
+++ b/kernel/bpf/bpf_task_storage.c
@@ -243,7 +243,7 @@ const struct bpf_func_proto bpf_task_storage_get_proto = {
.arg2_type = ARG_PTR_TO_BTF_ID_OR_NULL,
.arg2_btf_id = &btf_tracing_ids[BTF_TRACING_TYPE_TASK],
.arg3_type = ARG_PTR_TO_MAP_VALUE_OR_NULL,
- .arg4_type = ARG_ANYTHING,
+ .arg4_type = ARG_SCALAR,
};
const struct bpf_func_proto bpf_task_storage_delete_proto = {
diff --git a/kernel/bpf/btf.c b/kernel/bpf/btf.c
index 9c2cab08bb79..0bd26b2ec77d 100644
--- a/kernel/bpf/btf.c
+++ b/kernel/bpf/btf.c
@@ -8222,7 +8222,7 @@ int btf_prepare_func_args(struct bpf_verifier_env *env, int subprog)
return -EINVAL;
}
if (btf_type_is_int(t) || btf_is_any_enum(t)) {
- sub->args[i].arg_type = ARG_ANYTHING;
+ sub->args[i].arg_type = ARG_SCALAR;
continue;
}
if (!is_global)
@@ -8747,8 +8747,8 @@ const struct bpf_func_proto bpf_btf_find_by_name_kind_proto = {
.ret_type = RET_INTEGER,
.arg1_type = ARG_PTR_TO_MEM | MEM_RDONLY,
.arg2_type = ARG_MEM_SIZE,
- .arg3_type = ARG_ANYTHING,
- .arg4_type = ARG_ANYTHING,
+ .arg3_type = ARG_SCALAR,
+ .arg4_type = ARG_SCALAR,
};
BTF_ID_LIST_GLOBAL(btf_tracing_ids, MAX_BTF_TRACING_TYPE)
diff --git a/kernel/bpf/cgroup.c b/kernel/bpf/cgroup.c
index 149672c76c49..aa7da999cdb8 100644
--- a/kernel/bpf/cgroup.c
+++ b/kernel/bpf/cgroup.c
@@ -1840,7 +1840,7 @@ const struct bpf_func_proto bpf_get_local_storage_proto = {
.gpl_only = false,
.ret_type = RET_PTR_TO_MAP_VALUE,
.arg1_type = ARG_CONST_MAP_PTR,
- .arg2_type = ARG_ANYTHING,
+ .arg2_type = ARG_SCALAR,
};
BPF_CALL_0(bpf_get_retval)
@@ -1870,7 +1870,7 @@ const struct bpf_func_proto bpf_set_retval_proto = {
.func = bpf_set_retval,
.gpl_only = false,
.ret_type = RET_INTEGER,
- .arg1_type = ARG_ANYTHING,
+ .arg1_type = ARG_SCALAR,
};
static const struct bpf_func_proto *
@@ -2331,7 +2331,7 @@ static const struct bpf_func_proto bpf_sysctl_get_name_proto = {
.arg1_type = ARG_PTR_TO_CTX,
.arg2_type = ARG_PTR_TO_MEM | MEM_WRITE,
.arg3_type = ARG_MEM_SIZE,
- .arg4_type = ARG_ANYTHING,
+ .arg4_type = ARG_SCALAR,
};
static int copy_sysctl_value(char *dst, size_t dst_len, char *src,
diff --git a/kernel/bpf/core.c b/kernel/bpf/core.c
index 5db77d7915df..404dc6db0691 100644
--- a/kernel/bpf/core.c
+++ b/kernel/bpf/core.c
@@ -3208,7 +3208,7 @@ const struct bpf_func_proto bpf_tail_call_proto = {
.ret_type = RET_VOID,
.arg1_type = ARG_PTR_TO_CTX,
.arg2_type = ARG_CONST_MAP_PTR,
- .arg3_type = ARG_ANYTHING,
+ .arg3_type = ARG_SCALAR,
};
/* Stub for JITs that only support cBPF. eBPF programs are interpreted.
diff --git a/kernel/bpf/helpers.c b/kernel/bpf/helpers.c
index b3cc5c8fc875..72bfd8f93ae4 100644
--- a/kernel/bpf/helpers.c
+++ b/kernel/bpf/helpers.c
@@ -71,7 +71,7 @@ const struct bpf_func_proto bpf_map_update_elem_proto = {
.arg1_type = ARG_CONST_MAP_PTR,
.arg2_type = ARG_PTR_TO_MAP_KEY,
.arg3_type = ARG_PTR_TO_MAP_VALUE,
- .arg4_type = ARG_ANYTHING,
+ .arg4_type = ARG_SCALAR,
};
BPF_CALL_2(bpf_map_delete_elem, struct bpf_map *, map, void *, key)
@@ -101,7 +101,7 @@ const struct bpf_func_proto bpf_map_push_elem_proto = {
.ret_type = RET_INTEGER,
.arg1_type = ARG_CONST_MAP_PTR,
.arg2_type = ARG_PTR_TO_MAP_VALUE,
- .arg3_type = ARG_ANYTHING,
+ .arg3_type = ARG_SCALAR,
};
BPF_CALL_2(bpf_map_pop_elem, struct bpf_map *, map, void *, value)
@@ -143,7 +143,7 @@ const struct bpf_func_proto bpf_map_lookup_percpu_elem_proto = {
.ret_type = RET_PTR_TO_MAP_VALUE_OR_NULL,
.arg1_type = ARG_CONST_MAP_PTR,
.arg2_type = ARG_PTR_TO_MAP_KEY,
- .arg3_type = ARG_ANYTHING,
+ .arg3_type = ARG_SCALAR,
};
const struct bpf_func_proto bpf_get_prandom_u32_proto = {
@@ -441,7 +441,7 @@ const struct bpf_func_proto bpf_get_current_ancestor_cgroup_id_proto = {
.func = bpf_get_current_ancestor_cgroup_id,
.gpl_only = false,
.ret_type = RET_INTEGER,
- .arg1_type = ARG_ANYTHING,
+ .arg1_type = ARG_SCALAR,
};
#endif /* CONFIG_CGROUPS */
@@ -540,7 +540,7 @@ const struct bpf_func_proto bpf_strtol_proto = {
.ret_type = RET_INTEGER,
.arg1_type = ARG_PTR_TO_MEM | MEM_RDONLY,
.arg2_type = ARG_MEM_SIZE,
- .arg3_type = ARG_ANYTHING,
+ .arg3_type = ARG_SCALAR,
.arg4_type = ARG_PTR_TO_FIXED_SIZE_MEM | MEM_UNINIT | MEM_WRITE | MEM_ALIGNED,
.arg4_size = sizeof(s64),
};
@@ -568,7 +568,7 @@ const struct bpf_func_proto bpf_strtoul_proto = {
.ret_type = RET_INTEGER,
.arg1_type = ARG_PTR_TO_MEM | MEM_RDONLY,
.arg2_type = ARG_MEM_SIZE,
- .arg3_type = ARG_ANYTHING,
+ .arg3_type = ARG_SCALAR,
.arg4_type = ARG_PTR_TO_FIXED_SIZE_MEM | MEM_UNINIT | MEM_WRITE | MEM_ALIGNED,
.arg4_size = sizeof(u64),
};
@@ -624,8 +624,8 @@ const struct bpf_func_proto bpf_get_ns_current_pid_tgid_proto = {
.func = bpf_get_ns_current_pid_tgid,
.gpl_only = false,
.ret_type = RET_INTEGER,
- .arg1_type = ARG_ANYTHING,
- .arg2_type = ARG_ANYTHING,
+ .arg1_type = ARG_SCALAR,
+ .arg2_type = ARG_SCALAR,
.arg3_type = ARG_PTR_TO_UNINIT_MEM,
.arg4_type = ARG_MEM_SIZE,
};
@@ -651,7 +651,7 @@ const struct bpf_func_proto bpf_event_output_data_proto = {
.ret_type = RET_INTEGER,
.arg1_type = ARG_PTR_TO_CTX,
.arg2_type = ARG_CONST_MAP_PTR,
- .arg3_type = ARG_ANYTHING,
+ .arg3_type = ARG_SCALAR,
.arg4_type = ARG_PTR_TO_MEM | MEM_RDONLY,
.arg5_type = ARG_MEM_SIZE_OR_ZERO,
};
@@ -676,7 +676,7 @@ const struct bpf_func_proto bpf_copy_from_user_proto = {
.ret_type = RET_INTEGER,
.arg1_type = ARG_PTR_TO_UNINIT_MEM,
.arg2_type = ARG_MEM_SIZE_OR_ZERO,
- .arg3_type = ARG_ANYTHING,
+ .arg3_type = ARG_SCALAR,
};
BPF_CALL_5(bpf_copy_from_user_task, void *, dst, u32, size,
@@ -707,10 +707,10 @@ const struct bpf_func_proto bpf_copy_from_user_task_proto = {
.ret_type = RET_INTEGER,
.arg1_type = ARG_PTR_TO_UNINIT_MEM,
.arg2_type = ARG_MEM_SIZE_OR_ZERO,
- .arg3_type = ARG_ANYTHING,
+ .arg3_type = ARG_SCALAR,
.arg4_type = ARG_PTR_TO_BTF_ID,
.arg4_btf_id = &btf_tracing_ids[BTF_TRACING_TYPE_TASK],
- .arg5_type = ARG_ANYTHING
+ .arg5_type = ARG_SCALAR
};
BPF_CALL_2(bpf_per_cpu_ptr, const void *, ptr, u32, cpu)
@@ -726,7 +726,7 @@ const struct bpf_func_proto bpf_per_cpu_ptr_proto = {
.gpl_only = false,
.ret_type = RET_PTR_TO_MEM_OR_BTF_ID | PTR_MAYBE_NULL | MEM_RDONLY,
.arg1_type = ARG_PTR_TO_PERCPU_BTF_ID,
- .arg2_type = ARG_ANYTHING,
+ .arg2_type = ARG_SCALAR,
};
BPF_CALL_1(bpf_this_cpu_ptr, const void *, percpu_ptr)
@@ -1415,7 +1415,7 @@ static const struct bpf_func_proto bpf_timer_init_proto = {
.ret_type = RET_INTEGER,
.arg1_type = ARG_PTR_TO_TIMER,
.arg2_type = ARG_CONST_MAP_PTR,
- .arg3_type = ARG_ANYTHING,
+ .arg3_type = ARG_SCALAR,
};
static int bpf_async_update_prog_callback(struct bpf_async_cb *cb,
@@ -1558,8 +1558,8 @@ static const struct bpf_func_proto bpf_timer_start_proto = {
.gpl_only = true,
.ret_type = RET_INTEGER,
.arg1_type = ARG_PTR_TO_TIMER,
- .arg2_type = ARG_ANYTHING,
- .arg3_type = ARG_ANYTHING,
+ .arg2_type = ARG_SCALAR,
+ .arg3_type = ARG_SCALAR,
};
BPF_CALL_1(bpf_timer_cancel, struct bpf_async_kern *, async)
@@ -1889,7 +1889,7 @@ static const struct bpf_func_proto bpf_dynptr_from_mem_proto = {
.ret_type = RET_INTEGER,
.arg1_type = ARG_PTR_TO_UNINIT_MEM,
.arg2_type = ARG_MEM_SIZE_OR_ZERO,
- .arg3_type = ARG_ANYTHING,
+ .arg3_type = ARG_SCALAR,
.arg4_type = ARG_PTR_TO_DYNPTR | DYNPTR_TYPE_LOCAL | MEM_UNINIT | MEM_WRITE,
};
@@ -1945,8 +1945,8 @@ static const struct bpf_func_proto bpf_dynptr_read_proto = {
.arg1_type = ARG_PTR_TO_UNINIT_MEM,
.arg2_type = ARG_MEM_SIZE_OR_ZERO,
.arg3_type = ARG_PTR_TO_DYNPTR,
- .arg4_type = ARG_ANYTHING,
- .arg5_type = ARG_ANYTHING,
+ .arg4_type = ARG_SCALAR,
+ .arg5_type = ARG_SCALAR,
};
int __bpf_dynptr_write(const struct bpf_dynptr_kern *dst, u64 offset, void *src,
@@ -2002,10 +2002,10 @@ static const struct bpf_func_proto bpf_dynptr_write_proto = {
.gpl_only = false,
.ret_type = RET_INTEGER,
.arg1_type = ARG_PTR_TO_DYNPTR,
- .arg2_type = ARG_ANYTHING,
+ .arg2_type = ARG_SCALAR,
.arg3_type = ARG_PTR_TO_MEM | MEM_RDONLY,
.arg4_type = ARG_MEM_SIZE_OR_ZERO,
- .arg5_type = ARG_ANYTHING,
+ .arg5_type = ARG_SCALAR,
};
BPF_CALL_3(bpf_dynptr_data, const struct bpf_dynptr_kern *, ptr, u64, offset, u64, len)
@@ -2045,7 +2045,7 @@ static const struct bpf_func_proto bpf_dynptr_data_proto = {
.gpl_only = false,
.ret_type = RET_PTR_TO_DYNPTR_MEM_OR_NULL,
.arg1_type = ARG_PTR_TO_DYNPTR,
- .arg2_type = ARG_ANYTHING,
+ .arg2_type = ARG_SCALAR,
.arg3_type = ARG_CONST_ALLOC_SIZE_OR_ZERO,
};
@@ -2957,7 +2957,7 @@ const struct bpf_func_proto bpf_current_task_under_cgroup_proto = {
.gpl_only = false,
.ret_type = RET_INTEGER,
.arg1_type = ARG_CONST_MAP_PTR,
- .arg2_type = ARG_ANYTHING,
+ .arg2_type = ARG_SCALAR,
};
/**
diff --git a/kernel/bpf/ringbuf.c b/kernel/bpf/ringbuf.c
index 3f1013d80544..63c0aba56a3f 100644
--- a/kernel/bpf/ringbuf.c
+++ b/kernel/bpf/ringbuf.c
@@ -553,7 +553,7 @@ const struct bpf_func_proto bpf_ringbuf_reserve_proto = {
.ret_type = RET_PTR_TO_RINGBUF_MEM_OR_NULL,
.arg1_type = ARG_CONST_MAP_PTR,
.arg2_type = ARG_CONST_ALLOC_SIZE_OR_ZERO,
- .arg3_type = ARG_ANYTHING,
+ .arg3_type = ARG_SCALAR,
};
static void bpf_ringbuf_commit(void *sample, u64 flags, bool discard)
@@ -594,7 +594,7 @@ const struct bpf_func_proto bpf_ringbuf_submit_proto = {
.func = bpf_ringbuf_submit,
.ret_type = RET_VOID,
.arg1_type = ARG_PTR_TO_RINGBUF_MEM | OBJ_RELEASE,
- .arg2_type = ARG_ANYTHING,
+ .arg2_type = ARG_SCALAR,
};
BPF_CALL_2(bpf_ringbuf_discard, void *, sample, u64, flags)
@@ -607,7 +607,7 @@ const struct bpf_func_proto bpf_ringbuf_discard_proto = {
.func = bpf_ringbuf_discard,
.ret_type = RET_VOID,
.arg1_type = ARG_PTR_TO_RINGBUF_MEM | OBJ_RELEASE,
- .arg2_type = ARG_ANYTHING,
+ .arg2_type = ARG_SCALAR,
};
BPF_CALL_4(bpf_ringbuf_output, struct bpf_map *, map, void *, data, u64, size,
@@ -635,7 +635,7 @@ const struct bpf_func_proto bpf_ringbuf_output_proto = {
.arg1_type = ARG_CONST_MAP_PTR,
.arg2_type = ARG_PTR_TO_MEM | MEM_RDONLY,
.arg3_type = ARG_MEM_SIZE_OR_ZERO,
- .arg4_type = ARG_ANYTHING,
+ .arg4_type = ARG_SCALAR,
};
BPF_CALL_2(bpf_ringbuf_query, struct bpf_map *, map, u64, flags)
@@ -664,7 +664,7 @@ const struct bpf_func_proto bpf_ringbuf_query_proto = {
.func = bpf_ringbuf_query,
.ret_type = RET_INTEGER,
.arg1_type = ARG_CONST_MAP_PTR,
- .arg2_type = ARG_ANYTHING,
+ .arg2_type = ARG_SCALAR,
};
BPF_CALL_4(bpf_ringbuf_reserve_dynptr, struct bpf_map *, map, u32, size, u64, flags,
@@ -702,8 +702,8 @@ const struct bpf_func_proto bpf_ringbuf_reserve_dynptr_proto = {
.func = bpf_ringbuf_reserve_dynptr,
.ret_type = RET_INTEGER,
.arg1_type = ARG_CONST_MAP_PTR,
- .arg2_type = ARG_ANYTHING,
- .arg3_type = ARG_ANYTHING,
+ .arg2_type = ARG_SCALAR,
+ .arg3_type = ARG_SCALAR,
.arg4_type = ARG_PTR_TO_DYNPTR | DYNPTR_TYPE_RINGBUF | MEM_UNINIT | MEM_WRITE,
};
@@ -723,7 +723,7 @@ const struct bpf_func_proto bpf_ringbuf_submit_dynptr_proto = {
.func = bpf_ringbuf_submit_dynptr,
.ret_type = RET_VOID,
.arg1_type = ARG_PTR_TO_DYNPTR | DYNPTR_TYPE_RINGBUF | OBJ_RELEASE,
- .arg2_type = ARG_ANYTHING,
+ .arg2_type = ARG_SCALAR,
};
BPF_CALL_2(bpf_ringbuf_discard_dynptr, struct bpf_dynptr_kern *, ptr, u64, flags)
@@ -742,7 +742,7 @@ const struct bpf_func_proto bpf_ringbuf_discard_dynptr_proto = {
.func = bpf_ringbuf_discard_dynptr,
.ret_type = RET_VOID,
.arg1_type = ARG_PTR_TO_DYNPTR | DYNPTR_TYPE_RINGBUF | OBJ_RELEASE,
- .arg2_type = ARG_ANYTHING,
+ .arg2_type = ARG_SCALAR,
};
static int __bpf_user_ringbuf_peek(struct bpf_ringbuf *rb, void **sample, u32 *size)
@@ -876,5 +876,5 @@ const struct bpf_func_proto bpf_user_ringbuf_drain_proto = {
.arg1_type = ARG_CONST_MAP_PTR,
.arg2_type = ARG_PTR_TO_FUNC,
.arg3_type = ARG_PTR_TO_STACK_OR_NULL,
- .arg4_type = ARG_ANYTHING,
+ .arg4_type = ARG_SCALAR,
};
diff --git a/kernel/bpf/stackmap.c b/kernel/bpf/stackmap.c
index a839041e0d00..272678b82500 100644
--- a/kernel/bpf/stackmap.c
+++ b/kernel/bpf/stackmap.c
@@ -665,7 +665,7 @@ const struct bpf_func_proto bpf_get_stackid_proto = {
.ret_type = RET_INTEGER,
.arg1_type = ARG_PTR_TO_CTX,
.arg2_type = ARG_CONST_MAP_PTR,
- .arg3_type = ARG_ANYTHING,
+ .arg3_type = ARG_SCALAR,
};
static __u64 count_kernel_ip(const struct perf_callchain_entry *trace)
@@ -739,7 +739,7 @@ const struct bpf_func_proto bpf_get_stackid_proto_pe = {
.ret_type = RET_INTEGER,
.arg1_type = ARG_PTR_TO_CTX,
.arg2_type = ARG_CONST_MAP_PTR,
- .arg3_type = ARG_ANYTHING,
+ .arg3_type = ARG_SCALAR,
};
static u32 callchain_store(const struct perf_callchain_entry *trace, u32 trace_nr,
@@ -863,7 +863,7 @@ const struct bpf_func_proto bpf_get_stack_proto = {
.arg1_type = ARG_PTR_TO_CTX,
.arg2_type = ARG_PTR_TO_UNINIT_MEM,
.arg3_type = ARG_MEM_SIZE_OR_ZERO,
- .arg4_type = ARG_ANYTHING,
+ .arg4_type = ARG_SCALAR,
};
BPF_CALL_4(bpf_get_stack_sleepable, struct pt_regs *, regs, void *, buf, u32, size,
@@ -879,7 +879,7 @@ const struct bpf_func_proto bpf_get_stack_sleepable_proto = {
.arg1_type = ARG_PTR_TO_CTX,
.arg2_type = ARG_PTR_TO_UNINIT_MEM,
.arg3_type = ARG_MEM_SIZE_OR_ZERO,
- .arg4_type = ARG_ANYTHING,
+ .arg4_type = ARG_SCALAR,
};
static long __bpf_get_task_stack(struct task_struct *task, void *buf, u32 size,
@@ -916,7 +916,7 @@ const struct bpf_func_proto bpf_get_task_stack_proto = {
.arg1_btf_id = &btf_tracing_ids[BTF_TRACING_TYPE_TASK],
.arg2_type = ARG_PTR_TO_UNINIT_MEM,
.arg3_type = ARG_MEM_SIZE_OR_ZERO,
- .arg4_type = ARG_ANYTHING,
+ .arg4_type = ARG_SCALAR,
};
BPF_CALL_4(bpf_get_task_stack_sleepable, struct task_struct *, task, void *, buf,
@@ -933,7 +933,7 @@ const struct bpf_func_proto bpf_get_task_stack_sleepable_proto = {
.arg1_btf_id = &btf_tracing_ids[BTF_TRACING_TYPE_TASK],
.arg2_type = ARG_PTR_TO_UNINIT_MEM,
.arg3_type = ARG_MEM_SIZE_OR_ZERO,
- .arg4_type = ARG_ANYTHING,
+ .arg4_type = ARG_SCALAR,
};
static int __bpf_get_stack_pe(const struct perf_callchain_entry *trace, u32 trace_nr,
@@ -1015,7 +1015,7 @@ const struct bpf_func_proto bpf_get_stack_proto_pe = {
.arg1_type = ARG_PTR_TO_CTX,
.arg2_type = ARG_PTR_TO_UNINIT_MEM,
.arg3_type = ARG_MEM_SIZE_OR_ZERO,
- .arg4_type = ARG_ANYTHING,
+ .arg4_type = ARG_SCALAR,
};
/* Called from eBPF program */
diff --git a/kernel/bpf/syscall.c b/kernel/bpf/syscall.c
index 6874ba1424af..6950517f6364 100644
--- a/kernel/bpf/syscall.c
+++ b/kernel/bpf/syscall.c
@@ -6569,7 +6569,7 @@ static const struct bpf_func_proto bpf_sys_bpf_proto = {
.func = bpf_sys_bpf,
.gpl_only = false,
.ret_type = RET_INTEGER,
- .arg1_type = ARG_ANYTHING,
+ .arg1_type = ARG_SCALAR,
.arg2_type = ARG_PTR_TO_MEM | MEM_RDONLY,
.arg3_type = ARG_MEM_SIZE,
};
@@ -6594,7 +6594,7 @@ static const struct bpf_func_proto bpf_sys_close_proto = {
.func = bpf_sys_close,
.gpl_only = false,
.ret_type = RET_INTEGER,
- .arg1_type = ARG_ANYTHING,
+ .arg1_type = ARG_SCALAR,
};
BPF_CALL_4(bpf_kallsyms_lookup_name, const char *, name, int, name_sz, int, flags, u64 *, res)
@@ -6619,7 +6619,7 @@ static const struct bpf_func_proto bpf_kallsyms_lookup_name_proto = {
.ret_type = RET_INTEGER,
.arg1_type = ARG_PTR_TO_MEM | MEM_RDONLY,
.arg2_type = ARG_MEM_SIZE_OR_ZERO,
- .arg3_type = ARG_ANYTHING,
+ .arg3_type = ARG_SCALAR,
.arg4_type = ARG_PTR_TO_FIXED_SIZE_MEM | MEM_UNINIT | MEM_WRITE | MEM_ALIGNED,
.arg4_size = sizeof(u64),
};
diff --git a/kernel/bpf/task_iter.c b/kernel/bpf/task_iter.c
index 13e1aabe6f88..91b166acb3b5 100644
--- a/kernel/bpf/task_iter.c
+++ b/kernel/bpf/task_iter.c
@@ -824,10 +824,10 @@ const struct bpf_func_proto bpf_find_vma_proto = {
.ret_type = RET_INTEGER,
.arg1_type = ARG_PTR_TO_BTF_ID,
.arg1_btf_id = &btf_tracing_ids[BTF_TRACING_TYPE_TASK],
- .arg2_type = ARG_ANYTHING,
+ .arg2_type = ARG_SCALAR,
.arg3_type = ARG_PTR_TO_FUNC,
.arg4_type = ARG_PTR_TO_STACK_OR_NULL,
- .arg5_type = ARG_ANYTHING,
+ .arg5_type = ARG_SCALAR,
};
static inline void bpf_iter_mmput_async(struct mm_struct *mm)
diff --git a/kernel/bpf/verifier.c b/kernel/bpf/verifier.c
index e8ae6edf2f58..d444e72fdd97 100644
--- a/kernel/bpf/verifier.c
+++ b/kernel/bpf/verifier.c
@@ -8797,14 +8797,11 @@ static int check_func_arg(struct bpf_verifier_env *env, u32 arg,
u32 key_size;
int err = 0;
- if (arg_type == ARG_DONTCARE)
- return 0;
-
err = check_reg_arg(env, regno, SRC_OP);
if (err)
return err;
- if (arg_type == ARG_ANYTHING) {
+ if (arg_type == ARG_SCALAR) {
if (__is_pointer_value(env->allow_ptr_leaks, reg)) {
verbose(env, "%s leaks addr into helper function\n",
reg_arg_name(env, argno));
@@ -9305,7 +9302,7 @@ static bool check_raw_mode_ok(const struct bpf_func_proto *fn, struct bpf_call_a
int i;
for (i = 0; i < ARRAY_SIZE(fn->arg_type); i++) {
- if (fn->arg_type[i] == ARG_DONTCARE)
+ if (fn->arg_type[i] == ARG_UNUSED)
break;
if (!arg_type_is_raw_mem(fn->arg_type[i]))
continue;
@@ -9355,7 +9352,7 @@ static bool check_btf_id_ok(const struct bpf_func_proto *fn)
int i;
for (i = 0; i < ARRAY_SIZE(fn->arg_type); i++) {
- if (fn->arg_type[i] == ARG_DONTCARE)
+ if (fn->arg_type[i] == ARG_UNUSED)
break;
if (base_type(fn->arg_type[i]) == ARG_PTR_TO_BTF_ID)
return !!fn->arg_btf_id[i];
@@ -9378,7 +9375,7 @@ static bool check_mem_arg_rw_flag_ok(const struct bpf_func_proto *fn)
for (i = 0; i < ARRAY_SIZE(fn->arg_type); i++) {
enum bpf_arg_type arg_type = fn->arg_type[i];
- if (arg_type == ARG_DONTCARE)
+ if (arg_type == ARG_UNUSED)
break;
if (base_type(arg_type) != ARG_PTR_TO_MEM)
continue;
@@ -9396,7 +9393,7 @@ static bool check_proto_release_reg(const struct bpf_func_proto *fn, struct bpf_
for (i = 0; i < ARRAY_SIZE(fn->arg_type); i++) {
enum bpf_arg_type arg_type = fn->arg_type[i];
- if (arg_type == ARG_DONTCARE)
+ if (arg_type == ARG_UNUSED)
break;
if (arg_type_is_release(arg_type)) {
if (meta->release_regno)
@@ -9767,7 +9764,7 @@ static int btf_check_func_arg_match(struct bpf_verifier_env *env, int subprog,
struct bpf_reg_state *reg = get_func_arg_reg(caller, regs, i);
struct bpf_subprog_arg_info *arg = &sub->args[i];
- if (arg->arg_type == ARG_ANYTHING) {
+ if (arg->arg_type == ARG_SCALAR) {
if (reg->type != SCALAR_VALUE) {
bpf_log(log, "%s is not a scalar\n", reg_arg_name(env, argno));
return -EINVAL;
@@ -9791,7 +9788,7 @@ static int btf_check_func_arg_match(struct bpf_verifier_env *env, int subprog,
return -EINVAL;
}
} else if (base_type(arg->arg_type) == ARG_PTR_TO_MEM) {
- ret = check_func_arg_reg_off(env, reg, argno, ARG_DONTCARE);
+ ret = check_func_arg_reg_off(env, reg, argno, ARG_PTR_TO_MEM);
if (ret < 0)
return ret;
if (check_mem_reg(env, reg, argno, arg->mem_size, BPF_READ | BPF_WRITE, NULL,
@@ -10949,6 +10946,8 @@ static int check_helper_call(struct bpf_verifier_env *env, struct bpf_insn *insn
meta.fn = fn;
/* check args */
for (i = 0; i < MAX_BPF_FUNC_REG_ARGS; i++) {
+ if (fn->arg_type[i] == ARG_UNUSED)
+ break;
err = check_func_arg(env, i, &meta, insn_idx);
if (err)
return err;
@@ -12828,7 +12827,7 @@ static int check_kfunc_args(struct bpf_verifier_env *env, struct bpf_call_arg_me
for (i = 0; i < nargs; i++) {
struct bpf_reg_state *reg = get_func_arg_reg(caller, regs, i);
const struct btf_type *t, *ref_t, *resolve_ret;
- enum bpf_arg_type arg_type = ARG_DONTCARE;
+ enum bpf_arg_type arg_type = ARG_UNUSED;
argno_t argno = argno_from_arg(i + 1);
int regno = reg_from_argno(argno);
bool btf_id_fixed_off_ok = true;
@@ -17841,7 +17840,7 @@ bool bpf_get_call_summary(struct bpf_verifier_env *env, struct bpf_insn *call,
cs->is_void = fn->ret_type == RET_VOID;
cs->num_params = 0;
for (i = 0; i < ARRAY_SIZE(fn->arg_type); ++i) {
- if (fn->arg_type[i] == ARG_DONTCARE)
+ if (fn->arg_type[i] == ARG_UNUSED)
break;
cs->num_params++;
}
@@ -19637,7 +19636,7 @@ static int do_check_common(struct bpf_verifier_env *env, int subprog)
}
/* Also ensure the callback only has a single scalar argument. */
- if (sub->arg_cnt != 1 || sub->args[0].arg_type != ARG_ANYTHING) {
+ if (sub->arg_cnt != 1 || sub->args[0].arg_type != ARG_SCALAR) {
verbose(env, "exception cb only supports single integer argument\n");
ret = -EINVAL;
goto out;
@@ -19650,7 +19649,7 @@ static int do_check_common(struct bpf_verifier_env *env, int subprog)
if (arg->arg_type == ARG_PTR_TO_CTX) {
reg->type = PTR_TO_CTX;
mark_reg_known_zero(env, regs, i);
- } else if (arg->arg_type == ARG_ANYTHING) {
+ } else if (arg->arg_type == ARG_SCALAR) {
reg->type = SCALAR_VALUE;
mark_reg_unknown(env, regs, i);
} else if (arg->arg_type == ARG_PTR_TO_DYNPTR) {
diff --git a/kernel/trace/bpf_trace.c b/kernel/trace/bpf_trace.c
index 29260951aa87..e414858581d0 100644
--- a/kernel/trace/bpf_trace.c
+++ b/kernel/trace/bpf_trace.c
@@ -195,7 +195,7 @@ static const struct bpf_func_proto bpf_override_return_proto = {
.gpl_only = true,
.ret_type = RET_INTEGER,
.arg1_type = ARG_PTR_TO_CTX,
- .arg2_type = ARG_ANYTHING,
+ .arg2_type = ARG_SCALAR,
};
#endif
@@ -222,7 +222,7 @@ const struct bpf_func_proto bpf_probe_read_user_proto = {
.ret_type = RET_INTEGER,
.arg1_type = ARG_PTR_TO_UNINIT_MEM,
.arg2_type = ARG_MEM_SIZE_OR_ZERO,
- .arg3_type = ARG_ANYTHING,
+ .arg3_type = ARG_SCALAR,
};
static __always_inline int
@@ -259,7 +259,7 @@ const struct bpf_func_proto bpf_probe_read_user_str_proto = {
.ret_type = RET_INTEGER,
.arg1_type = ARG_PTR_TO_UNINIT_MEM,
.arg2_type = ARG_MEM_SIZE_OR_ZERO,
- .arg3_type = ARG_ANYTHING,
+ .arg3_type = ARG_SCALAR,
};
BPF_CALL_3(bpf_probe_read_kernel, void *, dst, u32, size,
@@ -274,7 +274,7 @@ const struct bpf_func_proto bpf_probe_read_kernel_proto = {
.ret_type = RET_INTEGER,
.arg1_type = ARG_PTR_TO_UNINIT_MEM,
.arg2_type = ARG_MEM_SIZE_OR_ZERO,
- .arg3_type = ARG_ANYTHING,
+ .arg3_type = ARG_SCALAR,
};
static __always_inline int
@@ -309,7 +309,7 @@ const struct bpf_func_proto bpf_probe_read_kernel_str_proto = {
.ret_type = RET_INTEGER,
.arg1_type = ARG_PTR_TO_UNINIT_MEM,
.arg2_type = ARG_MEM_SIZE_OR_ZERO,
- .arg3_type = ARG_ANYTHING,
+ .arg3_type = ARG_SCALAR,
};
#ifdef CONFIG_ARCH_HAS_NON_OVERLAPPING_ADDRESS_SPACE
@@ -329,7 +329,7 @@ static const struct bpf_func_proto bpf_probe_read_compat_proto = {
.ret_type = RET_INTEGER,
.arg1_type = ARG_PTR_TO_UNINIT_MEM,
.arg2_type = ARG_MEM_SIZE_OR_ZERO,
- .arg3_type = ARG_ANYTHING,
+ .arg3_type = ARG_SCALAR,
};
BPF_CALL_3(bpf_probe_read_compat_str, void *, dst, u32, size,
@@ -348,7 +348,7 @@ static const struct bpf_func_proto bpf_probe_read_compat_str_proto = {
.ret_type = RET_INTEGER,
.arg1_type = ARG_PTR_TO_UNINIT_MEM,
.arg2_type = ARG_MEM_SIZE_OR_ZERO,
- .arg3_type = ARG_ANYTHING,
+ .arg3_type = ARG_SCALAR,
};
#endif /* CONFIG_ARCH_HAS_NON_OVERLAPPING_ADDRESS_SPACE */
@@ -381,7 +381,7 @@ static const struct bpf_func_proto bpf_probe_write_user_proto = {
.func = bpf_probe_write_user,
.gpl_only = true,
.ret_type = RET_INTEGER,
- .arg1_type = ARG_ANYTHING,
+ .arg1_type = ARG_SCALAR,
.arg2_type = ARG_PTR_TO_MEM | MEM_RDONLY,
.arg3_type = ARG_MEM_SIZE,
};
@@ -560,7 +560,7 @@ static const struct bpf_func_proto bpf_seq_printf_btf_proto = {
.arg1_btf_id = &btf_seq_file_ids[0],
.arg2_type = ARG_PTR_TO_MEM | MEM_RDONLY,
.arg3_type = ARG_MEM_SIZE_OR_ZERO,
- .arg4_type = ARG_ANYTHING,
+ .arg4_type = ARG_SCALAR,
};
static __always_inline int
@@ -606,7 +606,7 @@ const struct bpf_func_proto bpf_perf_event_read_proto = {
.gpl_only = true,
.ret_type = RET_INTEGER,
.arg1_type = ARG_CONST_MAP_PTR,
- .arg2_type = ARG_ANYTHING,
+ .arg2_type = ARG_SCALAR,
};
BPF_CALL_4(bpf_perf_event_read_value, struct bpf_map *, map, u64, flags,
@@ -631,7 +631,7 @@ static const struct bpf_func_proto bpf_perf_event_read_value_proto = {
.gpl_only = true,
.ret_type = RET_INTEGER,
.arg1_type = ARG_CONST_MAP_PTR,
- .arg2_type = ARG_ANYTHING,
+ .arg2_type = ARG_SCALAR,
.arg3_type = ARG_PTR_TO_UNINIT_MEM,
.arg4_type = ARG_MEM_SIZE,
};
@@ -728,7 +728,7 @@ static const struct bpf_func_proto bpf_perf_event_output_proto = {
.ret_type = RET_INTEGER,
.arg1_type = ARG_PTR_TO_CTX,
.arg2_type = ARG_CONST_MAP_PTR,
- .arg3_type = ARG_ANYTHING,
+ .arg3_type = ARG_SCALAR,
.arg4_type = ARG_PTR_TO_MEM | MEM_RDONLY,
.arg5_type = ARG_MEM_SIZE_OR_ZERO,
};
@@ -914,7 +914,7 @@ const struct bpf_func_proto bpf_send_signal_proto = {
.func = bpf_send_signal,
.gpl_only = false,
.ret_type = RET_INTEGER,
- .arg1_type = ARG_ANYTHING,
+ .arg1_type = ARG_SCALAR,
};
BPF_CALL_1(bpf_send_signal_thread, u32, sig)
@@ -926,7 +926,7 @@ const struct bpf_func_proto bpf_send_signal_thread_proto = {
.func = bpf_send_signal_thread,
.gpl_only = false,
.ret_type = RET_INTEGER,
- .arg1_type = ARG_ANYTHING,
+ .arg1_type = ARG_SCALAR,
};
BPF_CALL_3(bpf_d_path, const struct path *, path, char *, buf, u32, sz)
@@ -1056,7 +1056,7 @@ const struct bpf_func_proto bpf_snprintf_btf_proto = {
.arg2_type = ARG_MEM_SIZE,
.arg3_type = ARG_PTR_TO_MEM | MEM_RDONLY,
.arg4_type = ARG_MEM_SIZE,
- .arg5_type = ARG_ANYTHING,
+ .arg5_type = ARG_SCALAR,
};
BPF_CALL_1(bpf_get_func_ip_tracing, void *, ctx)
@@ -1236,7 +1236,7 @@ static const struct bpf_func_proto bpf_get_func_arg_proto = {
.func = get_func_arg,
.ret_type = RET_INTEGER,
.arg1_type = ARG_PTR_TO_CTX,
- .arg2_type = ARG_ANYTHING,
+ .arg2_type = ARG_SCALAR,
.arg3_type = ARG_PTR_TO_FIXED_SIZE_MEM | MEM_UNINIT | MEM_WRITE | MEM_ALIGNED,
.arg3_size = sizeof(u64),
};
@@ -1419,7 +1419,7 @@ static const struct bpf_func_proto bpf_perf_event_output_proto_tp = {
.ret_type = RET_INTEGER,
.arg1_type = ARG_PTR_TO_CTX,
.arg2_type = ARG_CONST_MAP_PTR,
- .arg3_type = ARG_ANYTHING,
+ .arg3_type = ARG_SCALAR,
.arg4_type = ARG_PTR_TO_MEM | MEM_RDONLY,
.arg5_type = ARG_MEM_SIZE_OR_ZERO,
};
@@ -1444,7 +1444,7 @@ static const struct bpf_func_proto bpf_get_stackid_proto_tp = {
.ret_type = RET_INTEGER,
.arg1_type = ARG_PTR_TO_CTX,
.arg2_type = ARG_CONST_MAP_PTR,
- .arg3_type = ARG_ANYTHING,
+ .arg3_type = ARG_SCALAR,
};
BPF_CALL_4(bpf_get_stack_tp, void *, tp_buff, void *, buf, u32, size,
@@ -1463,7 +1463,7 @@ static const struct bpf_func_proto bpf_get_stack_proto_tp = {
.arg1_type = ARG_PTR_TO_CTX,
.arg2_type = ARG_PTR_TO_UNINIT_MEM,
.arg3_type = ARG_MEM_SIZE_OR_ZERO,
- .arg4_type = ARG_ANYTHING,
+ .arg4_type = ARG_SCALAR,
};
static const struct bpf_func_proto *
@@ -1567,7 +1567,7 @@ static const struct bpf_func_proto bpf_read_branch_records_proto = {
.arg1_type = ARG_PTR_TO_CTX,
.arg2_type = ARG_PTR_TO_MEM_OR_NULL | MEM_WRITE,
.arg3_type = ARG_MEM_SIZE_OR_ZERO,
- .arg4_type = ARG_ANYTHING,
+ .arg4_type = ARG_SCALAR,
};
static const struct bpf_func_proto *
@@ -1644,7 +1644,7 @@ static const struct bpf_func_proto bpf_perf_event_output_proto_raw_tp = {
.ret_type = RET_INTEGER,
.arg1_type = ARG_PTR_TO_CTX,
.arg2_type = ARG_CONST_MAP_PTR,
- .arg3_type = ARG_ANYTHING,
+ .arg3_type = ARG_SCALAR,
.arg4_type = ARG_PTR_TO_MEM | MEM_RDONLY,
.arg5_type = ARG_MEM_SIZE_OR_ZERO,
};
@@ -1676,7 +1676,7 @@ static const struct bpf_func_proto bpf_get_stackid_proto_raw_tp = {
.ret_type = RET_INTEGER,
.arg1_type = ARG_PTR_TO_CTX,
.arg2_type = ARG_CONST_MAP_PTR,
- .arg3_type = ARG_ANYTHING,
+ .arg3_type = ARG_SCALAR,
};
BPF_CALL_4(bpf_get_stack_raw_tp, struct bpf_raw_tracepoint_args *, args,
@@ -1702,7 +1702,7 @@ static const struct bpf_func_proto bpf_get_stack_proto_raw_tp = {
.arg1_type = ARG_PTR_TO_CTX,
.arg2_type = ARG_PTR_TO_UNINIT_MEM,
.arg3_type = ARG_MEM_SIZE_OR_ZERO,
- .arg4_type = ARG_ANYTHING,
+ .arg4_type = ARG_SCALAR,
};
static const struct bpf_func_proto *
diff --git a/net/core/bpf_sk_storage.c b/net/core/bpf_sk_storage.c
index 1d295a8769fa..ad0f8f222222 100644
--- a/net/core/bpf_sk_storage.c
+++ b/net/core/bpf_sk_storage.c
@@ -333,7 +333,7 @@ const struct bpf_func_proto bpf_sk_storage_get_proto = {
.arg1_type = ARG_CONST_MAP_PTR,
.arg2_type = ARG_PTR_TO_BTF_ID_SOCK_COMMON,
.arg3_type = ARG_PTR_TO_MAP_VALUE_OR_NULL,
- .arg4_type = ARG_ANYTHING,
+ .arg4_type = ARG_SCALAR,
};
const struct bpf_func_proto bpf_sk_storage_get_cg_sock_proto = {
@@ -343,7 +343,7 @@ const struct bpf_func_proto bpf_sk_storage_get_cg_sock_proto = {
.arg1_type = ARG_CONST_MAP_PTR,
.arg2_type = ARG_PTR_TO_CTX, /* context is 'struct sock' */
.arg3_type = ARG_PTR_TO_MAP_VALUE_OR_NULL,
- .arg4_type = ARG_ANYTHING,
+ .arg4_type = ARG_SCALAR,
};
const struct bpf_func_proto bpf_sk_storage_delete_proto = {
@@ -408,7 +408,7 @@ const struct bpf_func_proto bpf_sk_storage_get_tracing_proto = {
.arg2_type = ARG_PTR_TO_BTF_ID_OR_NULL,
.arg2_btf_id = &btf_sock_ids[BTF_SOCK_TYPE_SOCK_COMMON],
.arg3_type = ARG_PTR_TO_MAP_VALUE_OR_NULL,
- .arg4_type = ARG_ANYTHING,
+ .arg4_type = ARG_SCALAR,
.allowed = bpf_sk_storage_tracing_allowed,
};
diff --git a/net/core/filter.c b/net/core/filter.c
index 61940e753552..89f8755989c5 100644
--- a/net/core/filter.c
+++ b/net/core/filter.c
@@ -1746,10 +1746,10 @@ static const struct bpf_func_proto bpf_skb_store_bytes_proto = {
.gpl_only = false,
.ret_type = RET_INTEGER,
.arg1_type = ARG_PTR_TO_CTX,
- .arg2_type = ARG_ANYTHING,
+ .arg2_type = ARG_SCALAR,
.arg3_type = ARG_PTR_TO_MEM | MEM_RDONLY,
.arg4_type = ARG_MEM_SIZE,
- .arg5_type = ARG_ANYTHING,
+ .arg5_type = ARG_SCALAR,
};
int __bpf_skb_store_bytes(struct sk_buff *skb, u32 offset, const void *from,
@@ -1783,7 +1783,7 @@ static const struct bpf_func_proto bpf_skb_load_bytes_proto = {
.gpl_only = false,
.ret_type = RET_INTEGER,
.arg1_type = ARG_PTR_TO_CTX,
- .arg2_type = ARG_ANYTHING,
+ .arg2_type = ARG_SCALAR,
.arg3_type = ARG_PTR_TO_UNINIT_MEM,
.arg4_type = ARG_MEM_SIZE,
};
@@ -1822,7 +1822,7 @@ static const struct bpf_func_proto bpf_flow_dissector_load_bytes_proto = {
.gpl_only = false,
.ret_type = RET_INTEGER,
.arg1_type = ARG_PTR_TO_CTX,
- .arg2_type = ARG_ANYTHING,
+ .arg2_type = ARG_SCALAR,
.arg3_type = ARG_PTR_TO_UNINIT_MEM,
.arg4_type = ARG_MEM_SIZE,
};
@@ -1866,10 +1866,10 @@ static const struct bpf_func_proto bpf_skb_load_bytes_relative_proto = {
.gpl_only = false,
.ret_type = RET_INTEGER,
.arg1_type = ARG_PTR_TO_CTX,
- .arg2_type = ARG_ANYTHING,
+ .arg2_type = ARG_SCALAR,
.arg3_type = ARG_PTR_TO_UNINIT_MEM,
.arg4_type = ARG_MEM_SIZE,
- .arg5_type = ARG_ANYTHING,
+ .arg5_type = ARG_SCALAR,
};
BPF_CALL_2(bpf_skb_pull_data, struct sk_buff *, skb, u32, len)
@@ -1891,7 +1891,7 @@ static const struct bpf_func_proto bpf_skb_pull_data_proto = {
.gpl_only = false,
.ret_type = RET_INTEGER,
.arg1_type = ARG_PTR_TO_CTX,
- .arg2_type = ARG_ANYTHING,
+ .arg2_type = ARG_SCALAR,
};
BPF_CALL_1(bpf_sk_fullsock, struct sock *, sk)
@@ -1931,7 +1931,7 @@ static const struct bpf_func_proto sk_skb_pull_data_proto = {
.gpl_only = false,
.ret_type = RET_INTEGER,
.arg1_type = ARG_PTR_TO_CTX,
- .arg2_type = ARG_ANYTHING,
+ .arg2_type = ARG_SCALAR,
};
BPF_CALL_5(bpf_l3_csum_replace, struct sk_buff *, skb, u32, offset,
@@ -1972,10 +1972,10 @@ static const struct bpf_func_proto bpf_l3_csum_replace_proto = {
.gpl_only = false,
.ret_type = RET_INTEGER,
.arg1_type = ARG_PTR_TO_CTX,
- .arg2_type = ARG_ANYTHING,
- .arg3_type = ARG_ANYTHING,
- .arg4_type = ARG_ANYTHING,
- .arg5_type = ARG_ANYTHING,
+ .arg2_type = ARG_SCALAR,
+ .arg3_type = ARG_SCALAR,
+ .arg4_type = ARG_SCALAR,
+ .arg5_type = ARG_SCALAR,
};
BPF_CALL_5(bpf_l4_csum_replace, struct sk_buff *, skb, u32, offset,
@@ -2026,10 +2026,10 @@ static const struct bpf_func_proto bpf_l4_csum_replace_proto = {
.gpl_only = false,
.ret_type = RET_INTEGER,
.arg1_type = ARG_PTR_TO_CTX,
- .arg2_type = ARG_ANYTHING,
- .arg3_type = ARG_ANYTHING,
- .arg4_type = ARG_ANYTHING,
- .arg5_type = ARG_ANYTHING,
+ .arg2_type = ARG_SCALAR,
+ .arg3_type = ARG_SCALAR,
+ .arg4_type = ARG_SCALAR,
+ .arg5_type = ARG_SCALAR,
};
BPF_CALL_5(bpf_csum_diff, __be32 *, from, u32, from_size,
@@ -2067,7 +2067,7 @@ static const struct bpf_func_proto bpf_csum_diff_proto = {
.arg2_type = ARG_MEM_SIZE_OR_ZERO,
.arg3_type = ARG_PTR_TO_MEM | PTR_MAYBE_NULL | MEM_RDONLY,
.arg4_type = ARG_MEM_SIZE_OR_ZERO,
- .arg5_type = ARG_ANYTHING,
+ .arg5_type = ARG_SCALAR,
};
BPF_CALL_2(bpf_csum_update, struct sk_buff *, skb, __wsum, csum)
@@ -2087,7 +2087,7 @@ static const struct bpf_func_proto bpf_csum_update_proto = {
.gpl_only = false,
.ret_type = RET_INTEGER,
.arg1_type = ARG_PTR_TO_CTX,
- .arg2_type = ARG_ANYTHING,
+ .arg2_type = ARG_SCALAR,
};
BPF_CALL_2(bpf_csum_level, struct sk_buff *, skb, u64, level)
@@ -2121,7 +2121,7 @@ static const struct bpf_func_proto bpf_csum_level_proto = {
.gpl_only = false,
.ret_type = RET_INTEGER,
.arg1_type = ARG_PTR_TO_CTX,
- .arg2_type = ARG_ANYTHING,
+ .arg2_type = ARG_SCALAR,
};
static inline int __bpf_rx_skb(struct net_device *dev, struct sk_buff *skb)
@@ -2506,8 +2506,8 @@ static const struct bpf_func_proto bpf_clone_redirect_proto = {
.gpl_only = false,
.ret_type = RET_INTEGER,
.arg1_type = ARG_PTR_TO_CTX,
- .arg2_type = ARG_ANYTHING,
- .arg3_type = ARG_ANYTHING,
+ .arg2_type = ARG_SCALAR,
+ .arg3_type = ARG_SCALAR,
};
static struct net_device *skb_get_peer_dev(struct net_device *dev)
@@ -2575,8 +2575,8 @@ static const struct bpf_func_proto bpf_redirect_proto = {
.func = bpf_redirect,
.gpl_only = false,
.ret_type = RET_INTEGER,
- .arg1_type = ARG_ANYTHING,
- .arg2_type = ARG_ANYTHING,
+ .arg1_type = ARG_SCALAR,
+ .arg2_type = ARG_SCALAR,
};
BPF_CALL_2(bpf_redirect_peer, u32, ifindex, u64, flags)
@@ -2597,8 +2597,8 @@ static const struct bpf_func_proto bpf_redirect_peer_proto = {
.func = bpf_redirect_peer,
.gpl_only = false,
.ret_type = RET_INTEGER,
- .arg1_type = ARG_ANYTHING,
- .arg2_type = ARG_ANYTHING,
+ .arg1_type = ARG_SCALAR,
+ .arg2_type = ARG_SCALAR,
};
BPF_CALL_4(bpf_redirect_neigh, u32, ifindex, struct bpf_redir_neigh *, params,
@@ -2625,10 +2625,10 @@ static const struct bpf_func_proto bpf_redirect_neigh_proto = {
.func = bpf_redirect_neigh,
.gpl_only = false,
.ret_type = RET_INTEGER,
- .arg1_type = ARG_ANYTHING,
+ .arg1_type = ARG_SCALAR,
.arg2_type = ARG_PTR_TO_MEM | PTR_MAYBE_NULL | MEM_RDONLY,
.arg3_type = ARG_MEM_SIZE_OR_ZERO,
- .arg4_type = ARG_ANYTHING,
+ .arg4_type = ARG_SCALAR,
};
BPF_CALL_2(bpf_msg_apply_bytes, struct sk_msg *, msg, u32, bytes)
@@ -2642,7 +2642,7 @@ static const struct bpf_func_proto bpf_msg_apply_bytes_proto = {
.gpl_only = false,
.ret_type = RET_INTEGER,
.arg1_type = ARG_PTR_TO_CTX,
- .arg2_type = ARG_ANYTHING,
+ .arg2_type = ARG_SCALAR,
};
BPF_CALL_2(bpf_msg_cork_bytes, struct sk_msg *, msg, u32, bytes)
@@ -2701,7 +2701,7 @@ static const struct bpf_func_proto bpf_msg_cork_bytes_proto = {
.gpl_only = false,
.ret_type = RET_INTEGER,
.arg1_type = ARG_PTR_TO_CTX,
- .arg2_type = ARG_ANYTHING,
+ .arg2_type = ARG_SCALAR,
};
BPF_CALL_4(bpf_msg_pull_data, struct sk_msg *, msg, u32, start,
@@ -2836,9 +2836,9 @@ static const struct bpf_func_proto bpf_msg_pull_data_proto = {
.gpl_only = false,
.ret_type = RET_INTEGER,
.arg1_type = ARG_PTR_TO_CTX,
- .arg2_type = ARG_ANYTHING,
- .arg3_type = ARG_ANYTHING,
- .arg4_type = ARG_ANYTHING,
+ .arg2_type = ARG_SCALAR,
+ .arg3_type = ARG_SCALAR,
+ .arg4_type = ARG_SCALAR,
};
BPF_CALL_4(bpf_msg_push_data, struct sk_msg *, msg, u32, start,
@@ -3001,9 +3001,9 @@ static const struct bpf_func_proto bpf_msg_push_data_proto = {
.gpl_only = false,
.ret_type = RET_INTEGER,
.arg1_type = ARG_PTR_TO_CTX,
- .arg2_type = ARG_ANYTHING,
- .arg3_type = ARG_ANYTHING,
- .arg4_type = ARG_ANYTHING,
+ .arg2_type = ARG_SCALAR,
+ .arg3_type = ARG_SCALAR,
+ .arg4_type = ARG_SCALAR,
};
static void sk_msg_shift_left(struct sk_msg *msg, int i)
@@ -3185,9 +3185,9 @@ static const struct bpf_func_proto bpf_msg_pop_data_proto = {
.gpl_only = false,
.ret_type = RET_INTEGER,
.arg1_type = ARG_PTR_TO_CTX,
- .arg2_type = ARG_ANYTHING,
- .arg3_type = ARG_ANYTHING,
- .arg4_type = ARG_ANYTHING,
+ .arg2_type = ARG_SCALAR,
+ .arg3_type = ARG_SCALAR,
+ .arg4_type = ARG_SCALAR,
};
#ifdef CONFIG_CGROUP_NET_CLASSID
@@ -3292,7 +3292,7 @@ static const struct bpf_func_proto bpf_set_hash_proto = {
.gpl_only = false,
.ret_type = RET_INTEGER,
.arg1_type = ARG_PTR_TO_CTX,
- .arg2_type = ARG_ANYTHING,
+ .arg2_type = ARG_SCALAR,
};
BPF_CALL_3(bpf_skb_vlan_push, struct sk_buff *, skb, __be16, vlan_proto,
@@ -3318,8 +3318,8 @@ static const struct bpf_func_proto bpf_skb_vlan_push_proto = {
.gpl_only = false,
.ret_type = RET_INTEGER,
.arg1_type = ARG_PTR_TO_CTX,
- .arg2_type = ARG_ANYTHING,
- .arg3_type = ARG_ANYTHING,
+ .arg2_type = ARG_SCALAR,
+ .arg3_type = ARG_SCALAR,
};
BPF_CALL_1(bpf_skb_vlan_pop, struct sk_buff *, skb)
@@ -3532,8 +3532,8 @@ static const struct bpf_func_proto bpf_skb_change_proto_proto = {
.gpl_only = false,
.ret_type = RET_INTEGER,
.arg1_type = ARG_PTR_TO_CTX,
- .arg2_type = ARG_ANYTHING,
- .arg3_type = ARG_ANYTHING,
+ .arg2_type = ARG_SCALAR,
+ .arg3_type = ARG_SCALAR,
};
BPF_CALL_2(bpf_skb_change_type, struct sk_buff *, skb, u32, pkt_type)
@@ -3552,7 +3552,7 @@ static const struct bpf_func_proto bpf_skb_change_type_proto = {
.gpl_only = false,
.ret_type = RET_INTEGER,
.arg1_type = ARG_PTR_TO_CTX,
- .arg2_type = ARG_ANYTHING,
+ .arg2_type = ARG_SCALAR,
};
static u32 bpf_skb_net_base_len(const struct sk_buff *skb)
@@ -3839,9 +3839,9 @@ static const struct bpf_func_proto sk_skb_adjust_room_proto = {
.gpl_only = false,
.ret_type = RET_INTEGER,
.arg1_type = ARG_PTR_TO_CTX,
- .arg2_type = ARG_ANYTHING,
- .arg3_type = ARG_ANYTHING,
- .arg4_type = ARG_ANYTHING,
+ .arg2_type = ARG_SCALAR,
+ .arg3_type = ARG_SCALAR,
+ .arg4_type = ARG_SCALAR,
};
BPF_CALL_4(bpf_skb_adjust_room, struct sk_buff *, skb, s32, len_diff,
@@ -3944,9 +3944,9 @@ static const struct bpf_func_proto bpf_skb_adjust_room_proto = {
.gpl_only = false,
.ret_type = RET_INTEGER,
.arg1_type = ARG_PTR_TO_CTX,
- .arg2_type = ARG_ANYTHING,
- .arg3_type = ARG_ANYTHING,
- .arg4_type = ARG_ANYTHING,
+ .arg2_type = ARG_SCALAR,
+ .arg3_type = ARG_SCALAR,
+ .arg4_type = ARG_SCALAR,
};
static u32 __bpf_skb_min_len(const struct sk_buff *skb)
@@ -4040,8 +4040,8 @@ static const struct bpf_func_proto bpf_skb_change_tail_proto = {
.gpl_only = false,
.ret_type = RET_INTEGER,
.arg1_type = ARG_PTR_TO_CTX,
- .arg2_type = ARG_ANYTHING,
- .arg3_type = ARG_ANYTHING,
+ .arg2_type = ARG_SCALAR,
+ .arg3_type = ARG_SCALAR,
};
BPF_CALL_3(sk_skb_change_tail, struct sk_buff *, skb, u32, new_len,
@@ -4055,8 +4055,8 @@ static const struct bpf_func_proto sk_skb_change_tail_proto = {
.gpl_only = false,
.ret_type = RET_INTEGER,
.arg1_type = ARG_PTR_TO_CTX,
- .arg2_type = ARG_ANYTHING,
- .arg3_type = ARG_ANYTHING,
+ .arg2_type = ARG_SCALAR,
+ .arg3_type = ARG_SCALAR,
};
static inline int __bpf_skb_change_head(struct sk_buff *skb, u32 head_room,
@@ -4107,8 +4107,8 @@ static const struct bpf_func_proto bpf_skb_change_head_proto = {
.gpl_only = false,
.ret_type = RET_INTEGER,
.arg1_type = ARG_PTR_TO_CTX,
- .arg2_type = ARG_ANYTHING,
- .arg3_type = ARG_ANYTHING,
+ .arg2_type = ARG_SCALAR,
+ .arg3_type = ARG_SCALAR,
};
BPF_CALL_3(sk_skb_change_head, struct sk_buff *, skb, u32, head_room,
@@ -4122,8 +4122,8 @@ static const struct bpf_func_proto sk_skb_change_head_proto = {
.gpl_only = false,
.ret_type = RET_INTEGER,
.arg1_type = ARG_PTR_TO_CTX,
- .arg2_type = ARG_ANYTHING,
- .arg3_type = ARG_ANYTHING,
+ .arg2_type = ARG_SCALAR,
+ .arg3_type = ARG_SCALAR,
};
BPF_CALL_1(bpf_xdp_get_buff_len, struct xdp_buff*, xdp)
@@ -4178,7 +4178,7 @@ static const struct bpf_func_proto bpf_xdp_adjust_head_proto = {
.gpl_only = false,
.ret_type = RET_INTEGER,
.arg1_type = ARG_PTR_TO_CTX,
- .arg2_type = ARG_ANYTHING,
+ .arg2_type = ARG_SCALAR,
};
void bpf_xdp_copy_buf(struct xdp_buff *xdp, unsigned long off,
@@ -4282,7 +4282,7 @@ static const struct bpf_func_proto bpf_xdp_load_bytes_proto = {
.gpl_only = false,
.ret_type = RET_INTEGER,
.arg1_type = ARG_PTR_TO_CTX,
- .arg2_type = ARG_ANYTHING,
+ .arg2_type = ARG_SCALAR,
.arg3_type = ARG_PTR_TO_UNINIT_MEM,
.arg4_type = ARG_MEM_SIZE,
};
@@ -4314,7 +4314,7 @@ static const struct bpf_func_proto bpf_xdp_store_bytes_proto = {
.gpl_only = false,
.ret_type = RET_INTEGER,
.arg1_type = ARG_PTR_TO_CTX,
- .arg2_type = ARG_ANYTHING,
+ .arg2_type = ARG_SCALAR,
.arg3_type = ARG_PTR_TO_MEM | MEM_RDONLY,
.arg4_type = ARG_MEM_SIZE,
};
@@ -4453,7 +4453,7 @@ static const struct bpf_func_proto bpf_xdp_adjust_tail_proto = {
.gpl_only = false,
.ret_type = RET_INTEGER,
.arg1_type = ARG_PTR_TO_CTX,
- .arg2_type = ARG_ANYTHING,
+ .arg2_type = ARG_SCALAR,
};
BPF_CALL_2(bpf_xdp_adjust_meta, struct xdp_buff *, xdp, int, offset)
@@ -4480,7 +4480,7 @@ static const struct bpf_func_proto bpf_xdp_adjust_meta_proto = {
.gpl_only = false,
.ret_type = RET_INTEGER,
.arg1_type = ARG_PTR_TO_CTX,
- .arg2_type = ARG_ANYTHING,
+ .arg2_type = ARG_SCALAR,
};
/**
@@ -4825,8 +4825,8 @@ static const struct bpf_func_proto bpf_xdp_redirect_proto = {
.func = bpf_xdp_redirect,
.gpl_only = false,
.ret_type = RET_INTEGER,
- .arg1_type = ARG_ANYTHING,
- .arg2_type = ARG_ANYTHING,
+ .arg1_type = ARG_SCALAR,
+ .arg2_type = ARG_SCALAR,
};
BPF_CALL_3(bpf_xdp_redirect_map, struct bpf_map *, map, u64, key,
@@ -4840,8 +4840,8 @@ static const struct bpf_func_proto bpf_xdp_redirect_map_proto = {
.gpl_only = false,
.ret_type = RET_INTEGER,
.arg1_type = ARG_CONST_MAP_PTR,
- .arg2_type = ARG_ANYTHING,
- .arg3_type = ARG_ANYTHING,
+ .arg2_type = ARG_SCALAR,
+ .arg3_type = ARG_SCALAR,
};
static unsigned long bpf_skb_copy(void *dst_buff, const void *skb,
@@ -4877,7 +4877,7 @@ static const struct bpf_func_proto bpf_skb_event_output_proto = {
.ret_type = RET_INTEGER,
.arg1_type = ARG_PTR_TO_CTX,
.arg2_type = ARG_CONST_MAP_PTR,
- .arg3_type = ARG_ANYTHING,
+ .arg3_type = ARG_SCALAR,
.arg4_type = ARG_PTR_TO_MEM | MEM_RDONLY,
.arg5_type = ARG_MEM_SIZE_OR_ZERO,
};
@@ -4891,7 +4891,7 @@ const struct bpf_func_proto bpf_skb_output_proto = {
.arg1_type = ARG_PTR_TO_BTF_ID,
.arg1_btf_id = &bpf_skb_output_btf_ids[0],
.arg2_type = ARG_CONST_MAP_PTR,
- .arg3_type = ARG_ANYTHING,
+ .arg3_type = ARG_SCALAR,
.arg4_type = ARG_PTR_TO_MEM | MEM_RDONLY,
.arg5_type = ARG_MEM_SIZE_OR_ZERO,
};
@@ -4977,7 +4977,7 @@ static const struct bpf_func_proto bpf_skb_get_tunnel_key_proto = {
.arg1_type = ARG_PTR_TO_CTX,
.arg2_type = ARG_PTR_TO_UNINIT_MEM,
.arg3_type = ARG_MEM_SIZE,
- .arg4_type = ARG_ANYTHING,
+ .arg4_type = ARG_SCALAR,
};
BPF_CALL_3(bpf_skb_get_tunnel_opt, struct sk_buff *, skb, u8 *, to, u32, size)
@@ -5094,7 +5094,7 @@ static const struct bpf_func_proto bpf_skb_set_tunnel_key_proto = {
.arg1_type = ARG_PTR_TO_CTX,
.arg2_type = ARG_PTR_TO_MEM | MEM_RDONLY,
.arg3_type = ARG_MEM_SIZE,
- .arg4_type = ARG_ANYTHING,
+ .arg4_type = ARG_SCALAR,
};
BPF_CALL_3(bpf_skb_set_tunnel_opt, struct sk_buff *, skb,
@@ -5175,7 +5175,7 @@ static const struct bpf_func_proto bpf_skb_under_cgroup_proto = {
.ret_type = RET_INTEGER,
.arg1_type = ARG_PTR_TO_CTX,
.arg2_type = ARG_CONST_MAP_PTR,
- .arg3_type = ARG_ANYTHING,
+ .arg3_type = ARG_SCALAR,
};
#ifdef CONFIG_SOCK_CGROUP_DATA
@@ -5232,7 +5232,7 @@ static const struct bpf_func_proto bpf_skb_ancestor_cgroup_id_proto = {
.gpl_only = false,
.ret_type = RET_INTEGER,
.arg1_type = ARG_PTR_TO_CTX,
- .arg2_type = ARG_ANYTHING,
+ .arg2_type = ARG_SCALAR,
};
BPF_CALL_1(bpf_sk_cgroup_id, struct sock *, sk)
@@ -5257,7 +5257,7 @@ static const struct bpf_func_proto bpf_sk_ancestor_cgroup_id_proto = {
.gpl_only = false,
.ret_type = RET_INTEGER,
.arg1_type = ARG_PTR_TO_BTF_ID_SOCK_COMMON,
- .arg2_type = ARG_ANYTHING,
+ .arg2_type = ARG_SCALAR,
};
#endif
@@ -5291,7 +5291,7 @@ static const struct bpf_func_proto bpf_xdp_event_output_proto = {
.ret_type = RET_INTEGER,
.arg1_type = ARG_PTR_TO_CTX,
.arg2_type = ARG_CONST_MAP_PTR,
- .arg3_type = ARG_ANYTHING,
+ .arg3_type = ARG_SCALAR,
.arg4_type = ARG_PTR_TO_MEM | MEM_RDONLY,
.arg5_type = ARG_MEM_SIZE_OR_ZERO,
};
@@ -5305,7 +5305,7 @@ const struct bpf_func_proto bpf_xdp_output_proto = {
.arg1_type = ARG_PTR_TO_BTF_ID,
.arg1_btf_id = &bpf_xdp_output_btf_ids[0],
.arg2_type = ARG_CONST_MAP_PTR,
- .arg3_type = ARG_ANYTHING,
+ .arg3_type = ARG_SCALAR,
.arg4_type = ARG_PTR_TO_MEM | MEM_RDONLY,
.arg5_type = ARG_MEM_SIZE_OR_ZERO,
};
@@ -5853,8 +5853,8 @@ const struct bpf_func_proto bpf_sk_setsockopt_proto = {
.gpl_only = false,
.ret_type = RET_INTEGER,
.arg1_type = ARG_PTR_TO_BTF_ID_SOCK_COMMON,
- .arg2_type = ARG_ANYTHING,
- .arg3_type = ARG_ANYTHING,
+ .arg2_type = ARG_SCALAR,
+ .arg3_type = ARG_SCALAR,
.arg4_type = ARG_PTR_TO_MEM | MEM_RDONLY,
.arg5_type = ARG_MEM_SIZE,
};
@@ -5870,8 +5870,8 @@ const struct bpf_func_proto bpf_sk_getsockopt_proto = {
.gpl_only = false,
.ret_type = RET_INTEGER,
.arg1_type = ARG_PTR_TO_BTF_ID_SOCK_COMMON,
- .arg2_type = ARG_ANYTHING,
- .arg3_type = ARG_ANYTHING,
+ .arg2_type = ARG_SCALAR,
+ .arg3_type = ARG_SCALAR,
.arg4_type = ARG_PTR_TO_UNINIT_MEM,
.arg5_type = ARG_MEM_SIZE,
};
@@ -5894,8 +5894,8 @@ const struct bpf_func_proto bpf_sk_setsockopt_nodelay_proto = {
.gpl_only = false,
.ret_type = RET_INTEGER,
.arg1_type = ARG_PTR_TO_BTF_ID_SOCK_COMMON,
- .arg2_type = ARG_ANYTHING,
- .arg3_type = ARG_ANYTHING,
+ .arg2_type = ARG_SCALAR,
+ .arg3_type = ARG_SCALAR,
.arg4_type = ARG_PTR_TO_MEM | MEM_RDONLY,
.arg5_type = ARG_MEM_SIZE,
};
@@ -5911,8 +5911,8 @@ const struct bpf_func_proto bpf_unlocked_sk_setsockopt_proto = {
.gpl_only = false,
.ret_type = RET_INTEGER,
.arg1_type = ARG_PTR_TO_BTF_ID_SOCK_COMMON,
- .arg2_type = ARG_ANYTHING,
- .arg3_type = ARG_ANYTHING,
+ .arg2_type = ARG_SCALAR,
+ .arg3_type = ARG_SCALAR,
.arg4_type = ARG_PTR_TO_MEM | MEM_RDONLY,
.arg5_type = ARG_MEM_SIZE,
};
@@ -5928,8 +5928,8 @@ const struct bpf_func_proto bpf_unlocked_sk_getsockopt_proto = {
.gpl_only = false,
.ret_type = RET_INTEGER,
.arg1_type = ARG_PTR_TO_BTF_ID_SOCK_COMMON,
- .arg2_type = ARG_ANYTHING,
- .arg3_type = ARG_ANYTHING,
+ .arg2_type = ARG_SCALAR,
+ .arg3_type = ARG_SCALAR,
.arg4_type = ARG_PTR_TO_UNINIT_MEM,
.arg5_type = ARG_MEM_SIZE,
};
@@ -5945,8 +5945,8 @@ static const struct bpf_func_proto bpf_sock_addr_setsockopt_proto = {
.gpl_only = false,
.ret_type = RET_INTEGER,
.arg1_type = ARG_PTR_TO_CTX,
- .arg2_type = ARG_ANYTHING,
- .arg3_type = ARG_ANYTHING,
+ .arg2_type = ARG_SCALAR,
+ .arg3_type = ARG_SCALAR,
.arg4_type = ARG_PTR_TO_MEM | MEM_RDONLY,
.arg5_type = ARG_MEM_SIZE,
};
@@ -5962,8 +5962,8 @@ static const struct bpf_func_proto bpf_sock_addr_getsockopt_proto = {
.gpl_only = false,
.ret_type = RET_INTEGER,
.arg1_type = ARG_PTR_TO_CTX,
- .arg2_type = ARG_ANYTHING,
- .arg3_type = ARG_ANYTHING,
+ .arg2_type = ARG_SCALAR,
+ .arg3_type = ARG_SCALAR,
.arg4_type = ARG_PTR_TO_UNINIT_MEM,
.arg5_type = ARG_MEM_SIZE,
};
@@ -6007,8 +6007,8 @@ static const struct bpf_func_proto bpf_sock_create_setsockopt_proto = {
.gpl_only = false,
.ret_type = RET_INTEGER,
.arg1_type = ARG_PTR_TO_CTX,
- .arg2_type = ARG_ANYTHING,
- .arg3_type = ARG_ANYTHING,
+ .arg2_type = ARG_SCALAR,
+ .arg3_type = ARG_SCALAR,
.arg4_type = ARG_PTR_TO_MEM | MEM_RDONLY,
.arg5_type = ARG_MEM_SIZE,
};
@@ -6033,8 +6033,8 @@ static const struct bpf_func_proto bpf_sock_create_getsockopt_proto = {
.gpl_only = false,
.ret_type = RET_INTEGER,
.arg1_type = ARG_PTR_TO_CTX,
- .arg2_type = ARG_ANYTHING,
- .arg3_type = ARG_ANYTHING,
+ .arg2_type = ARG_SCALAR,
+ .arg3_type = ARG_SCALAR,
.arg4_type = ARG_PTR_TO_UNINIT_MEM,
.arg5_type = ARG_MEM_SIZE,
};
@@ -6059,8 +6059,8 @@ static const struct bpf_func_proto bpf_sock_ops_setsockopt_proto = {
.gpl_only = false,
.ret_type = RET_INTEGER,
.arg1_type = ARG_PTR_TO_CTX,
- .arg2_type = ARG_ANYTHING,
- .arg3_type = ARG_ANYTHING,
+ .arg2_type = ARG_SCALAR,
+ .arg3_type = ARG_SCALAR,
.arg4_type = ARG_PTR_TO_MEM | MEM_RDONLY,
.arg5_type = ARG_MEM_SIZE,
};
@@ -6169,8 +6169,8 @@ static const struct bpf_func_proto bpf_sock_ops_getsockopt_proto = {
.gpl_only = false,
.ret_type = RET_INTEGER,
.arg1_type = ARG_PTR_TO_CTX,
- .arg2_type = ARG_ANYTHING,
- .arg3_type = ARG_ANYTHING,
+ .arg2_type = ARG_SCALAR,
+ .arg3_type = ARG_SCALAR,
.arg4_type = ARG_PTR_TO_UNINIT_MEM,
.arg5_type = ARG_MEM_SIZE,
};
@@ -6197,7 +6197,7 @@ static const struct bpf_func_proto bpf_sock_ops_cb_flags_set_proto = {
.gpl_only = false,
.ret_type = RET_INTEGER,
.arg1_type = ARG_PTR_TO_CTX,
- .arg2_type = ARG_ANYTHING,
+ .arg2_type = ARG_SCALAR,
};
BPF_CALL_3(bpf_bind, struct bpf_sock_addr_kern *, ctx, struct sockaddr *, addr,
@@ -6290,10 +6290,10 @@ static const struct bpf_func_proto bpf_skb_get_xfrm_state_proto = {
.gpl_only = false,
.ret_type = RET_INTEGER,
.arg1_type = ARG_PTR_TO_CTX,
- .arg2_type = ARG_ANYTHING,
+ .arg2_type = ARG_SCALAR,
.arg3_type = ARG_PTR_TO_UNINIT_MEM,
.arg4_type = ARG_MEM_SIZE,
- .arg5_type = ARG_ANYTHING,
+ .arg5_type = ARG_SCALAR,
};
#endif
@@ -6700,7 +6700,7 @@ static const struct bpf_func_proto bpf_xdp_fib_lookup_proto = {
.arg1_type = ARG_PTR_TO_CTX,
.arg2_type = ARG_PTR_TO_MEM | MEM_WRITE,
.arg3_type = ARG_MEM_SIZE,
- .arg4_type = ARG_ANYTHING,
+ .arg4_type = ARG_SCALAR,
};
BPF_CALL_4(bpf_skb_fib_lookup, struct sk_buff *, skb,
@@ -6760,7 +6760,7 @@ static const struct bpf_func_proto bpf_skb_fib_lookup_proto = {
.arg1_type = ARG_PTR_TO_CTX,
.arg2_type = ARG_PTR_TO_MEM | MEM_WRITE,
.arg3_type = ARG_MEM_SIZE,
- .arg4_type = ARG_ANYTHING,
+ .arg4_type = ARG_SCALAR,
};
static struct net_device *__dev_via_ifindex(struct net_device *dev_curr,
@@ -6857,11 +6857,11 @@ static const struct bpf_func_proto bpf_skb_check_mtu_proto = {
.gpl_only = true,
.ret_type = RET_INTEGER,
.arg1_type = ARG_PTR_TO_CTX,
- .arg2_type = ARG_ANYTHING,
+ .arg2_type = ARG_SCALAR,
.arg3_type = ARG_PTR_TO_FIXED_SIZE_MEM | MEM_WRITE | MEM_ALIGNED,
.arg3_size = sizeof(u32),
- .arg4_type = ARG_ANYTHING,
- .arg5_type = ARG_ANYTHING,
+ .arg4_type = ARG_SCALAR,
+ .arg5_type = ARG_SCALAR,
};
static const struct bpf_func_proto bpf_xdp_check_mtu_proto = {
@@ -6869,11 +6869,11 @@ static const struct bpf_func_proto bpf_xdp_check_mtu_proto = {
.gpl_only = true,
.ret_type = RET_INTEGER,
.arg1_type = ARG_PTR_TO_CTX,
- .arg2_type = ARG_ANYTHING,
+ .arg2_type = ARG_SCALAR,
.arg3_type = ARG_PTR_TO_FIXED_SIZE_MEM | MEM_WRITE | MEM_ALIGNED,
.arg3_size = sizeof(u32),
- .arg4_type = ARG_ANYTHING,
- .arg5_type = ARG_ANYTHING,
+ .arg4_type = ARG_SCALAR,
+ .arg5_type = ARG_SCALAR,
};
#if IS_ENABLED(CONFIG_IPV6_SEG6_BPF)
@@ -6955,7 +6955,7 @@ static const struct bpf_func_proto bpf_lwt_in_push_encap_proto = {
.gpl_only = false,
.ret_type = RET_INTEGER,
.arg1_type = ARG_PTR_TO_CTX,
- .arg2_type = ARG_ANYTHING,
+ .arg2_type = ARG_SCALAR,
.arg3_type = ARG_PTR_TO_MEM | MEM_RDONLY,
.arg4_type = ARG_MEM_SIZE
};
@@ -6965,7 +6965,7 @@ static const struct bpf_func_proto bpf_lwt_xmit_push_encap_proto = {
.gpl_only = false,
.ret_type = RET_INTEGER,
.arg1_type = ARG_PTR_TO_CTX,
- .arg2_type = ARG_ANYTHING,
+ .arg2_type = ARG_SCALAR,
.arg3_type = ARG_PTR_TO_MEM | MEM_RDONLY,
.arg4_type = ARG_MEM_SIZE
};
@@ -7009,7 +7009,7 @@ static const struct bpf_func_proto bpf_lwt_seg6_store_bytes_proto = {
.gpl_only = false,
.ret_type = RET_INTEGER,
.arg1_type = ARG_PTR_TO_CTX,
- .arg2_type = ARG_ANYTHING,
+ .arg2_type = ARG_SCALAR,
.arg3_type = ARG_PTR_TO_MEM | MEM_RDONLY,
.arg4_type = ARG_MEM_SIZE
};
@@ -7098,7 +7098,7 @@ static const struct bpf_func_proto bpf_lwt_seg6_action_proto = {
.gpl_only = false,
.ret_type = RET_INTEGER,
.arg1_type = ARG_PTR_TO_CTX,
- .arg2_type = ARG_ANYTHING,
+ .arg2_type = ARG_SCALAR,
.arg3_type = ARG_PTR_TO_MEM | MEM_RDONLY,
.arg4_type = ARG_MEM_SIZE
};
@@ -7159,8 +7159,8 @@ static const struct bpf_func_proto bpf_lwt_seg6_adjust_srh_proto = {
.gpl_only = false,
.ret_type = RET_INTEGER,
.arg1_type = ARG_PTR_TO_CTX,
- .arg2_type = ARG_ANYTHING,
- .arg3_type = ARG_ANYTHING,
+ .arg2_type = ARG_SCALAR,
+ .arg3_type = ARG_SCALAR,
};
#endif /* CONFIG_IPV6_SEG6_BPF */
@@ -7332,8 +7332,8 @@ static const struct bpf_func_proto bpf_skc_lookup_tcp_proto = {
.arg1_type = ARG_PTR_TO_CTX,
.arg2_type = ARG_PTR_TO_MEM | MEM_RDONLY,
.arg3_type = ARG_MEM_SIZE_OR_ZERO,
- .arg4_type = ARG_ANYTHING,
- .arg5_type = ARG_ANYTHING,
+ .arg4_type = ARG_SCALAR,
+ .arg5_type = ARG_SCALAR,
};
BPF_CALL_5(bpf_sk_lookup_tcp, struct sk_buff *, skb,
@@ -7351,8 +7351,8 @@ static const struct bpf_func_proto bpf_sk_lookup_tcp_proto = {
.arg1_type = ARG_PTR_TO_CTX,
.arg2_type = ARG_PTR_TO_MEM | MEM_RDONLY,
.arg3_type = ARG_MEM_SIZE_OR_ZERO,
- .arg4_type = ARG_ANYTHING,
- .arg5_type = ARG_ANYTHING,
+ .arg4_type = ARG_SCALAR,
+ .arg5_type = ARG_SCALAR,
};
BPF_CALL_5(bpf_sk_lookup_udp, struct sk_buff *, skb,
@@ -7370,8 +7370,8 @@ static const struct bpf_func_proto bpf_sk_lookup_udp_proto = {
.arg1_type = ARG_PTR_TO_CTX,
.arg2_type = ARG_PTR_TO_MEM | MEM_RDONLY,
.arg3_type = ARG_MEM_SIZE_OR_ZERO,
- .arg4_type = ARG_ANYTHING,
- .arg5_type = ARG_ANYTHING,
+ .arg4_type = ARG_SCALAR,
+ .arg5_type = ARG_SCALAR,
};
BPF_CALL_5(bpf_tc_skc_lookup_tcp, struct sk_buff *, skb,
@@ -7394,8 +7394,8 @@ static const struct bpf_func_proto bpf_tc_skc_lookup_tcp_proto = {
.arg1_type = ARG_PTR_TO_CTX,
.arg2_type = ARG_PTR_TO_MEM | MEM_RDONLY,
.arg3_type = ARG_MEM_SIZE_OR_ZERO,
- .arg4_type = ARG_ANYTHING,
- .arg5_type = ARG_ANYTHING,
+ .arg4_type = ARG_SCALAR,
+ .arg5_type = ARG_SCALAR,
};
BPF_CALL_5(bpf_tc_sk_lookup_tcp, struct sk_buff *, skb,
@@ -7418,8 +7418,8 @@ static const struct bpf_func_proto bpf_tc_sk_lookup_tcp_proto = {
.arg1_type = ARG_PTR_TO_CTX,
.arg2_type = ARG_PTR_TO_MEM | MEM_RDONLY,
.arg3_type = ARG_MEM_SIZE_OR_ZERO,
- .arg4_type = ARG_ANYTHING,
- .arg5_type = ARG_ANYTHING,
+ .arg4_type = ARG_SCALAR,
+ .arg5_type = ARG_SCALAR,
};
BPF_CALL_5(bpf_tc_sk_lookup_udp, struct sk_buff *, skb,
@@ -7442,8 +7442,8 @@ static const struct bpf_func_proto bpf_tc_sk_lookup_udp_proto = {
.arg1_type = ARG_PTR_TO_CTX,
.arg2_type = ARG_PTR_TO_MEM | MEM_RDONLY,
.arg3_type = ARG_MEM_SIZE_OR_ZERO,
- .arg4_type = ARG_ANYTHING,
- .arg5_type = ARG_ANYTHING,
+ .arg4_type = ARG_SCALAR,
+ .arg5_type = ARG_SCALAR,
};
BPF_CALL_1(bpf_sk_release, struct sock *, sk)
@@ -7480,8 +7480,8 @@ static const struct bpf_func_proto bpf_xdp_sk_lookup_udp_proto = {
.arg1_type = ARG_PTR_TO_CTX,
.arg2_type = ARG_PTR_TO_MEM | MEM_RDONLY,
.arg3_type = ARG_MEM_SIZE_OR_ZERO,
- .arg4_type = ARG_ANYTHING,
- .arg5_type = ARG_ANYTHING,
+ .arg4_type = ARG_SCALAR,
+ .arg5_type = ARG_SCALAR,
};
BPF_CALL_5(bpf_xdp_skc_lookup_tcp, struct xdp_buff *, ctx,
@@ -7504,8 +7504,8 @@ static const struct bpf_func_proto bpf_xdp_skc_lookup_tcp_proto = {
.arg1_type = ARG_PTR_TO_CTX,
.arg2_type = ARG_PTR_TO_MEM | MEM_RDONLY,
.arg3_type = ARG_MEM_SIZE_OR_ZERO,
- .arg4_type = ARG_ANYTHING,
- .arg5_type = ARG_ANYTHING,
+ .arg4_type = ARG_SCALAR,
+ .arg5_type = ARG_SCALAR,
};
BPF_CALL_5(bpf_xdp_sk_lookup_tcp, struct xdp_buff *, ctx,
@@ -7528,8 +7528,8 @@ static const struct bpf_func_proto bpf_xdp_sk_lookup_tcp_proto = {
.arg1_type = ARG_PTR_TO_CTX,
.arg2_type = ARG_PTR_TO_MEM | MEM_RDONLY,
.arg3_type = ARG_MEM_SIZE_OR_ZERO,
- .arg4_type = ARG_ANYTHING,
- .arg5_type = ARG_ANYTHING,
+ .arg4_type = ARG_SCALAR,
+ .arg5_type = ARG_SCALAR,
};
BPF_CALL_5(bpf_sock_addr_skc_lookup_tcp, struct bpf_sock_addr_kern *, ctx,
@@ -7548,8 +7548,8 @@ static const struct bpf_func_proto bpf_sock_addr_skc_lookup_tcp_proto = {
.arg1_type = ARG_PTR_TO_CTX,
.arg2_type = ARG_PTR_TO_MEM | MEM_RDONLY,
.arg3_type = ARG_MEM_SIZE_OR_ZERO,
- .arg4_type = ARG_ANYTHING,
- .arg5_type = ARG_ANYTHING,
+ .arg4_type = ARG_SCALAR,
+ .arg5_type = ARG_SCALAR,
};
BPF_CALL_5(bpf_sock_addr_sk_lookup_tcp, struct bpf_sock_addr_kern *, ctx,
@@ -7567,8 +7567,8 @@ static const struct bpf_func_proto bpf_sock_addr_sk_lookup_tcp_proto = {
.arg1_type = ARG_PTR_TO_CTX,
.arg2_type = ARG_PTR_TO_MEM | MEM_RDONLY,
.arg3_type = ARG_MEM_SIZE_OR_ZERO,
- .arg4_type = ARG_ANYTHING,
- .arg5_type = ARG_ANYTHING,
+ .arg4_type = ARG_SCALAR,
+ .arg5_type = ARG_SCALAR,
};
BPF_CALL_5(bpf_sock_addr_sk_lookup_udp, struct bpf_sock_addr_kern *, ctx,
@@ -7586,8 +7586,8 @@ static const struct bpf_func_proto bpf_sock_addr_sk_lookup_udp_proto = {
.arg1_type = ARG_PTR_TO_CTX,
.arg2_type = ARG_PTR_TO_MEM | MEM_RDONLY,
.arg3_type = ARG_MEM_SIZE_OR_ZERO,
- .arg4_type = ARG_ANYTHING,
- .arg5_type = ARG_ANYTHING,
+ .arg4_type = ARG_SCALAR,
+ .arg5_type = ARG_SCALAR,
};
bool bpf_tcp_sock_is_valid_access(int off, int size, enum bpf_access_type type,
@@ -8006,7 +8006,7 @@ static const struct bpf_func_proto bpf_sk_assign_proto = {
.ret_type = RET_INTEGER,
.arg1_type = ARG_PTR_TO_CTX,
.arg2_type = ARG_PTR_TO_BTF_ID_SOCK_COMMON,
- .arg3_type = ARG_ANYTHING,
+ .arg3_type = ARG_SCALAR,
};
static const u8 *bpf_search_tcp_opt(const u8 *op, const u8 *opend,
@@ -8131,7 +8131,7 @@ static const struct bpf_func_proto bpf_sock_ops_load_hdr_opt_proto = {
.arg1_type = ARG_PTR_TO_CTX,
.arg2_type = ARG_PTR_TO_MEM | MEM_WRITE,
.arg3_type = ARG_MEM_SIZE,
- .arg4_type = ARG_ANYTHING,
+ .arg4_type = ARG_SCALAR,
};
BPF_CALL_4(bpf_sock_ops_store_hdr_opt, struct bpf_sock_ops_kern *, bpf_sock,
@@ -8209,7 +8209,7 @@ static const struct bpf_func_proto bpf_sock_ops_store_hdr_opt_proto = {
.arg1_type = ARG_PTR_TO_CTX,
.arg2_type = ARG_PTR_TO_MEM | MEM_RDONLY,
.arg3_type = ARG_MEM_SIZE,
- .arg4_type = ARG_ANYTHING,
+ .arg4_type = ARG_SCALAR,
};
BPF_CALL_3(bpf_sock_ops_reserve_hdr_opt, struct bpf_sock_ops_kern *, bpf_sock,
@@ -8234,8 +8234,8 @@ static const struct bpf_func_proto bpf_sock_ops_reserve_hdr_opt_proto = {
.gpl_only = false,
.ret_type = RET_INTEGER,
.arg1_type = ARG_PTR_TO_CTX,
- .arg2_type = ARG_ANYTHING,
- .arg3_type = ARG_ANYTHING,
+ .arg2_type = ARG_SCALAR,
+ .arg3_type = ARG_SCALAR,
};
BPF_CALL_3(bpf_skb_set_tstamp, struct sk_buff *, skb,
@@ -8275,8 +8275,8 @@ static const struct bpf_func_proto bpf_skb_set_tstamp_proto = {
.gpl_only = false,
.ret_type = RET_INTEGER,
.arg1_type = ARG_PTR_TO_CTX,
- .arg2_type = ARG_ANYTHING,
- .arg3_type = ARG_ANYTHING,
+ .arg2_type = ARG_SCALAR,
+ .arg3_type = ARG_SCALAR,
};
#ifdef CONFIG_SYN_COOKIES
@@ -11787,7 +11787,7 @@ static const struct bpf_func_proto sk_select_reuseport_proto = {
.arg1_type = ARG_PTR_TO_CTX,
.arg2_type = ARG_CONST_MAP_PTR,
.arg3_type = ARG_PTR_TO_MAP_KEY,
- .arg4_type = ARG_ANYTHING,
+ .arg4_type = ARG_SCALAR,
};
BPF_CALL_4(sk_reuseport_load_bytes,
@@ -11802,7 +11802,7 @@ static const struct bpf_func_proto sk_reuseport_load_bytes_proto = {
.gpl_only = false,
.ret_type = RET_INTEGER,
.arg1_type = ARG_PTR_TO_CTX,
- .arg2_type = ARG_ANYTHING,
+ .arg2_type = ARG_SCALAR,
.arg3_type = ARG_PTR_TO_UNINIT_MEM,
.arg4_type = ARG_MEM_SIZE,
};
@@ -11820,10 +11820,10 @@ static const struct bpf_func_proto sk_reuseport_load_bytes_relative_proto = {
.gpl_only = false,
.ret_type = RET_INTEGER,
.arg1_type = ARG_PTR_TO_CTX,
- .arg2_type = ARG_ANYTHING,
+ .arg2_type = ARG_SCALAR,
.arg3_type = ARG_PTR_TO_UNINIT_MEM,
.arg4_type = ARG_MEM_SIZE,
- .arg5_type = ARG_ANYTHING,
+ .arg5_type = ARG_SCALAR,
};
static const struct bpf_func_proto *
@@ -12010,7 +12010,7 @@ static const struct bpf_func_proto bpf_sk_lookup_assign_proto = {
.ret_type = RET_INTEGER,
.arg1_type = ARG_PTR_TO_CTX,
.arg2_type = ARG_PTR_TO_SOCKET_OR_NULL,
- .arg3_type = ARG_ANYTHING,
+ .arg3_type = ARG_SCALAR,
};
static const struct bpf_func_proto *
diff --git a/net/core/sock_map.c b/net/core/sock_map.c
index ca49bc7f8687..03d95aac8b60 100644
--- a/net/core/sock_map.c
+++ b/net/core/sock_map.c
@@ -641,7 +641,7 @@ const struct bpf_func_proto bpf_sock_map_update_proto = {
.arg1_type = ARG_PTR_TO_CTX,
.arg2_type = ARG_CONST_MAP_PTR,
.arg3_type = ARG_PTR_TO_MAP_KEY,
- .arg4_type = ARG_ANYTHING,
+ .arg4_type = ARG_SCALAR,
};
BPF_CALL_4(bpf_sk_redirect_map, struct sk_buff *, skb,
@@ -668,8 +668,8 @@ const struct bpf_func_proto bpf_sk_redirect_map_proto = {
.ret_type = RET_INTEGER,
.arg1_type = ARG_PTR_TO_CTX,
.arg2_type = ARG_CONST_MAP_PTR,
- .arg3_type = ARG_ANYTHING,
- .arg4_type = ARG_ANYTHING,
+ .arg3_type = ARG_SCALAR,
+ .arg4_type = ARG_SCALAR,
};
BPF_CALL_4(bpf_msg_redirect_map, struct sk_msg *, msg,
@@ -699,8 +699,8 @@ const struct bpf_func_proto bpf_msg_redirect_map_proto = {
.ret_type = RET_INTEGER,
.arg1_type = ARG_PTR_TO_CTX,
.arg2_type = ARG_CONST_MAP_PTR,
- .arg3_type = ARG_ANYTHING,
- .arg4_type = ARG_ANYTHING,
+ .arg3_type = ARG_SCALAR,
+ .arg4_type = ARG_SCALAR,
};
struct sock_map_seq_info {
@@ -1247,7 +1247,7 @@ const struct bpf_func_proto bpf_sock_hash_update_proto = {
.arg1_type = ARG_PTR_TO_CTX,
.arg2_type = ARG_CONST_MAP_PTR,
.arg3_type = ARG_PTR_TO_MAP_KEY,
- .arg4_type = ARG_ANYTHING,
+ .arg4_type = ARG_SCALAR,
};
BPF_CALL_4(bpf_sk_redirect_hash, struct sk_buff *, skb,
@@ -1275,7 +1275,7 @@ const struct bpf_func_proto bpf_sk_redirect_hash_proto = {
.arg1_type = ARG_PTR_TO_CTX,
.arg2_type = ARG_CONST_MAP_PTR,
.arg3_type = ARG_PTR_TO_MAP_KEY,
- .arg4_type = ARG_ANYTHING,
+ .arg4_type = ARG_SCALAR,
};
BPF_CALL_4(bpf_msg_redirect_hash, struct sk_msg *, msg,
@@ -1306,7 +1306,7 @@ const struct bpf_func_proto bpf_msg_redirect_hash_proto = {
.arg1_type = ARG_PTR_TO_CTX,
.arg2_type = ARG_CONST_MAP_PTR,
.arg3_type = ARG_PTR_TO_MAP_KEY,
- .arg4_type = ARG_ANYTHING,
+ .arg4_type = ARG_SCALAR,
};
struct sock_hash_seq_info {
diff --git a/net/ipv4/bpf_tcp_ca.c b/net/ipv4/bpf_tcp_ca.c
index 791e15063237..3684c0434da1 100644
--- a/net/ipv4/bpf_tcp_ca.c
+++ b/net/ipv4/bpf_tcp_ca.c
@@ -132,7 +132,7 @@ static const struct bpf_func_proto bpf_tcp_send_ack_proto = {
.ret_type = RET_INTEGER,
.arg1_type = ARG_PTR_TO_BTF_ID,
.arg1_btf_id = &tcp_sock_id,
- .arg2_type = ARG_ANYTHING,
+ .arg2_type = ARG_SCALAR,
};
static u32 prog_ops_moff(const struct bpf_prog *prog)
--
2.52.0
^ permalink raw reply related [flat|nested] 54+ messages in thread
* [PATCH bpf-next v1 06/22] bpf: Unify kfunc argument kinds with enum bpf_arg_type
2026-09-05 22:00 [PATCH bpf-next v1 00/22] bpf: Unify helper and kfunc argument checks Amery Hung
` (4 preceding siblings ...)
2026-09-05 22:01 ` [PATCH bpf-next v1 05/22] bpf: Rename ambiguous function argument types Amery Hung
@ 2026-09-05 22:01 ` Amery Hung
2026-09-05 23:08 ` bot+bpf-ci
2026-09-05 22:01 ` [PATCH bpf-next v1 07/22] bpf: Align helper and kfunc ARG_PTR_TO_PROG_AUX handling Amery Hung
` (16 subsequent siblings)
22 siblings, 1 reply; 54+ messages in thread
From: Amery Hung @ 2026-09-05 22:01 UTC (permalink / raw)
To: bpf
Cc: alexei.starovoitov, andrii, daniel, eddyz87, memxor, ameryhung,
kernel-team
check_kfunc_args() classifies arguments with enum kfunc_ptr_arg_type
while check_func_arg() uses enum bpf_arg_type, yet both classifications
are stored in bpf_func_proto::arg_type. The overlapping namespaces force
the kfunc path to translate argument kinds before calling shared checks.
Fold the kfunc kinds into enum bpf_arg_type. Reuse ARG_SCALAR and
the existing pointer and memory kinds where their semantics match,
map kfunc callbacks to ARG_PTR_TO_FUNC, and add enumerators for the
remaining kfunc-only kinds. check_kfunc_args() can then carry one
classification throughout verification.
Preserving the original argument kind also lets
check_func_arg_reg_off() derive the zero-offset requirement for
ARG_PTR_TO_REFCOUNTED_KPTR directly. Remove its separate
btf_id_fixed_off_ok parameter and wrapper, along with the now-empty
translation switch in check_kfunc_args().
No functional change.
Signed-off-by: Amery Hung <ameryhung@gmail.com>
---
include/linux/bpf.h | 14 +++
kernel/bpf/verifier.c | 221 ++++++++++++++----------------------------
2 files changed, 87 insertions(+), 148 deletions(-)
diff --git a/include/linux/bpf.h b/include/linux/bpf.h
index 1574fe2d8cc0..f620920ea575 100644
--- a/include/linux/bpf.h
+++ b/include/linux/bpf.h
@@ -908,6 +908,20 @@ enum bpf_arg_type {
ARG_PTR_TO_TIMER, /* pointer to bpf_timer */
ARG_KPTR_XCHG_DEST, /* pointer to destination that kptrs are bpf_kptr_xchg'd into */
ARG_PTR_TO_DYNPTR, /* pointer to bpf_dynptr. See bpf_type_flag for dynptr type */
+
+ ARG_CONST_SCALAR, /* scalar known at verification time */
+ ARG_CONST_MEM_SIZE, /* ARG_MEM_SIZE that must be constant */
+ ARG_PTR_TO_ALLOC_BTF_ID, /* pointer to an allocated object */
+ ARG_PTR_TO_REFCOUNTED_KPTR, /* pointer to a refcounted local kptr */
+ ARG_PTR_TO_ITER, /* pointer to an iterator */
+ ARG_PTR_TO_LIST_HEAD, /* pointer to bpf_list_head */
+ ARG_PTR_TO_LIST_NODE, /* pointer to bpf_list_node */
+ ARG_PTR_TO_RB_ROOT, /* pointer to bpf_rb_root */
+ ARG_PTR_TO_RB_NODE, /* pointer to bpf_rb_node */
+ ARG_PTR_TO_WORKQUEUE, /* pointer to bpf_wq */
+ ARG_PTR_TO_TASK_WORK, /* pointer to bpf_task_work */
+ ARG_PTR_TO_IRQ_FLAG, /* pointer to saved IRQ flags on the stack */
+ ARG_PTR_TO_RES_SPIN_LOCK, /* pointer to bpf_res_spin_lock */
__BPF_ARG_TYPE_MAX,
/* Extended arg_types. */
diff --git a/kernel/bpf/verifier.c b/kernel/bpf/verifier.c
index d444e72fdd97..a0a74a5e23be 100644
--- a/kernel/bpf/verifier.c
+++ b/kernel/bpf/verifier.c
@@ -8533,10 +8533,9 @@ 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,
- bool btf_id_fixed_off_ok)
+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)
{
u32 type = reg->type;
@@ -8592,12 +8591,15 @@ 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 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.
+ * its fixed offset must be 0. bpf_refcount_acquire() returns the
+ * pointer it was given while incrementing the refcount at the
+ * refcount field offset, so it needs a zero offset too. 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, btf_id_fixed_off_ok);
+ return __check_ptr_off_reg(env, reg, argno,
+ base_type(arg_type) != ARG_PTR_TO_REFCOUNTED_KPTR);
case PTR_TO_CTX:
/*
* Allow fixed and variable offsets for syscall context, but
@@ -8613,13 +8615,6 @@ 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)
{
@@ -11793,34 +11788,6 @@ static void btf_member_path_str(const struct btf *btf, const struct btf_member_p
}
}
-enum kfunc_ptr_arg_type {
- KF_ARG_CONST_MEM_SIZE,
- KF_ARG_MEM_SIZE,
- KF_ARG_CONST,
- KF_ARG_CONST_ALLOC_SIZE_OR_ZERO,
- KF_ARG_ANYTHING,
- KF_ARG_PTR_TO_CTX,
- KF_ARG_PTR_TO_ALLOC_BTF_ID, /* Allocated object */
- KF_ARG_PTR_TO_REFCOUNTED_KPTR, /* Refcounted local kptr */
- KF_ARG_PTR_TO_DYNPTR,
- KF_ARG_PTR_TO_ITER,
- KF_ARG_PTR_TO_LIST_HEAD,
- KF_ARG_PTR_TO_LIST_NODE,
- KF_ARG_PTR_TO_BTF_ID, /* Also covers reg2btf_ids conversions */
- KF_ARG_PTR_TO_MEM,
- KF_ARG_PTR_TO_CALLBACK,
- KF_ARG_PTR_TO_RB_ROOT,
- KF_ARG_PTR_TO_RB_NODE,
- KF_ARG_PTR_TO_CONST_STR,
- KF_ARG_CONST_MAP_PTR,
- KF_ARG_PTR_TO_TIMER,
- KF_ARG_PTR_TO_WORKQUEUE,
- KF_ARG_PTR_TO_IRQ_FLAG,
- KF_ARG_PTR_TO_RES_SPIN_LOCK,
- KF_ARG_PTR_TO_TASK_WORK,
- KF_ARG_PTR_TO_ARENA,
-};
-
enum special_kfunc_type {
KF_bpf_obj_new_impl,
KF_bpf_obj_new,
@@ -12082,15 +12049,15 @@ get_kfunc_arg_type(struct bpf_verifier_env *env, struct bpf_call_arg_meta *meta,
/* Scalar arguments are classified from their BTF suffix/name alone. */
if (btf_type_is_scalar(t)) {
if (is_kfunc_arg_constant(meta->btf, &args[arg]))
- return KF_ARG_CONST;
+ return ARG_CONST_SCALAR;
if (is_kfunc_arg_const_mem_size(meta->btf, &args[arg]))
- return KF_ARG_CONST_MEM_SIZE;
+ return ARG_CONST_MEM_SIZE;
if (is_kfunc_arg_mem_size(meta->btf, &args[arg]))
- return KF_ARG_MEM_SIZE;
+ return ARG_MEM_SIZE;
if (is_kfunc_arg_scalar_with_name(meta->btf, &args[arg], "rdonly_buf_size") ||
is_kfunc_arg_scalar_with_name(meta->btf, &args[arg], "rdwr_buf_size"))
- return KF_ARG_CONST_ALLOC_SIZE_OR_ZERO;
- return KF_ARG_ANYTHING;
+ return ARG_CONST_ALLOC_SIZE_OR_ZERO;
+ return ARG_SCALAR;
}
if (!btf_type_is_ptr(t)) {
@@ -12104,48 +12071,48 @@ get_kfunc_arg_type(struct bpf_verifier_env *env, struct bpf_call_arg_meta *meta,
/* 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.
+ * arguments, we resolve it to a known bpf_arg_type.
*/
if (is_kfunc_call(meta, special_kfunc_list[KF_bpf_cast_to_kern_ctx]) ||
is_kfunc_call(meta, special_kfunc_list[KF_bpf_session_is_return]) ||
is_kfunc_call(meta, special_kfunc_list[KF_bpf_session_cookie]))
- arg_type = KF_ARG_PTR_TO_CTX;
+ arg_type = 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;
+ arg_type = ARG_PTR_TO_CTX;
else if (is_kfunc_arg_alloc_obj(meta->btf, &args[arg]))
- arg_type = KF_ARG_PTR_TO_ALLOC_BTF_ID;
+ arg_type = 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;
+ arg_type = ARG_PTR_TO_REFCOUNTED_KPTR;
else if (is_kfunc_arg_dynptr(meta->btf, &args[arg]))
- arg_type = KF_ARG_PTR_TO_DYNPTR;
+ arg_type = ARG_PTR_TO_DYNPTR;
else if (is_kfunc_arg_iter(meta, arg, &args[arg]))
- arg_type = KF_ARG_PTR_TO_ITER;
+ arg_type = ARG_PTR_TO_ITER;
else if (is_kfunc_arg_list_head(meta->btf, &args[arg]))
- arg_type = KF_ARG_PTR_TO_LIST_HEAD;
+ arg_type = ARG_PTR_TO_LIST_HEAD;
else if (is_kfunc_arg_list_node(meta->btf, &args[arg]))
- arg_type = KF_ARG_PTR_TO_LIST_NODE;
+ arg_type = ARG_PTR_TO_LIST_NODE;
else if (is_kfunc_arg_rbtree_root(meta->btf, &args[arg]))
- arg_type = KF_ARG_PTR_TO_RB_ROOT;
+ arg_type = ARG_PTR_TO_RB_ROOT;
else if (is_kfunc_arg_rbtree_node(meta->btf, &args[arg]))
- arg_type = KF_ARG_PTR_TO_RB_NODE;
+ arg_type = ARG_PTR_TO_RB_NODE;
else if (is_kfunc_arg_const_str(meta->btf, &args[arg]))
- arg_type = KF_ARG_PTR_TO_CONST_STR;
+ arg_type = ARG_PTR_TO_CONST_STR;
else if (is_kfunc_arg_const_map(meta->btf, &args[arg]))
- arg_type = KF_ARG_CONST_MAP_PTR;
+ arg_type = ARG_CONST_MAP_PTR;
else if (is_kfunc_arg_map(meta->btf, &args[arg]))
- arg_type = KF_ARG_PTR_TO_BTF_ID;
+ arg_type = ARG_PTR_TO_BTF_ID;
else if (is_kfunc_arg_wq(meta->btf, &args[arg]))
- arg_type = KF_ARG_PTR_TO_WORKQUEUE;
+ arg_type = ARG_PTR_TO_WORKQUEUE;
else if (is_kfunc_arg_timer(meta->btf, &args[arg]))
- arg_type = KF_ARG_PTR_TO_TIMER;
+ arg_type = ARG_PTR_TO_TIMER;
else if (is_kfunc_arg_task_work(meta->btf, &args[arg]))
- arg_type = KF_ARG_PTR_TO_TASK_WORK;
+ arg_type = ARG_PTR_TO_TASK_WORK;
else if (is_kfunc_arg_irq_flag(meta->btf, &args[arg]))
- arg_type = KF_ARG_PTR_TO_IRQ_FLAG;
+ arg_type = 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;
+ arg_type = ARG_PTR_TO_RES_SPIN_LOCK;
else if (is_kfunc_arg_callback(env, meta->btf, &args[arg]))
- arg_type = KF_ARG_PTR_TO_CALLBACK;
+ arg_type = ARG_PTR_TO_FUNC;
else if (is_kfunc_arg_arena(meta->btf, &args[arg])) {
if (!bpf_jit_supports_arena_args()) {
verbose(env, "JIT does not support kfunc %s() with arena pointer arguments\n",
@@ -12168,7 +12135,7 @@ get_kfunc_arg_type(struct bpf_verifier_env *env, struct bpf_call_arg_meta *meta,
* whether the JIT rebases it to the arena base or preserves NULL.
* The common nullable path below records that verifier property.
*/
- arg_type = KF_ARG_PTR_TO_ARENA;
+ arg_type = ARG_PTR_TO_ARENA;
} 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]))) {
@@ -12178,10 +12145,10 @@ get_kfunc_arg_type(struct bpf_verifier_env *env, struct bpf_call_arg_meta *meta,
reg_arg_name(env, argno), btf_type_str(ref_t), ref_tname);
return -EINVAL;
}
- arg_type = KF_ARG_PTR_TO_MEM;
+ arg_type = 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;
+ /* A pointer to a struct without a size argument is classified as ARG_PTR_TO_BTF_ID */
+ arg_type = ARG_PTR_TO_BTF_ID;
else {
/*
* Otherwise this is a fixed-size memory buffer supported by
@@ -12194,7 +12161,7 @@ get_kfunc_arg_type(struct bpf_verifier_env *env, struct bpf_call_arg_meta *meta,
reg_arg_name(env, argno), btf_type_str(ref_t), ref_tname);
return -EINVAL;
}
- arg_type = KF_ARG_PTR_TO_MEM | MEM_FIXED_SIZE;
+ arg_type = ARG_PTR_TO_MEM | MEM_FIXED_SIZE;
}
if (is_kfunc_arg_nullable(meta->btf, &args[arg]))
@@ -12826,13 +12793,11 @@ static int check_kfunc_args(struct bpf_verifier_env *env, struct bpf_call_arg_me
*/
for (i = 0; i < nargs; i++) {
struct bpf_reg_state *reg = get_func_arg_reg(caller, regs, i);
+ enum bpf_arg_type arg_type = meta->fn->arg_type[i];
const struct btf_type *t, *ref_t, *resolve_ret;
- enum bpf_arg_type arg_type = ARG_UNUSED;
argno_t argno = argno_from_arg(i + 1);
int regno = reg_from_argno(argno);
- bool btf_id_fixed_off_ok = true;
u32 ref_id = args[i].type, type_size;
- int kf_arg_type = meta->fn->arg_type[i];
if (is_kfunc_arg_prog_aux(btf, &args[i])) {
/* Reject repeated use bpf_prog_aux */
@@ -12862,7 +12827,7 @@ static int check_kfunc_args(struct bpf_verifier_env *env, struct bpf_call_arg_me
if (btf_type_is_ptr(t) &&
(bpf_register_is_null(reg) || type_may_be_null(reg->type)) &&
- !type_may_be_null(kf_arg_type)) {
+ !type_may_be_null(arg_type)) {
const char *expected_type;
expected_type = bpf_diag_fmt_btf_type(env, btf, args[i].type);
@@ -12892,7 +12857,7 @@ static int check_kfunc_args(struct bpf_verifier_env *env, struct bpf_call_arg_me
if (reg_is_referenced(env, reg))
update_ref_obj(&meta->ref_obj, reg);
- if (bpf_register_is_null(reg) && type_may_be_null(kf_arg_type))
+ if (bpf_register_is_null(reg) && type_may_be_null(arg_type))
continue;
if (is_kfunc_arg_map(btf, &args[i])) {
@@ -12901,54 +12866,14 @@ static int check_kfunc_args(struct bpf_verifier_env *env, struct bpf_call_arg_me
ref_tname = btf_name_by_offset(btf, ref_t->name_off);
}
- switch (base_type(kf_arg_type)) {
- case KF_ARG_CONST:
- case KF_ARG_CONST_MEM_SIZE:
- case KF_ARG_MEM_SIZE:
- case KF_ARG_ANYTHING:
- case KF_ARG_CONST_ALLOC_SIZE_OR_ZERO:
- case KF_ARG_PTR_TO_ALLOC_BTF_ID:
- case KF_ARG_PTR_TO_BTF_ID:
- case KF_ARG_CONST_MAP_PTR:
- case KF_ARG_PTR_TO_ITER:
- case KF_ARG_PTR_TO_LIST_HEAD:
- case KF_ARG_PTR_TO_LIST_NODE:
- case KF_ARG_PTR_TO_RB_ROOT:
- case KF_ARG_PTR_TO_RB_NODE:
- case KF_ARG_PTR_TO_MEM:
- case KF_ARG_PTR_TO_CALLBACK:
- case KF_ARG_PTR_TO_CONST_STR:
- case KF_ARG_PTR_TO_WORKQUEUE:
- case KF_ARG_PTR_TO_TIMER:
- case KF_ARG_PTR_TO_TASK_WORK:
- case KF_ARG_PTR_TO_IRQ_FLAG:
- case KF_ARG_PTR_TO_RES_SPIN_LOCK:
- case KF_ARG_PTR_TO_ARENA:
- break;
- case KF_ARG_PTR_TO_DYNPTR:
- arg_type = ARG_PTR_TO_DYNPTR;
- break;
- 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;
- }
-
if (regno == meta->release_regno)
arg_type |= OBJ_RELEASE;
- ret = __check_func_arg_reg_off(env, reg, argno, arg_type,
- btf_id_fixed_off_ok);
+ ret = check_func_arg_reg_off(env, reg, argno, arg_type);
if (ret < 0)
return ret;
- switch (base_type(kf_arg_type)) {
- case KF_ARG_CONST:
+ switch (base_type(arg_type)) {
+ case ARG_CONST_SCALAR:
if (reg->type != SCALAR_VALUE) {
verbose(env, "%s is not a scalar\n", reg_arg_name(env, argno));
bpf_diag_call_arg_fmt(env, insn_idx, argno, func_name,
@@ -12969,7 +12894,7 @@ static int check_kfunc_args(struct bpf_verifier_env *env, struct bpf_call_arg_me
return ret;
}
break;
- case KF_ARG_ANYTHING:
+ case ARG_SCALAR:
if (reg->type != SCALAR_VALUE) {
verbose(env, "%s is not a scalar\n", reg_arg_name(env, argno));
bpf_diag_call_arg_fmt(env, insn_idx, argno, func_name,
@@ -12980,7 +12905,7 @@ static int check_kfunc_args(struct bpf_verifier_env *env, struct bpf_call_arg_me
return -EINVAL;
}
break;
- case KF_ARG_CONST_ALLOC_SIZE_OR_ZERO:
+ case ARG_CONST_ALLOC_SIZE_OR_ZERO:
if (reg->type != SCALAR_VALUE) {
verbose(env, "%s is not a scalar\n", reg_arg_name(env, argno));
bpf_diag_call_arg_fmt(env, insn_idx, argno, func_name,
@@ -13003,7 +12928,7 @@ static int check_kfunc_args(struct bpf_verifier_env *env, struct bpf_call_arg_me
return ret;
}
break;
- case KF_ARG_PTR_TO_CTX:
+ case ARG_PTR_TO_CTX:
if (reg->type != PTR_TO_CTX) {
verbose(env, "%s expected pointer to ctx, but got %s\n",
reg_arg_name(env, argno), reg_type_str(env, reg->type));
@@ -13022,14 +12947,14 @@ static int check_kfunc_args(struct bpf_verifier_env *env, struct bpf_call_arg_me
meta->ret_btf_id = ret;
}
break;
- case KF_ARG_PTR_TO_ARENA:
+ case ARG_PTR_TO_ARENA:
if (reg->type != PTR_TO_ARENA && reg->type != SCALAR_VALUE) {
verbose(env, "%s is not a pointer to arena or scalar\n",
reg_arg_name(env, argno));
return -EINVAL;
}
break;
- case KF_ARG_PTR_TO_ALLOC_BTF_ID:
+ case ARG_PTR_TO_ALLOC_BTF_ID:
if (reg->type == (PTR_TO_BTF_ID | MEM_ALLOC)) {
if (!is_bpf_obj_drop_kfunc(meta->func_id)) {
verbose(env, "%s expected for bpf_obj_drop()\n",
@@ -13065,7 +12990,7 @@ static int check_kfunc_args(struct bpf_verifier_env *env, struct bpf_call_arg_me
meta->arg_btf_id = reg->btf_id;
}
break;
- case KF_ARG_PTR_TO_DYNPTR:
+ case ARG_PTR_TO_DYNPTR:
{
enum bpf_arg_type dynptr_arg_type = ARG_PTR_TO_DYNPTR;
@@ -13100,7 +13025,7 @@ static int check_kfunc_args(struct bpf_verifier_env *env, struct bpf_call_arg_me
return ret;
break;
}
- case KF_ARG_PTR_TO_ITER:
+ case ARG_PTR_TO_ITER:
if (is_kfunc_call(meta, special_kfunc_list[KF_bpf_iter_css_task_new])) {
if (!check_css_task_iter_allowlist(env)) {
verbose(env, "css_task_iter is only allowed in bpf_lsm, bpf_iter and sleepable progs\n");
@@ -13111,7 +13036,7 @@ static int check_kfunc_args(struct bpf_verifier_env *env, struct bpf_call_arg_me
if (ret < 0)
return ret;
break;
- case KF_ARG_PTR_TO_LIST_HEAD:
+ case ARG_PTR_TO_LIST_HEAD:
if (reg->type != PTR_TO_MAP_VALUE &&
reg->type != (PTR_TO_BTF_ID | MEM_ALLOC)) {
verbose(env, "%s expected pointer to map value or allocated object\n",
@@ -13127,7 +13052,7 @@ static int check_kfunc_args(struct bpf_verifier_env *env, struct bpf_call_arg_me
if (ret < 0)
return ret;
break;
- case KF_ARG_PTR_TO_RB_ROOT:
+ case ARG_PTR_TO_RB_ROOT:
if (reg->type != PTR_TO_MAP_VALUE &&
reg->type != (PTR_TO_BTF_ID | MEM_ALLOC)) {
verbose(env, "%s expected pointer to map value or allocated object\n",
@@ -13143,7 +13068,7 @@ static int check_kfunc_args(struct bpf_verifier_env *env, struct bpf_call_arg_me
if (ret < 0)
return ret;
break;
- case KF_ARG_PTR_TO_LIST_NODE:
+ case ARG_PTR_TO_LIST_NODE:
if (is_kfunc_arg_nonown_allowed(btf, &args[i]) &&
type_is_non_owning_ref(reg->type) && !reg_is_referenced(env, reg)) {
/* Allow bpf_list_front/back return value for
@@ -13165,7 +13090,7 @@ static int check_kfunc_args(struct bpf_verifier_env *env, struct bpf_call_arg_me
if (ret < 0)
return ret;
break;
- case KF_ARG_PTR_TO_RB_NODE:
+ case ARG_PTR_TO_RB_NODE:
if (is_bpf_rbtree_add_kfunc(meta->func_id)) {
if (reg->type != (PTR_TO_BTF_ID | MEM_ALLOC)) {
verbose(env, "%s expected pointer to allocated object\n",
@@ -13192,7 +13117,7 @@ static int check_kfunc_args(struct bpf_verifier_env *env, struct bpf_call_arg_me
if (ret < 0)
return ret;
break;
- case KF_ARG_CONST_MAP_PTR:
+ case ARG_CONST_MAP_PTR:
if (base_type(reg->type) != CONST_PTR_TO_MAP ||
type_may_be_null(reg->type)) {
verbose(env, "pointer in %s isn't map pointer\n",
@@ -13203,7 +13128,7 @@ static int check_kfunc_args(struct bpf_verifier_env *env, struct bpf_call_arg_me
if (ret < 0)
return ret;
break;
- case KF_ARG_PTR_TO_BTF_ID:
+ case ARG_PTR_TO_BTF_ID:
/* Only base_type is checked, further checks are done here */
if (base_type(reg->type) == PTR_TO_BTF_ID ||
reg2btf_ids[base_type(reg->type)]) {
@@ -13268,10 +13193,10 @@ static int check_kfunc_args(struct bpf_verifier_env *env, struct bpf_call_arg_me
* If the register does not contain btf id but the argument type is a pointer to
* scalar-only struct, allow verifying it as a fixed size memory.
*/
- kf_arg_type = KF_ARG_PTR_TO_MEM | MEM_FIXED_SIZE;
+ arg_type = ARG_PTR_TO_MEM | MEM_FIXED_SIZE;
fallthrough;
- case KF_ARG_PTR_TO_MEM:
- if (kf_arg_type & MEM_FIXED_SIZE) {
+ case ARG_PTR_TO_MEM:
+ if (arg_type & MEM_FIXED_SIZE) {
bool known_memory;
resolve_ret = btf_resolve_size(btf, ref_t, &type_size);
@@ -13305,7 +13230,7 @@ static int check_kfunc_args(struct bpf_verifier_env *env, struct bpf_call_arg_me
}
}
break;
- case KF_ARG_CONST_MEM_SIZE:
+ case ARG_CONST_MEM_SIZE:
ret = process_const_arg(env, reg, argno, meta);
if (ret < 0) {
if (ret == -EINVAL)
@@ -13316,7 +13241,7 @@ static int check_kfunc_args(struct bpf_verifier_env *env, struct bpf_call_arg_me
return ret;
}
fallthrough;
- case KF_ARG_MEM_SIZE:
+ case ARG_MEM_SIZE:
{
struct bpf_reg_state *buff_reg = get_func_arg_reg(caller, regs, i - 1);
struct bpf_reg_state *size_reg = reg;
@@ -13369,14 +13294,14 @@ static int check_kfunc_args(struct bpf_verifier_env *env, struct bpf_call_arg_me
}
break;
}
- case KF_ARG_PTR_TO_CALLBACK:
+ case ARG_PTR_TO_FUNC:
if (reg->type != PTR_TO_FUNC) {
verbose(env, "%s expected pointer to func\n", reg_arg_name(env, argno));
return -EINVAL;
}
meta->subprogno = reg->subprogno;
break;
- case KF_ARG_PTR_TO_REFCOUNTED_KPTR:
+ case ARG_PTR_TO_REFCOUNTED_KPTR:
if (!type_is_ptr_alloc_obj(reg->type)) {
verbose(env, "%s is neither owning or non-owning ref\n",
reg_arg_name(env, argno));
@@ -13405,7 +13330,7 @@ static int check_kfunc_args(struct bpf_verifier_env *env, struct bpf_call_arg_me
meta->arg_btf = reg->btf;
meta->arg_btf_id = reg->btf_id;
break;
- case KF_ARG_PTR_TO_CONST_STR:
+ case ARG_PTR_TO_CONST_STR:
if (reg->type != PTR_TO_MAP_VALUE) {
verbose(env, "%s doesn't point to a const string\n",
reg_arg_name(env, argno));
@@ -13420,7 +13345,7 @@ static int check_kfunc_args(struct bpf_verifier_env *env, struct bpf_call_arg_me
if (ret)
return ret;
break;
- case KF_ARG_PTR_TO_WORKQUEUE:
+ case ARG_PTR_TO_WORKQUEUE:
if (reg->type != PTR_TO_MAP_VALUE) {
verbose(env, "%s doesn't point to a map value\n",
reg_arg_name(env, argno));
@@ -13430,7 +13355,7 @@ static int check_kfunc_args(struct bpf_verifier_env *env, struct bpf_call_arg_me
if (ret < 0)
return ret;
break;
- case KF_ARG_PTR_TO_TIMER:
+ case ARG_PTR_TO_TIMER:
if (reg->type != PTR_TO_MAP_VALUE) {
verbose(env, "%s doesn't point to a map value\n",
reg_arg_name(env, argno));
@@ -13440,7 +13365,7 @@ static int check_kfunc_args(struct bpf_verifier_env *env, struct bpf_call_arg_me
if (ret < 0)
return ret;
break;
- case KF_ARG_PTR_TO_TASK_WORK:
+ case ARG_PTR_TO_TASK_WORK:
if (reg->type != PTR_TO_MAP_VALUE) {
verbose(env, "%s doesn't point to a map value\n",
reg_arg_name(env, argno));
@@ -13450,7 +13375,7 @@ static int check_kfunc_args(struct bpf_verifier_env *env, struct bpf_call_arg_me
if (ret < 0)
return ret;
break;
- case KF_ARG_PTR_TO_IRQ_FLAG:
+ case ARG_PTR_TO_IRQ_FLAG:
if (reg->type != PTR_TO_STACK) {
verbose(env, "%s doesn't point to an irq flag on stack\n",
reg_arg_name(env, argno));
@@ -13465,7 +13390,7 @@ static int check_kfunc_args(struct bpf_verifier_env *env, struct bpf_call_arg_me
if (ret < 0)
return ret;
break;
- case KF_ARG_PTR_TO_RES_SPIN_LOCK:
+ case ARG_PTR_TO_RES_SPIN_LOCK:
{
int flags = PROCESS_RES_LOCK;
--
2.52.0
^ permalink raw reply related [flat|nested] 54+ messages in thread
* [PATCH bpf-next v1 07/22] bpf: Align helper and kfunc ARG_PTR_TO_PROG_AUX handling
2026-09-05 22:00 [PATCH bpf-next v1 00/22] bpf: Unify helper and kfunc argument checks Amery Hung
` (5 preceding siblings ...)
2026-09-05 22:01 ` [PATCH bpf-next v1 06/22] bpf: Unify kfunc argument kinds with enum bpf_arg_type Amery Hung
@ 2026-09-05 22:01 ` Amery Hung
2026-09-05 23:08 ` bot+bpf-ci
2026-09-05 22:01 ` [PATCH bpf-next v1 08/22] bpf: Classify kfunc arguments the verifier ignores Amery Hung
` (15 subsequent siblings)
22 siblings, 1 reply; 54+ messages in thread
From: Amery Hung @ 2026-09-05 22:01 UTC (permalink / raw)
To: bpf
Cc: alexei.starovoitov, andrii, daniel, eddyz87, memxor, ameryhung,
kernel-team
The verifier supplies a bpf_prog_aux argument to both
bpf_timer_set_callback() and kfuncs rather than reading it from the BPF
program. The helper prototype leaves its third argument unused, while
the kfunc path identifies the argument from BTF at every call.
Add ARG_PTR_TO_PROG_AUX, record it in the helper prototype, and classify
the kfunc argument when its prototype is generated. Validate in one
place that a prototype contains at most one such argument and that it
is register-passed, as required by the BPF_LD_IMM64 fixup.
Record the argument register in the per-instruction metadata for both
call kinds. Drive the helper fixup from that metadata instead of the
helper ID and its hard-coded R3, matching the existing kfunc fixup.
bpf_call_arg_meta::arg_prog is then no longer needed.
No functional change beyond reporting invalid kfunc signatures when
the call is added rather than when it is verified.
Signed-off-by: Amery Hung <ameryhung@gmail.com>
---
include/linux/bpf.h | 1 +
include/linux/bpf_verifier.h | 1 -
kernel/bpf/fixups.c | 5 +--
kernel/bpf/helpers.c | 1 +
kernel/bpf/verifier.c | 67 ++++++++++++++++++++++++++----------
5 files changed, 53 insertions(+), 22 deletions(-)
diff --git a/include/linux/bpf.h b/include/linux/bpf.h
index f620920ea575..547703f54a89 100644
--- a/include/linux/bpf.h
+++ b/include/linux/bpf.h
@@ -922,6 +922,7 @@ enum bpf_arg_type {
ARG_PTR_TO_TASK_WORK, /* pointer to bpf_task_work */
ARG_PTR_TO_IRQ_FLAG, /* pointer to saved IRQ flags on the stack */
ARG_PTR_TO_RES_SPIN_LOCK, /* pointer to bpf_res_spin_lock */
+ ARG_PTR_TO_PROG_AUX, /* pointer to the caller's bpf_prog_aux */
__BPF_ARG_TYPE_MAX,
/* Extended arg_types. */
diff --git a/include/linux/bpf_verifier.h b/include/linux/bpf_verifier.h
index 1a3c44ab06a1..e919e308f272 100644
--- a/include/linux/bpf_verifier.h
+++ b/include/linux/bpf_verifier.h
@@ -1585,7 +1585,6 @@ struct bpf_call_arg_meta {
struct btf *arg_btf;
u32 arg_btf_id;
bool arg_owning_ref;
- bool arg_prog;
struct {
struct btf_field *field;
diff --git a/kernel/bpf/fixups.c b/kernel/bpf/fixups.c
index 73fb3ffc18e3..9512f6497d32 100644
--- a/kernel/bpf/fixups.c
+++ b/kernel/bpf/fixups.c
@@ -2015,7 +2015,8 @@ int bpf_do_misc_fixups(struct bpf_verifier_env *env)
goto next_insn;
}
- if (insn->imm == BPF_FUNC_timer_set_callback) {
+ aux = &env->insn_aux_data[i + delta];
+ if (aux->arg_prog) {
/* The verifier will process callback_fn as many times as necessary
* with different maps and the register states prepared by
* set_timer_callback_state will be accurate.
@@ -2030,7 +2031,7 @@ int bpf_do_misc_fixups(struct bpf_verifier_env *env)
* bpf_timer_set_callback-ed will return -EINVAL.
*/
struct bpf_insn ld_addrs[2] = {
- BPF_LD_IMM64(BPF_REG_3, (long)prog->aux),
+ BPF_LD_IMM64(aux->arg_prog, (long)prog->aux),
};
insn_buf[0] = ld_addrs[0];
diff --git a/kernel/bpf/helpers.c b/kernel/bpf/helpers.c
index 72bfd8f93ae4..1b731aad54da 100644
--- a/kernel/bpf/helpers.c
+++ b/kernel/bpf/helpers.c
@@ -1510,6 +1510,7 @@ static const struct bpf_func_proto bpf_timer_set_callback_proto = {
.ret_type = RET_INTEGER,
.arg1_type = ARG_PTR_TO_TIMER,
.arg2_type = ARG_PTR_TO_FUNC,
+ .arg3_type = ARG_PTR_TO_PROG_AUX,
};
static bool defer_timer_wq_op(void)
diff --git a/kernel/bpf/verifier.c b/kernel/bpf/verifier.c
index a0a74a5e23be..22f5aff76b40 100644
--- a/kernel/bpf/verifier.c
+++ b/kernel/bpf/verifier.c
@@ -8737,6 +8737,7 @@ static int get_constant_map_key(struct bpf_verifier_env *env,
}
static bool can_elide_value_nullness(const struct bpf_map *map);
+static struct bpf_insn_aux_data *cur_aux(const struct bpf_verifier_env *env);
static int process_map_ptr_arg(struct bpf_verifier_env *env, struct bpf_reg_state *reg,
argno_t argno, struct bpf_call_arg_meta *meta)
@@ -8792,6 +8793,11 @@ static int check_func_arg(struct bpf_verifier_env *env, u32 arg,
u32 key_size;
int err = 0;
+ if (arg_type == ARG_PTR_TO_PROG_AUX) {
+ cur_aux(env)->arg_prog = regno;
+ return 0;
+ }
+
err = check_reg_arg(env, regno, SRC_OP);
if (err)
return err;
@@ -9400,9 +9406,42 @@ static bool check_proto_release_reg(const struct bpf_func_proto *fn, struct bpf_
return true;
}
-static int check_func_proto(const struct bpf_func_proto *fn, struct bpf_call_arg_meta *meta)
+static bool check_arg_prog_aux(struct bpf_verifier_env *env,
+ const struct bpf_func_proto *proto)
{
- return check_raw_mode_ok(fn, meta) &&
+ bool seen = false;
+ argno_t argno;
+ u32 i;
+
+ for (i = 0; i < ARRAY_SIZE(proto->arg_type); i++) {
+ if (proto->arg_type[i] == ARG_UNUSED)
+ break;
+ if (proto->arg_type[i] != ARG_PTR_TO_PROG_AUX)
+ continue;
+
+ if (seen) {
+ verifier_bug(env, "Only 1 prog->aux argument supported");
+ return false;
+ }
+
+ argno = argno_from_arg(i + 1);
+ if (reg_from_argno(argno) < 0) {
+ verbose(env, "%s prog->aux cannot be a stack argument\n",
+ reg_arg_name(env, argno));
+ return false;
+ }
+
+ seen = true;
+ }
+
+ return true;
+}
+
+static int check_func_proto(struct bpf_verifier_env *env, const struct bpf_func_proto *fn,
+ struct bpf_call_arg_meta *meta)
+{
+ return check_arg_prog_aux(env, fn) &&
+ check_raw_mode_ok(fn, meta) &&
check_arg_pair_ok(fn) &&
check_mem_arg_rw_flag_ok(fn) &&
check_proto_release_reg(fn, meta) &&
@@ -10916,7 +10955,7 @@ static int check_helper_call(struct bpf_verifier_env *env, struct bpf_insn *insn
memset(&meta, 0, sizeof(meta));
- err = check_func_proto(fn, &meta);
+ err = check_func_proto(env, fn, &meta);
if (err) {
verifier_bug(env, "incorrect func proto %s#%d", func_id_name(func_id), func_id);
return err;
@@ -12044,6 +12083,9 @@ get_kfunc_arg_type(struct bpf_verifier_env *env, struct bpf_call_arg_meta *meta,
const char *ref_tname = NULL;
int arg_type;
+ if (is_kfunc_arg_prog_aux(meta->btf, &args[arg]))
+ return ARG_PTR_TO_PROG_AUX;
+
t = btf_type_skip_modifiers(meta->btf, args[arg].type, NULL);
/* Scalar arguments are classified from their BTF suffix/name alone. */
@@ -12192,9 +12234,7 @@ static int gen_kfunc_arg_proto(struct bpf_verifier_env *env, struct bpf_call_arg
}
for (i = 0; i < nargs; i++) {
- if (is_kfunc_arg_prog_aux(btf, &args[i]) ||
- is_kfunc_arg_ignore(btf, &args[i]) ||
- is_kfunc_arg_implicit(meta, i))
+ if (is_kfunc_arg_ignore(btf, &args[i]) || is_kfunc_arg_implicit(meta, i))
continue;
arg_type = get_kfunc_arg_type(env, meta, args, i, nargs);
@@ -12204,7 +12244,7 @@ static int gen_kfunc_arg_proto(struct bpf_verifier_env *env, struct bpf_call_arg
proto->arg_type[i] = arg_type;
}
- return 0;
+ return check_arg_prog_aux(env, proto) ? 0 : -EINVAL;
}
static int process_kf_arg_ptr_to_btf_id(struct bpf_verifier_env *env,
@@ -12799,18 +12839,7 @@ static int check_kfunc_args(struct bpf_verifier_env *env, struct bpf_call_arg_me
int regno = reg_from_argno(argno);
u32 ref_id = args[i].type, type_size;
- if (is_kfunc_arg_prog_aux(btf, &args[i])) {
- /* Reject repeated use bpf_prog_aux */
- if (meta->arg_prog) {
- verifier_bug(env, "Only 1 prog->aux argument supported per-kfunc");
- return -EFAULT;
- }
- if (regno < 0) {
- verbose(env, "%s prog->aux cannot be a stack argument\n",
- reg_arg_name(env, argno));
- return -EINVAL;
- }
- meta->arg_prog = true;
+ if (arg_type == ARG_PTR_TO_PROG_AUX) {
cur_aux(env)->arg_prog = regno;
continue;
}
--
2.52.0
^ permalink raw reply related [flat|nested] 54+ messages in thread
* [PATCH bpf-next v1 08/22] bpf: Classify kfunc arguments the verifier ignores
2026-09-05 22:00 [PATCH bpf-next v1 00/22] bpf: Unify helper and kfunc argument checks Amery Hung
` (6 preceding siblings ...)
2026-09-05 22:01 ` [PATCH bpf-next v1 07/22] bpf: Align helper and kfunc ARG_PTR_TO_PROG_AUX handling Amery Hung
@ 2026-09-05 22:01 ` Amery Hung
2026-09-05 22:44 ` bot+bpf-ci
2026-09-05 22:01 ` [PATCH bpf-next v1 09/22] bpf: Set OBJ_RELEASE when generating kfunc argument types Amery Hung
` (14 subsequent siblings)
22 siblings, 1 reply; 54+ messages in thread
From: Amery Hung @ 2026-09-05 22:01 UTC (permalink / raw)
To: bpf
Cc: alexei.starovoitov, andrii, daniel, eddyz87, memxor, ameryhung,
kernel-team
The verifier does not inspect __ign arguments or the implicit
arguments of a KF_IMPLICIT_ARGS kfunc. get_kfunc_arg_type() leaves
them unclassified, so gen_kfunc_arg_proto() skips them and
check_kfunc_args() repeats the same BTF predicates for every call.
Add ARG_IGNORE and classify both cases in get_kfunc_arg_type(). The
generated prototype can then record every argument, and the call site
can consume the recorded type without deriving the classification
again.
No functional change.
Signed-off-by: Amery Hung <ameryhung@gmail.com>
---
include/linux/bpf.h | 1 +
kernel/bpf/verifier.c | 11 ++++++-----
2 files changed, 7 insertions(+), 5 deletions(-)
diff --git a/include/linux/bpf.h b/include/linux/bpf.h
index 547703f54a89..1f78746e0601 100644
--- a/include/linux/bpf.h
+++ b/include/linux/bpf.h
@@ -923,6 +923,7 @@ enum bpf_arg_type {
ARG_PTR_TO_IRQ_FLAG, /* pointer to saved IRQ flags on the stack */
ARG_PTR_TO_RES_SPIN_LOCK, /* pointer to bpf_res_spin_lock */
ARG_PTR_TO_PROG_AUX, /* pointer to the caller's bpf_prog_aux */
+ ARG_IGNORE, /* argument the verifier does not check at all */
__BPF_ARG_TYPE_MAX,
/* Extended arg_types. */
diff --git a/kernel/bpf/verifier.c b/kernel/bpf/verifier.c
index 22f5aff76b40..7416f1e16aa9 100644
--- a/kernel/bpf/verifier.c
+++ b/kernel/bpf/verifier.c
@@ -8797,6 +8797,8 @@ static int check_func_arg(struct bpf_verifier_env *env, u32 arg,
cur_aux(env)->arg_prog = regno;
return 0;
}
+ if (arg_type == ARG_IGNORE)
+ return 0;
err = check_reg_arg(env, regno, SRC_OP);
if (err)
@@ -12086,6 +12088,9 @@ get_kfunc_arg_type(struct bpf_verifier_env *env, struct bpf_call_arg_meta *meta,
if (is_kfunc_arg_prog_aux(meta->btf, &args[arg]))
return ARG_PTR_TO_PROG_AUX;
+ if (is_kfunc_arg_ignore(meta->btf, &args[arg]) || is_kfunc_arg_implicit(meta, arg))
+ return ARG_IGNORE;
+
t = btf_type_skip_modifiers(meta->btf, args[arg].type, NULL);
/* Scalar arguments are classified from their BTF suffix/name alone. */
@@ -12215,7 +12220,6 @@ get_kfunc_arg_type(struct bpf_verifier_env *env, struct bpf_call_arg_meta *meta,
static int gen_kfunc_arg_proto(struct bpf_verifier_env *env, struct bpf_call_arg_meta *meta,
struct bpf_func_proto *proto)
{
- const struct btf *btf = meta->btf;
const struct btf_param *args;
u32 i, nargs;
int arg_type;
@@ -12234,9 +12238,6 @@ static int gen_kfunc_arg_proto(struct bpf_verifier_env *env, struct bpf_call_arg
}
for (i = 0; i < nargs; i++) {
- if (is_kfunc_arg_ignore(btf, &args[i]) || is_kfunc_arg_implicit(meta, i))
- continue;
-
arg_type = get_kfunc_arg_type(env, meta, args, i, nargs);
if (arg_type < 0)
return arg_type;
@@ -12844,7 +12845,7 @@ static int check_kfunc_args(struct bpf_verifier_env *env, struct bpf_call_arg_me
continue;
}
- if (is_kfunc_arg_ignore(btf, &args[i]) || is_kfunc_arg_implicit(meta, i))
+ if (arg_type == ARG_IGNORE)
continue;
t = btf_type_skip_modifiers(btf, args[i].type, NULL);
--
2.52.0
^ permalink raw reply related [flat|nested] 54+ messages in thread
* [PATCH bpf-next v1 09/22] bpf: Set OBJ_RELEASE when generating kfunc argument types
2026-09-05 22:00 [PATCH bpf-next v1 00/22] bpf: Unify helper and kfunc argument checks Amery Hung
` (7 preceding siblings ...)
2026-09-05 22:01 ` [PATCH bpf-next v1 08/22] bpf: Classify kfunc arguments the verifier ignores Amery Hung
@ 2026-09-05 22:01 ` Amery Hung
2026-09-05 22:01 ` [PATCH bpf-next v1 10/22] bpf: Set MEM_UNINIT and dynptr subtypes when generating kfunc arg types Amery Hung
` (13 subsequent siblings)
22 siblings, 0 replies; 54+ messages in thread
From: Amery Hung @ 2026-09-05 22:01 UTC (permalink / raw)
To: bpf
Cc: alexei.starovoitov, andrii, daniel, eddyz87, memxor, ameryhung,
kernel-team
check_kfunc_args() ORs OBJ_RELEASE into arg_type on every verification
of every argument of every call, from a comparison against
meta->release_regno. Neither side of that comparison depends on the
call site. bpf_fetch_kfunc_arg_meta() sets release_regno to BPF_REG_1
when the kfunc is KF_RELEASE and leaves it zero otherwise, and regno
is derived from the argument index, so the test is true exactly for
argument 0 of a KF_RELEASE kfunc.
Set the flag in get_kfunc_arg_type() instead, where the rest
of the classification is built, and drop the comparison. The release
handling in check_func_proto() is helper-only, so an OBJ_RELEASE in a
generated kfunc prototype does not feed back into meta->release_regno.
meta->release_regno itself stays: it is still what tells the referenced
PTR_TO_BTF_ID check and release_reg() which register to act on.
No functional change.
Signed-off-by: Amery Hung <ameryhung@gmail.com>
---
kernel/bpf/verifier.c | 9 +++++++--
1 file changed, 7 insertions(+), 2 deletions(-)
diff --git a/kernel/bpf/verifier.c b/kernel/bpf/verifier.c
index 7416f1e16aa9..7662293ac7d2 100644
--- a/kernel/bpf/verifier.c
+++ b/kernel/bpf/verifier.c
@@ -12214,6 +12214,13 @@ get_kfunc_arg_type(struct bpf_verifier_env *env, struct bpf_call_arg_meta *meta,
if (is_kfunc_arg_nullable(meta->btf, &args[arg]))
arg_type |= PTR_MAYBE_NULL;
+ /*
+ * Only the first argument of a KF_RELEASE kfunc releases anything, and
+ * bpf_fetch_kfunc_arg_meta() only ever records BPF_REG_1 for it.
+ */
+ if (is_kfunc_release(meta) && arg == 0)
+ arg_type |= OBJ_RELEASE;
+
return arg_type;
}
@@ -12896,8 +12903,6 @@ static int check_kfunc_args(struct bpf_verifier_env *env, struct bpf_call_arg_me
ref_tname = btf_name_by_offset(btf, ref_t->name_off);
}
- if (regno == meta->release_regno)
- arg_type |= OBJ_RELEASE;
ret = check_func_arg_reg_off(env, reg, argno, arg_type);
if (ret < 0)
return ret;
--
2.52.0
^ permalink raw reply related [flat|nested] 54+ messages in thread
* [PATCH bpf-next v1 10/22] bpf: Set MEM_UNINIT and dynptr subtypes when generating kfunc arg types
2026-09-05 22:00 [PATCH bpf-next v1 00/22] bpf: Unify helper and kfunc argument checks Amery Hung
` (8 preceding siblings ...)
2026-09-05 22:01 ` [PATCH bpf-next v1 09/22] bpf: Set OBJ_RELEASE when generating kfunc argument types Amery Hung
@ 2026-09-05 22:01 ` Amery Hung
2026-09-05 22:01 ` [PATCH bpf-next v1 11/22] bpf: Set MEM_RCU when generating kfunc argument types Amery Hung
` (12 subsequent siblings)
22 siblings, 0 replies; 54+ messages in thread
From: Amery Hung @ 2026-09-05 22:01 UTC (permalink / raw)
To: bpf
Cc: alexei.starovoitov, andrii, daniel, eddyz87, memxor, ameryhung,
kernel-team
The dynptr flavour a kfunc argument expects is rebuilt on every
verification of the call: check_kfunc_args() starts from
ARG_PTR_TO_DYNPTR, adds MEM_UNINIT from the __uninit suffix, and then
walks a chain of func_id comparisons to add DYNPTR_TYPE_SKB, _XDP,
_SKB_META or _FILE. All of that is fixed by the kfunc identity and BTF,
so move it into get_kfunc_arg_type() and let the generated prototype
carry it.
Inheriting the classification also means PTR_MAYBE_NULL reaches
process_dynptr_func() for a __nullable dynptr argument, where it did not
before, and is_dynptr_type_expected() tested for any type of dynptr with
an exact arg_type == ARG_PTR_TO_DYNPTR comparison. Test the
DYNPTR_TYPE_FLAG_MASK bits instead, which is what the comment there
already claims to mean and does not care about unrelated flags.
What cannot move is the KF_bpf_dynptr_clone arm, which takes its type
from meta->dynptr.type, recorded while verifying the parent dynptr
argument earlier in the same call. That stays at the call site, applied
on top of the cached classification.
No functional change.
Signed-off-by: Amery Hung <ameryhung@gmail.com>
---
kernel/bpf/verifier.c | 47 ++++++++++++++++++++++++-------------------
1 file changed, 26 insertions(+), 21 deletions(-)
diff --git a/kernel/bpf/verifier.c b/kernel/bpf/verifier.c
index 7662293ac7d2..f43462a43ee9 100644
--- a/kernel/bpf/verifier.c
+++ b/kernel/bpf/verifier.c
@@ -951,8 +951,12 @@ static enum bpf_dynptr_type dynptr_reg_type(struct bpf_verifier_env *env, struct
static bool is_dynptr_type_expected(struct bpf_verifier_env *env, struct bpf_reg_state *reg,
enum bpf_arg_type arg_type)
{
- /* ARG_PTR_TO_DYNPTR takes any type of dynptr */
- if (arg_type == ARG_PTR_TO_DYNPTR)
+ /*
+ * ARG_PTR_TO_DYNPTR without a type flag takes any type of dynptr.
+ * Test the flags rather than the whole arg_type, which may carry
+ * unrelated ones such as PTR_MAYBE_NULL.
+ */
+ if (!(arg_type & DYNPTR_TYPE_FLAG_MASK))
return true;
return dynptr_reg_type(env, reg) == arg_to_dynptr_type(arg_type);
@@ -12130,9 +12134,20 @@ get_kfunc_arg_type(struct bpf_verifier_env *env, struct bpf_call_arg_meta *meta,
arg_type = ARG_PTR_TO_ALLOC_BTF_ID;
else if (is_kfunc_arg_refcounted_kptr(meta->btf, &args[arg]))
arg_type = ARG_PTR_TO_REFCOUNTED_KPTR;
- else if (is_kfunc_arg_dynptr(meta->btf, &args[arg]))
+ else if (is_kfunc_arg_dynptr(meta->btf, &args[arg])) {
arg_type = ARG_PTR_TO_DYNPTR;
- else if (is_kfunc_arg_iter(meta, arg, &args[arg]))
+
+ if (is_kfunc_call(meta, special_kfunc_list[KF_bpf_dynptr_from_skb]))
+ arg_type |= DYNPTR_TYPE_SKB;
+ else if (is_kfunc_call(meta, special_kfunc_list[KF_bpf_dynptr_from_xdp]))
+ arg_type |= DYNPTR_TYPE_XDP;
+ else if (is_kfunc_call(meta, special_kfunc_list[KF_bpf_dynptr_from_skb_meta]))
+ arg_type |= DYNPTR_TYPE_SKB_META;
+ else if (is_kfunc_call(meta, special_kfunc_list[KF_bpf_dynptr_from_file]) ||
+ is_kfunc_call(meta, special_kfunc_list[KF_bpf_dynptr_file_discard]))
+ /* OBJ_RELEASE for the latter comes from KF_RELEASE below */
+ arg_type |= DYNPTR_TYPE_FILE;
+ } else if (is_kfunc_arg_iter(meta, arg, &args[arg]))
arg_type = ARG_PTR_TO_ITER;
else if (is_kfunc_arg_list_head(meta->btf, &args[arg]))
arg_type = ARG_PTR_TO_LIST_HEAD;
@@ -12211,6 +12226,9 @@ get_kfunc_arg_type(struct bpf_verifier_env *env, struct bpf_call_arg_meta *meta,
arg_type = ARG_PTR_TO_MEM | MEM_FIXED_SIZE;
}
+ if (is_kfunc_arg_uninit(meta->btf, &args[arg]))
+ arg_type |= MEM_UNINIT;
+
if (is_kfunc_arg_nullable(meta->btf, &args[arg]))
arg_type |= PTR_MAYBE_NULL;
@@ -13027,23 +13045,10 @@ static int check_kfunc_args(struct bpf_verifier_env *env, struct bpf_call_arg_me
break;
case ARG_PTR_TO_DYNPTR:
{
- enum bpf_arg_type dynptr_arg_type = ARG_PTR_TO_DYNPTR;
-
- if (is_kfunc_arg_uninit(btf, &args[i]))
- dynptr_arg_type |= MEM_UNINIT;
-
- if (is_kfunc_call(meta, special_kfunc_list[KF_bpf_dynptr_from_skb])) {
- dynptr_arg_type |= DYNPTR_TYPE_SKB;
- } else if (is_kfunc_call(meta, special_kfunc_list[KF_bpf_dynptr_from_xdp])) {
- dynptr_arg_type |= DYNPTR_TYPE_XDP;
- } else if (is_kfunc_call(meta, special_kfunc_list[KF_bpf_dynptr_from_skb_meta])) {
- dynptr_arg_type |= DYNPTR_TYPE_SKB_META;
- } else if (is_kfunc_call(meta, special_kfunc_list[KF_bpf_dynptr_from_file])) {
- dynptr_arg_type |= DYNPTR_TYPE_FILE;
- } else if (is_kfunc_call(meta, special_kfunc_list[KF_bpf_dynptr_file_discard])) {
- dynptr_arg_type |= DYNPTR_TYPE_FILE | OBJ_RELEASE;
- } else if (is_kfunc_call(meta, special_kfunc_list[KF_bpf_dynptr_clone]) &&
- (dynptr_arg_type & MEM_UNINIT)) {
+ enum bpf_arg_type dynptr_arg_type = arg_type;
+
+ if (is_kfunc_call(meta, special_kfunc_list[KF_bpf_dynptr_clone]) &&
+ (dynptr_arg_type & MEM_UNINIT)) {
enum bpf_dynptr_type parent_type = meta->dynptr.type;
if (parent_type == BPF_DYNPTR_TYPE_INVALID) {
--
2.52.0
^ permalink raw reply related [flat|nested] 54+ messages in thread
* [PATCH bpf-next v1 11/22] bpf: Set MEM_RCU when generating kfunc argument types
2026-09-05 22:00 [PATCH bpf-next v1 00/22] bpf: Unify helper and kfunc argument checks Amery Hung
` (9 preceding siblings ...)
2026-09-05 22:01 ` [PATCH bpf-next v1 10/22] bpf: Set MEM_UNINIT and dynptr subtypes when generating kfunc arg types Amery Hung
@ 2026-09-05 22:01 ` Amery Hung
2026-09-05 22:44 ` bot+bpf-ci
2026-09-05 22:01 ` [PATCH bpf-next v1 12/22] bpf: Resolve BTF ID of ARG_PTR_TO_BTF_ID in kfunc bpf_func_proto Amery Hung
` (11 subsequent siblings)
22 siblings, 1 reply; 54+ messages in thread
From: Amery Hung @ 2026-09-05 22:01 UTC (permalink / raw)
To: bpf
Cc: alexei.starovoitov, andrii, daniel, eddyz87, memxor, ameryhung,
kernel-team
The BTF ID argument of a kfunc has to be referenced or trusted
unless the kfunc is KF_RCU, in which case an RCU-protected pointer is
accepted too. check_kfunc_args() reads that off meta->kfunc_flags on
every verification of the call, which is the last thing keeping the
argument policy of ARG_PTR_TO_BTF_ID out of the generated prototype.
Set MEM_RCU in get_kfunc_arg_type() for ARG_PTR_TO_BTF_ID arguments
of KF_RCU kfuncs, the way it already sets OBJ_RELEASE for release
arguments, and test the generated flag instead. Setting it on every
pointer argument would be equivalent here but would leak MEM_RCU into
arguments whose handling compares the whole arg_type, such as the
ARG_PTR_TO_CTX test in check_func_arg_reg_off().
No functional change intended.
Signed-off-by: Amery Hung <ameryhung@gmail.com>
---
kernel/bpf/verifier.c | 10 +++++++++-
1 file changed, 9 insertions(+), 1 deletion(-)
diff --git a/kernel/bpf/verifier.c b/kernel/bpf/verifier.c
index f43462a43ee9..c5b394e847e6 100644
--- a/kernel/bpf/verifier.c
+++ b/kernel/bpf/verifier.c
@@ -12239,6 +12239,14 @@ get_kfunc_arg_type(struct bpf_verifier_env *env, struct bpf_call_arg_meta *meta,
if (is_kfunc_release(meta) && arg == 0)
arg_type |= OBJ_RELEASE;
+ /*
+ * A KF_RCU kfunc accepts an RCU-protected pointer where it would
+ * otherwise demand a referenced or trusted one. Only ARG_PTR_TO_BTF_ID
+ * looks at where its register came from, so leave the other kinds alone.
+ */
+ if (base_type(arg_type) == ARG_PTR_TO_BTF_ID && is_kfunc_rcu(meta))
+ arg_type |= MEM_RCU;
+
return arg_type;
}
@@ -13174,7 +13182,7 @@ static int check_kfunc_args(struct bpf_verifier_env *env, struct bpf_call_arg_me
reg2btf_ids[base_type(reg->type)]) {
if (!is_trusted_reg(env, reg) ||
bpf_type_has_unsafe_modifiers(reg->type)) {
- if (!is_kfunc_rcu(meta)) {
+ if (!(arg_type & MEM_RCU)) {
const char *expected_type;
expected_type = bpf_diag_fmt_btf_type(env, btf, ref_id);
--
2.52.0
^ permalink raw reply related [flat|nested] 54+ messages in thread
* [PATCH bpf-next v1 12/22] bpf: Resolve BTF ID of ARG_PTR_TO_BTF_ID in kfunc bpf_func_proto
2026-09-05 22:00 [PATCH bpf-next v1 00/22] bpf: Unify helper and kfunc argument checks Amery Hung
` (10 preceding siblings ...)
2026-09-05 22:01 ` [PATCH bpf-next v1 11/22] bpf: Set MEM_RCU when generating kfunc argument types Amery Hung
@ 2026-09-05 22:01 ` Amery Hung
2026-09-05 23:08 ` bot+bpf-ci
2026-09-05 22:01 ` [PATCH bpf-next v1 13/22] bpf: Resolve ARG_PTR_TO_MEM | MEM_FIXED_SIZE size " Amery Hung
` (10 subsequent siblings)
22 siblings, 1 reply; 54+ messages in thread
From: Amery Hung @ 2026-09-05 22:01 UTC (permalink / raw)
To: bpf
Cc: alexei.starovoitov, andrii, daniel, eddyz87, memxor, ameryhung,
kernel-team
check_kfunc_args() walks the kfunc's BTF on every verification of a call
to work out which BTF ID an ARG_PTR_TO_BTF_ID argument expects. Helpers
name theirs in bpf_func_proto::arg_btf_id[], as a pointer to a BTF ID
that resolve_btfids fills in at build time.
The ID of a kfunc argument's referent is already stored in the
immutable BTF records that describe its pointer and modifier chain. Make
arg_btf_id[] point to the BTF field containing the resolved ID. A __map
argument instead uses the existing vmlinux BTF ID pointer. Produce this
metadata alongside the argument classification in get_kfunc_arg_type().
Declare the argument BTF ID pointers const, since the verifier only
reads through them. The BTF object owns their storage and remains
alive while the generated prototype is used, so the pointers remain
valid when the kfunc descriptor array is reallocated or sorted.
Both helper and kfunc callers can now read the expected BTF ID through
the same bpf_func_proto field, which lets check_func_arg() take over
the ARG_PTR_TO_BTF_ID case.
arg_btf_id[] shares a union with arg_size[], so an argument cannot
store both. The scalar-struct memory fallback keeps resolving its byte
size at verification time, as it does today.
No functional change.
Signed-off-by: Amery Hung <ameryhung@gmail.com>
---
include/linux/bpf.h | 12 +++++-----
kernel/bpf/verifier.c | 54 +++++++++++++++++++++++++++++++------------
2 files changed, 45 insertions(+), 21 deletions(-)
diff --git a/include/linux/bpf.h b/include/linux/bpf.h
index 1f78746e0601..fe6ab92ceeff 100644
--- a/include/linux/bpf.h
+++ b/include/linux/bpf.h
@@ -1020,13 +1020,13 @@ struct bpf_func_proto {
};
union {
struct {
- u32 *arg1_btf_id;
- u32 *arg2_btf_id;
- u32 *arg3_btf_id;
- u32 *arg4_btf_id;
- u32 *arg5_btf_id;
+ const u32 *arg1_btf_id;
+ const u32 *arg2_btf_id;
+ const u32 *arg3_btf_id;
+ const u32 *arg4_btf_id;
+ const u32 *arg5_btf_id;
};
- u32 *arg_btf_id[MAX_BPF_FUNC_ARGS];
+ const u32 *arg_btf_id[MAX_BPF_FUNC_ARGS];
struct {
size_t arg1_size;
size_t arg2_size;
diff --git a/kernel/bpf/verifier.c b/kernel/bpf/verifier.c
index c5b394e847e6..b6e36df72d13 100644
--- a/kernel/bpf/verifier.c
+++ b/kernel/bpf/verifier.c
@@ -8793,7 +8793,7 @@ static int check_func_arg(struct bpf_verifier_env *env, u32 arg,
enum bpf_arg_type arg_type = fn->arg_type[arg];
int regno = reg_from_argno(argno);
enum bpf_reg_type type = reg->type;
- u32 *arg_btf_id = NULL;
+ const u32 *arg_btf_id = NULL;
u32 key_size;
int err = 0;
@@ -12082,13 +12082,18 @@ bool bpf_is_kfunc_pkt_changing(struct bpf_call_arg_meta *meta)
static int
get_kfunc_arg_type(struct bpf_verifier_env *env, struct bpf_call_arg_meta *meta,
- const struct btf_param *args, int arg, int nargs)
+ const struct btf_param *args, int arg, int nargs,
+ struct bpf_func_proto *proto)
{
const struct btf_type *t, *ref_t = NULL;
+ const u32 *ref_id_ptr = NULL;
argno_t argno = argno_from_arg(arg + 1);
const char *ref_tname = NULL;
+ u32 ref_id;
int arg_type;
+ proto->arg_btf_id[arg] = NULL;
+
if (is_kfunc_arg_prog_aux(meta->btf, &args[arg]))
return ARG_PTR_TO_PROG_AUX;
@@ -12116,7 +12121,11 @@ get_kfunc_arg_type(struct bpf_verifier_env *env, struct bpf_call_arg_meta *meta,
reg_arg_name(env, argno), btf_type_str(t));
return -EINVAL;
}
- ref_t = btf_type_skip_modifiers(meta->btf, t->type, NULL);
+ /* Keep a pointer to the BTF field containing the resolved referent ID. */
+ ref_id_ptr = &t->type;
+ ref_t = btf_type_skip_modifiers(meta->btf, *ref_id_ptr, &ref_id);
+ while (*ref_id_ptr != ref_id)
+ ref_id_ptr = &btf_type_by_id(meta->btf, *ref_id_ptr)->type;
ref_tname = btf_name_by_offset(meta->btf, ref_t->name_off);
/* In this function, we verify the kfunc's BTF as per the argument type,
@@ -12239,13 +12248,21 @@ get_kfunc_arg_type(struct bpf_verifier_env *env, struct bpf_call_arg_meta *meta,
if (is_kfunc_release(meta) && arg == 0)
arg_type |= OBJ_RELEASE;
- /*
- * A KF_RCU kfunc accepts an RCU-protected pointer where it would
- * otherwise demand a referenced or trusted one. Only ARG_PTR_TO_BTF_ID
- * looks at where its register came from, so leave the other kinds alone.
- */
- if (base_type(arg_type) == ARG_PTR_TO_BTF_ID && is_kfunc_rcu(meta))
- arg_type |= MEM_RCU;
+ if (base_type(arg_type) == ARG_PTR_TO_BTF_ID) {
+ /* A __map argument names a vmlinux type rather than one from
+ * the kfunc's own BTF.
+ */
+ if (is_kfunc_arg_map(meta->btf, &args[arg]))
+ proto->arg_btf_id[arg] = reg2btf_ids[CONST_PTR_TO_MAP];
+ else
+ proto->arg_btf_id[arg] = ref_id_ptr;
+
+ /* A KF_RCU kfunc accepts an RCU-protected pointer where it would
+ * otherwise demand a referenced or trusted one.
+ */
+ if (is_kfunc_rcu(meta))
+ arg_type |= MEM_RCU;
+ }
return arg_type;
}
@@ -12271,7 +12288,7 @@ static int gen_kfunc_arg_proto(struct bpf_verifier_env *env, struct bpf_call_arg
}
for (i = 0; i < nargs; i++) {
- arg_type = get_kfunc_arg_type(env, meta, args, i, nargs);
+ arg_type = get_kfunc_arg_type(env, meta, args, i, nargs, proto);
if (arg_type < 0)
return arg_type;
@@ -12923,10 +12940,17 @@ static int check_kfunc_args(struct bpf_verifier_env *env, struct bpf_call_arg_me
if (bpf_register_is_null(reg) && type_may_be_null(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);
- ref_tname = btf_name_by_offset(btf, ref_t->name_off);
+ if (base_type(arg_type) == ARG_PTR_TO_BTF_ID) {
+ /*
+ * gen_kfunc_arg_proto() resolved the expected BTF ID
+ * once. A __map argument names a vmlinux type rather
+ * than one from the kfunc's own BTF.
+ */
+ ref_id = *meta->fn->arg_btf_id[i];
+ if (is_kfunc_arg_map(btf, &args[i])) {
+ ref_t = btf_type_by_id(btf_vmlinux, ref_id);
+ ref_tname = btf_name_by_offset(btf, ref_t->name_off);
+ }
}
ret = check_func_arg_reg_off(env, reg, argno, arg_type);
--
2.52.0
^ permalink raw reply related [flat|nested] 54+ messages in thread
* [PATCH bpf-next v1 13/22] bpf: Resolve ARG_PTR_TO_MEM | MEM_FIXED_SIZE size in kfunc bpf_func_proto
2026-09-05 22:00 [PATCH bpf-next v1 00/22] bpf: Unify helper and kfunc argument checks Amery Hung
` (11 preceding siblings ...)
2026-09-05 22:01 ` [PATCH bpf-next v1 12/22] bpf: Resolve BTF ID of ARG_PTR_TO_BTF_ID in kfunc bpf_func_proto Amery Hung
@ 2026-09-05 22:01 ` Amery Hung
2026-09-05 22:01 ` [PATCH bpf-next v1 14/22] bpf: Consolidate runtime argument type resolution Amery Hung
` (9 subsequent siblings)
22 siblings, 0 replies; 54+ messages in thread
From: Amery Hung @ 2026-09-05 22:01 UTC (permalink / raw)
To: bpf
Cc: alexei.starovoitov, andrii, daniel, eddyz87, memxor, ameryhung,
kernel-team
A generated kfunc prototype classifies a pointer to scalar memory
without an adjacent size argument as ARG_PTR_TO_MEM | MEM_FIXED_SIZE.
The kfunc path currently walks BTF and resolves the pointee size each
time it verifies a call. The common check_func_arg() path instead
expects that size in bpf_func_proto::arg_size[].
Resolve the size when generating the prototype and teach the existing
kfunc path to consume the cached value. This prepares fixed-size memory
arguments to move to the common checker.
arg_size[] shares storage with arg_btf_id[]. An ARG_PTR_TO_BTF_ID
argument that falls back to scalar-struct memory therefore keeps its BTF
ID pointer and continues resolving the size when that fallback is taken.
No functional change.
Signed-off-by: Amery Hung <ameryhung@gmail.com>
---
kernel/bpf/verifier.c | 37 ++++++++++++++++++++++++-------------
1 file changed, 24 insertions(+), 13 deletions(-)
diff --git a/kernel/bpf/verifier.c b/kernel/bpf/verifier.c
index b6e36df72d13..2e068c095436 100644
--- a/kernel/bpf/verifier.c
+++ b/kernel/bpf/verifier.c
@@ -12085,11 +12085,11 @@ get_kfunc_arg_type(struct bpf_verifier_env *env, struct bpf_call_arg_meta *meta,
const struct btf_param *args, int arg, int nargs,
struct bpf_func_proto *proto)
{
- const struct btf_type *t, *ref_t = NULL;
+ const struct btf_type *t, *ref_t = NULL, *resolve_ret;
const u32 *ref_id_ptr = NULL;
argno_t argno = argno_from_arg(arg + 1);
const char *ref_tname = NULL;
- u32 ref_id;
+ u32 ref_id, type_size;
int arg_type;
proto->arg_btf_id[arg] = NULL;
@@ -12232,6 +12232,15 @@ get_kfunc_arg_type(struct bpf_verifier_env *env, struct bpf_call_arg_meta *meta,
reg_arg_name(env, argno), btf_type_str(ref_t), ref_tname);
return -EINVAL;
}
+ resolve_ret = btf_resolve_size(meta->btf, ref_t, &type_size);
+ if (IS_ERR(resolve_ret)) {
+ verbose(env,
+ "%s reference type('%s %s') size cannot be determined: %ld\n",
+ reg_arg_name(env, argno), btf_type_str(ref_t),
+ ref_tname, PTR_ERR(resolve_ret));
+ return -EINVAL;
+ }
+ proto->arg_size[arg] = type_size;
arg_type = ARG_PTR_TO_MEM | MEM_FIXED_SIZE;
}
@@ -12888,7 +12897,8 @@ static int check_kfunc_args(struct bpf_verifier_env *env, struct bpf_call_arg_me
const struct btf_type *t, *ref_t, *resolve_ret;
argno_t argno = argno_from_arg(i + 1);
int regno = reg_from_argno(argno);
- u32 ref_id = args[i].type, type_size;
+ u32 ref_id = args[i].type;
+ u32 arg_size = arg_type & MEM_FIXED_SIZE ? meta->fn->arg_size[i] : 0;
if (arg_type == ARG_PTR_TO_PROG_AUX) {
cur_aux(env)->arg_prog = regno;
@@ -13265,20 +13275,21 @@ static int check_kfunc_args(struct bpf_verifier_env *env, struct bpf_call_arg_me
* If the register does not contain btf id but the argument type is a pointer to
* scalar-only struct, allow verifying it as a fixed size memory.
*/
+ resolve_ret = btf_resolve_size(btf, ref_t, &arg_size);
+ if (IS_ERR(resolve_ret)) {
+ verbose(env,
+ "%s reference type('%s %s') size cannot be determined: %ld\n",
+ reg_arg_name(env, argno), btf_type_str(ref_t),
+ ref_tname, PTR_ERR(resolve_ret));
+ return -EINVAL;
+ }
arg_type = ARG_PTR_TO_MEM | MEM_FIXED_SIZE;
fallthrough;
case ARG_PTR_TO_MEM:
if (arg_type & MEM_FIXED_SIZE) {
bool known_memory;
- resolve_ret = btf_resolve_size(btf, ref_t, &type_size);
- if (IS_ERR(resolve_ret)) {
- verbose(env, "%s reference type('%s %s') size cannot be determined: %ld\n",
- reg_arg_name(env, argno), btf_type_str(ref_t),
- ref_tname, PTR_ERR(resolve_ret));
- return -EINVAL;
- }
- ret = check_mem_reg(env, reg, argno, type_size, BPF_READ | BPF_WRITE,
+ ret = check_mem_reg(env, reg, argno, arg_size, BPF_READ | BPF_WRITE,
meta, &known_memory);
if (ret < 0) {
const char *expected_type;
@@ -13289,14 +13300,14 @@ static int check_kfunc_args(struct bpf_verifier_env *env, struct bpf_call_arg_me
env, insn_idx, argno, func_name,
"Pass memory with at least the required number of accessible bytes and suitable read and write access.",
"the kfunc expects %u bytes of memory for %s, but the verifier cannot prove that %s provides a readable and writable range of that size",
- type_size, expected_type,
+ arg_size, expected_type,
bpf_diag_reg_type_plain(env, reg->type));
else
bpf_diag_call_arg_fmt(
env, insn_idx, argno, func_name,
"Pass stack, map, context, or other verifier-known memory of the expected type and size, not an integer cast to a pointer.",
"the kfunc expects %u bytes of memory for %s, but it is %s and not verifier-known memory",
- type_size, expected_type,
+ arg_size, expected_type,
bpf_diag_reg_type_plain(env, reg->type));
return ret;
}
--
2.52.0
^ permalink raw reply related [flat|nested] 54+ messages in thread
* [PATCH bpf-next v1 14/22] bpf: Consolidate runtime argument type resolution
2026-09-05 22:00 [PATCH bpf-next v1 00/22] bpf: Unify helper and kfunc argument checks Amery Hung
` (12 preceding siblings ...)
2026-09-05 22:01 ` [PATCH bpf-next v1 13/22] bpf: Resolve ARG_PTR_TO_MEM | MEM_FIXED_SIZE size " Amery Hung
@ 2026-09-05 22:01 ` Amery Hung
2026-09-10 21:52 ` Alexei Starovoitov
2026-09-05 22:01 ` [PATCH bpf-next v1 15/22] bpf: Consolidate nullable argument validation Amery Hung
` (8 subsequent siblings)
22 siblings, 1 reply; 54+ messages in thread
From: Amery Hung @ 2026-09-05 22:01 UTC (permalink / raw)
To: bpf
Cc: alexei.starovoitov, andrii, daniel, eddyz87, memxor, ameryhung,
kernel-team
A function argument type can require call-site refinement before
register admission. Helpers refine map-value arguments according to the
recorded map type, while kfuncs reinterpret a pointer to a scalar-only
BTF struct as fixed-size memory when the register does not carry a
BTF ID.
Add resolve_func_arg_type() as the common entry point. It delegates
helper map-value refinement to the existing resolve_map_arg_type(),
then handles the kfunc BTF-to-memory fallback. It updates the effective
argument type and size consumed by the subsequent checks.
Preserve PTR_MAYBE_NULL when changing the base type so later
nullability validation observes the BTF contract. Keep OBJ_RELEASE
arguments in their original pointer class. The latter is not a bug fix:
existing kfunc checking already validates ownership before this
resolver. It preserves release metadata for the later shared checking
order, where ownership validation follows register admission.
Moving the kfunc fallback before admission lets the resulting
fixed-size memory argument enter the compatibility check directly.
This is another step toward routing both call types through
check_func_arg().
Signed-off-by: Amery Hung <ameryhung@gmail.com>
---
kernel/bpf/verifier.c | 199 +++++++++++++++++++++++-------------------
1 file changed, 110 insertions(+), 89 deletions(-)
diff --git a/kernel/bpf/verifier.c b/kernel/bpf/verifier.c
index 2e068c095436..15ed47b90316 100644
--- a/kernel/bpf/verifier.c
+++ b/kernel/bpf/verifier.c
@@ -8151,8 +8151,8 @@ static bool is_kfunc_call(const struct bpf_call_arg_meta *meta, u32 btf_id)
}
static int resolve_map_arg_type(struct bpf_verifier_env *env,
- const struct bpf_call_arg_meta *meta,
- enum bpf_arg_type *arg_type)
+ const struct bpf_call_arg_meta *meta,
+ enum bpf_arg_type *arg_type)
{
if (!meta->map.ptr) {
/* kernel subsystem misconfigured verifier */
@@ -8180,6 +8180,11 @@ static int resolve_map_arg_type(struct bpf_verifier_env *env,
return 0;
}
+static int resolve_func_arg_type(struct bpf_verifier_env *env,
+ struct bpf_reg_state *reg, u32 arg,
+ struct bpf_call_arg_meta *meta, int insn_idx,
+ enum bpf_arg_type *arg_type, u32 *arg_size);
+
struct bpf_reg_types {
const enum bpf_reg_type types[10];
u32 *btf_id;
@@ -8794,6 +8799,7 @@ static int check_func_arg(struct bpf_verifier_env *env, u32 arg,
int regno = reg_from_argno(argno);
enum bpf_reg_type type = reg->type;
const u32 *arg_btf_id = NULL;
+ u32 arg_size = arg_type & MEM_FIXED_SIZE ? fn->arg_size[arg] : 0;
u32 key_size;
int err = 0;
@@ -8823,11 +8829,9 @@ static int check_func_arg(struct bpf_verifier_env *env, u32 arg,
return -EACCES;
}
- if (base_type(arg_type) == ARG_PTR_TO_MAP_VALUE) {
- err = resolve_map_arg_type(env, meta, &arg_type);
- if (err)
- return err;
- }
+ err = resolve_func_arg_type(env, reg, arg, meta, insn_idx, &arg_type, &arg_size);
+ if (err)
+ return err;
if (bpf_register_is_null(reg) && type_may_be_null(arg_type))
/* A NULL register has a SCALAR_VALUE type, so skip
@@ -8965,12 +8969,12 @@ static int check_func_arg(struct bpf_verifier_env *env, u32 arg,
* next is_mem_size argument below.
*/
if (arg_type & MEM_FIXED_SIZE) {
- err = check_mem_reg(env, reg, argno, fn->arg_size[arg],
+ err = check_mem_reg(env, reg, argno, arg_size,
arg_type & MEM_WRITE ? BPF_WRITE : BPF_READ, meta, NULL);
if (err)
return err;
if (arg_type & MEM_ALIGNED)
- err = check_ptr_alignment(env, reg, 0, fn->arg_size[arg], true);
+ err = check_ptr_alignment(env, reg, 0, arg_size, true);
}
break;
case ARG_MEM_SIZE:
@@ -11814,6 +11818,65 @@ static bool btf_type_is_scalar_struct(struct bpf_verifier_env *env,
return btf_struct_is_composed_of(env, btf, t, BTF_MEMBER_SCALAR);
}
+static int resolve_func_arg_type(struct bpf_verifier_env *env,
+ struct bpf_reg_state *reg, u32 arg,
+ struct bpf_call_arg_meta *meta, int insn_idx,
+ enum bpf_arg_type *arg_type, u32 *arg_size)
+{
+ argno_t argno = argno_from_arg(arg + 1);
+ const struct btf_param *args;
+ const struct btf_type *ref_t, *resolve_ret;
+ const struct btf *btf;
+ const char *ref_tname;
+ u32 ref_id;
+
+ if (base_type(*arg_type) == ARG_PTR_TO_MAP_VALUE)
+ return resolve_map_arg_type(env, meta, arg_type);
+
+ if (base_type(*arg_type) == ARG_PTR_TO_BTF_ID) {
+ if (!meta->btf || arg_type_is_release(*arg_type) ||
+ base_type(reg->type) == PTR_TO_BTF_ID ||
+ reg2btf_ids[base_type(reg->type)])
+ return 0;
+
+ args = btf_params(meta->func_proto);
+ ref_id = *meta->fn->arg_btf_id[arg];
+ btf = is_kfunc_arg_map(meta->btf, &args[arg]) ? btf_vmlinux : meta->btf;
+ ref_t = btf_type_skip_modifiers(btf, ref_id, &ref_id);
+ ref_tname = btf_name_by_offset(btf, ref_t->name_off);
+
+ if (!btf_type_is_scalar_struct(env, btf, ref_t)) {
+ enum bpf_reg_type reg2btf_type = lookup_reg2btf_ids(ref_id);
+ const char *expected_type;
+
+ verbose(env, "%s is %s expected %s %s",
+ reg_arg_name(env, argno), reg_type_str(env, reg->type),
+ btf_type_str(ref_t), ref_tname);
+ if (reg2btf_type != NOT_INIT)
+ verbose(env, " or %s", reg_type_str(env, reg2btf_type));
+ verbose(env, "\n");
+ expected_type = bpf_diag_fmt_btf_type(env, btf, ref_id);
+ bpf_diag_call_arg_fmt(env, insn_idx, argno, meta->func_name,
+ "Pass a verifier-tracked pointer to the expected kernel object type, not a pointer to stack storage or another memory buffer.",
+ "the function expects a pointer to %s, but this argument is %s and cannot be used as that kernel object pointer",
+ expected_type,
+ bpf_diag_reg_type_plain(env, reg->type));
+ return -EINVAL;
+ }
+
+ resolve_ret = btf_resolve_size(btf, ref_t, arg_size);
+ if (IS_ERR(resolve_ret)) {
+ verbose(env, "%s reference type('%s %s') size cannot be determined: %ld\n",
+ reg_arg_name(env, argno), btf_type_str(ref_t), ref_tname,
+ PTR_ERR(resolve_ret));
+ return -EINVAL;
+ }
+ *arg_type = ARG_PTR_TO_MEM | MEM_FIXED_SIZE | (*arg_type & PTR_MAYBE_NULL);
+ }
+
+ return 0;
+}
+
static void btf_member_path_str(const struct btf *btf, const struct btf_member_path *path,
char *buf, size_t buf_sz)
{
@@ -12894,7 +12957,7 @@ static int check_kfunc_args(struct bpf_verifier_env *env, struct bpf_call_arg_me
for (i = 0; i < nargs; i++) {
struct bpf_reg_state *reg = get_func_arg_reg(caller, regs, i);
enum bpf_arg_type arg_type = meta->fn->arg_type[i];
- const struct btf_type *t, *ref_t, *resolve_ret;
+ const struct btf_type *t, *ref_t;
argno_t argno = argno_from_arg(i + 1);
int regno = reg_from_argno(argno);
u32 ref_id = args[i].type;
@@ -12951,18 +13014,13 @@ static int check_kfunc_args(struct bpf_verifier_env *env, struct bpf_call_arg_me
continue;
if (base_type(arg_type) == ARG_PTR_TO_BTF_ID) {
- /*
- * gen_kfunc_arg_proto() resolved the expected BTF ID
- * once. A __map argument names a vmlinux type rather
- * than one from the kfunc's own BTF.
- */
+ /* gen_kfunc_arg_proto() resolved the expected BTF ID once. */
ref_id = *meta->fn->arg_btf_id[i];
- if (is_kfunc_arg_map(btf, &args[i])) {
- ref_t = btf_type_by_id(btf_vmlinux, ref_id);
- ref_tname = btf_name_by_offset(btf, ref_t->name_off);
- }
}
+ ret = resolve_func_arg_type(env, reg, i, meta, insn_idx, &arg_type, &arg_size);
+ if (ret < 0)
+ return ret;
ret = check_func_arg_reg_off(env, reg, argno, arg_type);
if (ret < 0)
return ret;
@@ -13211,80 +13269,43 @@ static int check_kfunc_args(struct bpf_verifier_env *env, struct bpf_call_arg_me
return ret;
break;
case ARG_PTR_TO_BTF_ID:
- /* Only base_type is checked, further checks are done here */
- if (base_type(reg->type) == PTR_TO_BTF_ID ||
- reg2btf_ids[base_type(reg->type)]) {
- if (!is_trusted_reg(env, reg) ||
- bpf_type_has_unsafe_modifiers(reg->type)) {
- if (!(arg_type & MEM_RCU)) {
- const char *expected_type;
-
- expected_type = bpf_diag_fmt_btf_type(env, btf, ref_id);
- verbose(env, "%s must be referenced or trusted\n",
- reg_arg_name(env, argno));
- bpf_diag_call_arg_fmt(env, insn_idx, argno, func_name,
- "Pass a pointer acquired from a verifier-tracked source, or call this kfunc only inside the required protection if it accepts RCU pointers.",
- "the kfunc requires a trusted or resource-owning pointer to %s, but %s is %s",
- expected_type,
- reg_arg_name(env, argno),
- bpf_diag_reg_type_plain(env, reg->type));
- return -EINVAL;
- }
- if (!is_rcu_reg(reg)) {
- const char *expected_type;
-
- expected_type = bpf_diag_fmt_btf_type(env, btf, ref_id);
- verbose(env, "%s must be a rcu pointer\n",
- reg_arg_name(env, argno));
- bpf_diag_call_arg_fmt(env, insn_idx, argno, func_name,
- "Use this kfunc with a pointer that is valid in an RCU read lock region.",
- "the kfunc requires an RCU-protected pointer to %s, but %s is %s",
- expected_type,
- reg_arg_name(env, argno),
- bpf_diag_reg_type_plain(env, reg->type));
- return -EINVAL;
- }
- }
+ if (!is_trusted_reg(env, reg) ||
+ bpf_type_has_unsafe_modifiers(reg->type)) {
+ if (!(arg_type & MEM_RCU)) {
+ const char *actual_type, *arg_name, *expected_type;
- ret = process_kf_arg_ptr_to_btf_id(env, reg, ref_t, ref_tname, ref_id, meta, i, argno);
- if (ret < 0)
- return ret;
- break;
- }
+ expected_type = bpf_diag_fmt_btf_type(env, btf, ref_id);
+ verbose(env, "%s must be referenced or trusted\n",
+ reg_arg_name(env, argno));
+ arg_name = reg_arg_name(env, argno);
+ actual_type = bpf_diag_reg_type_plain(env, reg->type);
+ bpf_diag_call_arg_fmt(env, insn_idx, argno, func_name,
+ "Pass a pointer acquired from a verifier-tracked source, or call this kfunc only inside the required protection if it accepts RCU pointers.",
+ "the kfunc requires a trusted or resource-owning pointer to %s, but %s is %s",
+ expected_type, arg_name, actual_type);
+ return -EINVAL;
+ }
+ if (!is_rcu_reg(reg)) {
+ const char *actual_type, *arg_name, *expected_type;
- if (!btf_type_is_scalar_struct(env, meta->btf, ref_t)) {
- enum bpf_reg_type reg2btf_type = lookup_reg2btf_ids(ref_id);
- const char *expected_type;
-
- verbose(env, "%s is %s expected %s %s",
- reg_arg_name(env, argno), reg_type_str(env, reg->type),
- btf_type_str(ref_t), ref_tname);
- if (reg2btf_type != NOT_INIT)
- verbose(env, " or %s", reg_type_str(env, reg2btf_type));
- verbose(env, "\n");
- expected_type = bpf_diag_fmt_btf_type(env, btf, ref_id);
- bpf_diag_call_arg_fmt(env, insn_idx, argno, func_name,
- "Pass a verifier-tracked pointer to the expected kernel object type, not a pointer to stack storage or another memory buffer.",
- "the kfunc expects a pointer to %s, but this argument is %s and cannot be used as that kernel object pointer",
- expected_type,
- bpf_diag_reg_type_plain(env, reg->type));
- return -EINVAL;
+ expected_type = bpf_diag_fmt_btf_type(env, btf, ref_id);
+ verbose(env, "%s must be a rcu pointer\n",
+ reg_arg_name(env, argno));
+ arg_name = reg_arg_name(env, argno);
+ actual_type = bpf_diag_reg_type_plain(env, reg->type);
+ bpf_diag_call_arg_fmt(env, insn_idx, argno, func_name,
+ "Use this kfunc with a pointer that is valid in an RCU read lock region.",
+ "the kfunc requires an RCU-protected pointer to %s, but %s is %s",
+ expected_type, arg_name, actual_type);
+ return -EINVAL;
+ }
}
- /*
- * If the register does not contain btf id but the argument type is a pointer to
- * scalar-only struct, allow verifying it as a fixed size memory.
- */
- resolve_ret = btf_resolve_size(btf, ref_t, &arg_size);
- if (IS_ERR(resolve_ret)) {
- verbose(env,
- "%s reference type('%s %s') size cannot be determined: %ld\n",
- reg_arg_name(env, argno), btf_type_str(ref_t),
- ref_tname, PTR_ERR(resolve_ret));
- return -EINVAL;
- }
- arg_type = ARG_PTR_TO_MEM | MEM_FIXED_SIZE;
- fallthrough;
+ ret = process_kf_arg_ptr_to_btf_id(env, reg, ref_t, ref_tname,
+ ref_id, meta, i, argno);
+ if (ret < 0)
+ return ret;
+ break;
case ARG_PTR_TO_MEM:
if (arg_type & MEM_FIXED_SIZE) {
bool known_memory;
--
2.52.0
^ permalink raw reply related [flat|nested] 54+ messages in thread
* [PATCH bpf-next v1 15/22] bpf: Consolidate nullable argument validation
2026-09-05 22:00 [PATCH bpf-next v1 00/22] bpf: Unify helper and kfunc argument checks Amery Hung
` (13 preceding siblings ...)
2026-09-05 22:01 ` [PATCH bpf-next v1 14/22] bpf: Consolidate runtime argument type resolution Amery Hung
@ 2026-09-05 22:01 ` Amery Hung
2026-09-05 22:01 ` [PATCH bpf-next v1 16/22] bpf: Drop redundant BTF pointer helper write rejection Amery Hung
` (7 subsequent siblings)
22 siblings, 0 replies; 54+ messages in thread
From: Amery Hung @ 2026-09-05 22:01 UTC (permalink / raw)
To: bpf
Cc: alexei.starovoitov, andrii, daniel, eddyz87, memxor, ameryhung,
kernel-team
check_kfunc_args() rejects a NULL or possibly-NULL register passed to
a non-nullable pointer argument up front. Helpers only do so inside
check_reg_type() for PTR_TO_BTF_ID arguments, leaving other pointer
kinds to fail indirectly through compatible_reg_types[].
Give both paths the same policy and factor it into
check_func_arg_nullability(). Gate it on arg_type_is_scalar() rather
than on the BTF shape of the kfunc parameter. This keeps a zero passed
to a size argument from being interpreted as a NULL pointer.
Taking nullability from the argument classification also avoids an
is_kfunc_arg_nullable() lookup for every kfunc argument on every
verification.
Keep release-argument nullability separate from ownership checking.
A possibly-NULL release argument must be rejected even if it is
otherwise nullable, while the ownership check must still permit a
literal NULL for bpf_kptr_xchg().
Use call-neutral structured diagnostics and retain the expected BTF
type when it is available for a kfunc argument. Resolve that type lazily
from the call metadata only when reporting an error. Helpers now report
NULL-ness rather than a register-type mismatch for non-BTF-ID pointer
arguments and literal NULL passed to a non-nullable pointer argument.
Update the affected selftest expectations.
Signed-off-by: Amery Hung <ameryhung@gmail.com>
---
kernel/bpf/verifier.c | 110 ++++++++++++------
.../selftests/bpf/prog_tests/cb_refs.c | 2 +-
.../selftests/bpf/progs/cpumask_failure.c | 2 +-
.../selftests/bpf/progs/iters_testmod.c | 4 +-
.../selftests/bpf/progs/map_kptr_fail.c | 6 +-
.../selftests/bpf/progs/verifier_ctx.c | 2 +-
.../progs/verifier_helper_access_var_len.c | 4 +-
.../selftests/bpf/progs/verifier_live_stack.c | 2 +-
.../selftests/bpf/progs/verifier_map_in_map.c | 3 +-
.../bpf/progs/verifier_map_lookup_refine.c | 2 +-
.../bpf/progs/verifier_ref_tracking.c | 4 +-
.../selftests/bpf/progs/verifier_sock.c | 4 +-
.../testing/selftests/bpf/verifier/map_kptr.c | 2 +-
13 files changed, 94 insertions(+), 53 deletions(-)
diff --git a/kernel/bpf/verifier.c b/kernel/bpf/verifier.c
index 15ed47b90316..1f5f975c73a7 100644
--- a/kernel/bpf/verifier.c
+++ b/kernel/bpf/verifier.c
@@ -8135,6 +8135,25 @@ static bool arg_type_is_dynptr(enum bpf_arg_type type)
return base_type(type) == ARG_PTR_TO_DYNPTR;
}
+/*
+ * An argument that only ever takes a scalar, so a zero register passed to it
+ * is a value rather than a NULL pointer.
+ */
+static bool arg_type_is_scalar(enum bpf_arg_type type)
+{
+ switch (base_type(type)) {
+ case ARG_SCALAR:
+ case ARG_CONST_SCALAR:
+ case ARG_MEM_SIZE:
+ case ARG_MEM_SIZE_OR_ZERO:
+ case ARG_CONST_MEM_SIZE:
+ case ARG_CONST_ALLOC_SIZE_OR_ZERO:
+ return true;
+ default:
+ return false;
+ }
+}
+
/*
* A kfunc is named by a BTF ID, which can take the same numeric value as an
* enum bpf_func_id. Only test meta->func_id against a BPF_FUNC_* once the call
@@ -8336,6 +8355,34 @@ __printf(6, 7) static void bpf_diag_call_arg_fmt(struct bpf_verifier_env *env, u
bpf_diag_call_arg(env, insn_idx, argno, call_name, reason, suggestion);
}
+static int check_func_arg_nullability(struct bpf_verifier_env *env,
+ struct bpf_reg_state *reg, argno_t argno,
+ enum bpf_arg_type arg_type,
+ struct bpf_call_arg_meta *meta, int insn_idx)
+{
+ const char *expected_type = "pointer";
+
+ if (arg_type_is_scalar(arg_type) || type_may_be_null(arg_type) ||
+ (!bpf_register_is_null(reg) && !type_may_be_null(reg->type)))
+ return 0;
+
+ if (meta->btf) {
+ u32 arg_btf_id;
+
+ arg_btf_id = btf_params(meta->func_proto)[arg_idx_from_argno(argno)].type;
+ expected_type = bpf_diag_fmt(env, "value of type %s",
+ bpf_diag_fmt_btf_type(env, meta->btf, arg_btf_id));
+ }
+
+ verbose(env, "Possibly NULL pointer passed to trusted %s\n",
+ reg_arg_name(env, argno));
+ bpf_diag_call_arg_fmt(env, insn_idx, argno, meta->func_name,
+ "Add a NULL check and make the call only on the non-NULL path.",
+ "the pointer may be NULL, but this call requires a non-NULL %s",
+ expected_type);
+ return -EACCES;
+}
+
static const char *bpf_diag_expected_reg_types(struct bpf_verifier_env *env,
const enum bpf_reg_type *types, int count)
{
@@ -8448,17 +8495,6 @@ static int check_reg_type(struct bpf_verifier_env *env, struct bpf_reg_state *re
bool strict_type_match = arg_type_is_release(arg_type) &&
!is_helper_call(meta, BPF_FUNC_sk_release);
- if (type_may_be_null(reg->type) &&
- (!type_may_be_null(arg_type) || arg_type_is_release(arg_type))) {
- verbose(env, "Possibly NULL pointer passed to helper %s\n",
- reg_arg_name(env, argno));
- bpf_diag_call_arg(
- env, env->insn_idx, argno, meta->func_name,
- "the pointer may be NULL, but this call requires a non-NULL pointer",
- "Add a NULL check and make the call only on the non-NULL path.");
- return -EACCES;
- }
-
if (!arg_btf_id) {
if (!compatible->btf_id) {
verifier_bug(env, "missing arg compatible BTF ID");
@@ -8839,6 +8875,10 @@ static int check_func_arg(struct bpf_verifier_env *env, u32 arg,
*/
goto skip_type_check;
+ err = check_func_arg_nullability(env, reg, argno, arg_type, meta, insn_idx);
+ if (err)
+ return err;
+
/* arg_btf_id and arg_size are in a union. */
if (base_type(arg_type) == ARG_PTR_TO_BTF_ID ||
base_type(arg_type) == ARG_PTR_TO_SPIN_LOCK)
@@ -8853,15 +8893,28 @@ static int check_func_arg(struct bpf_verifier_env *env, u32 arg,
return err;
skip_type_check:
- if (arg_type_is_release(arg_type) && !arg_type_is_dynptr(arg_type) &&
- !reg_is_referenced(env, reg) && !bpf_register_is_null(reg)) {
- verbose(env, "release helper %s expects referenced PTR_TO_BTF_ID passed to %s\n",
- meta->func_name, reg_arg_name(env, argno));
- bpf_diag_call_arg(
- env, insn_idx, argno, meta->func_name,
- "release helpers require a value that owns a live resource returned by a matching acquire helper",
- "Pass the resource-owning pointer returned by the matching acquire helper, and avoid calling the release helper after ownership has already been transferred or released.");
- return -EINVAL;
+ if (arg_type_is_release(arg_type)) {
+ if (type_may_be_null(reg->type)) {
+ verbose(env, "Possibly NULL pointer passed to trusted %s\n",
+ reg_arg_name(env, argno));
+ bpf_diag_call_arg(
+ env, insn_idx, argno, meta->func_name,
+ "the pointer may be NULL, but this call requires a non-NULL pointer",
+ "Add a NULL check and make the call only on the non-NULL path.");
+ return -EACCES;
+ }
+
+ if (!arg_type_is_dynptr(arg_type) &&
+ !reg_is_referenced(env, reg) && !bpf_register_is_null(reg)) {
+ verbose(env,
+ "release helper %s expects referenced PTR_TO_BTF_ID passed to %s\n",
+ meta->func_name, reg_arg_name(env, argno));
+ bpf_diag_call_arg(
+ env, insn_idx, argno, meta->func_name,
+ "release helpers require a value that owns a live resource returned by a matching acquire helper",
+ "Pass the resource-owning pointer returned by the matching acquire helper, and avoid calling the release helper after ownership has already been transferred or released.");
+ return -EINVAL;
+ }
}
if (reg_is_referenced(env, reg))
@@ -12978,20 +13031,9 @@ static int check_kfunc_args(struct bpf_verifier_env *env, struct bpf_call_arg_me
ref_tname = btf_name_by_offset(btf, ref_t->name_off);
}
- if (btf_type_is_ptr(t) &&
- (bpf_register_is_null(reg) || type_may_be_null(reg->type)) &&
- !type_may_be_null(arg_type)) {
- const char *expected_type;
-
- expected_type = bpf_diag_fmt_btf_type(env, btf, args[i].type);
- verbose(env, "Possibly NULL pointer passed to trusted %s\n",
- reg_arg_name(env, argno));
- bpf_diag_call_arg_fmt(env, insn_idx, argno, func_name,
- "Add a NULL check and call the kfunc only on the non-NULL path.",
- "the pointer may be NULL, but this kfunc requires a non-NULL value of type %s",
- expected_type);
- return -EACCES;
- }
+ ret = check_func_arg_nullability(env, reg, argno, arg_type, meta, insn_idx);
+ if (ret < 0)
+ return ret;
if (regno == meta->release_regno && !is_kfunc_arg_dynptr(meta->btf, &args[i]) &&
!reg_is_referenced(env, reg) && !bpf_register_is_null(reg)) {
diff --git a/tools/testing/selftests/bpf/prog_tests/cb_refs.c b/tools/testing/selftests/bpf/prog_tests/cb_refs.c
index 78566b817fd7..50ea3d72d446 100644
--- a/tools/testing/selftests/bpf/prog_tests/cb_refs.c
+++ b/tools/testing/selftests/bpf/prog_tests/cb_refs.c
@@ -12,7 +12,7 @@ struct {
const char *err_msg;
} cb_refs_tests[] = {
{ "underflow_prog", "release kfunc bpf_kfunc_call_test_release expects referenced PTR_TO_BTF_ID passed to R1" },
- { "leak_prog", "Possibly NULL pointer passed to helper R2" },
+ { "leak_prog", "Possibly NULL pointer passed to trusted R2" },
{ "nested_cb", "Unreleased reference id=4 alloc_insn=2" }, /* alloc_insn=2{4,5} */
{ "non_cb_transfer_ref", "Unreleased reference id=4 alloc_insn=1" }, /* alloc_insn=1{1,2} */
};
diff --git a/tools/testing/selftests/bpf/progs/cpumask_failure.c b/tools/testing/selftests/bpf/progs/cpumask_failure.c
index 4628feb53d86..6d730535eb95 100644
--- a/tools/testing/selftests/bpf/progs/cpumask_failure.c
+++ b/tools/testing/selftests/bpf/progs/cpumask_failure.c
@@ -183,7 +183,7 @@ int BPF_PROG(test_global_mask_no_null_check, struct task_struct *task, u64 clone
}
SEC("tp_btf/task_newtask")
-__failure __msg("Possibly NULL pointer passed to helper R2")
+__failure __msg("Possibly NULL pointer passed to trusted R2")
int BPF_PROG(test_global_mask_rcu_no_null_check, struct task_struct *task, u64 clone_flags)
{
struct bpf_cpumask *prev, *curr;
diff --git a/tools/testing/selftests/bpf/progs/iters_testmod.c b/tools/testing/selftests/bpf/progs/iters_testmod.c
index 76012dbbdb41..5a3ff65e8234 100644
--- a/tools/testing/selftests/bpf/progs/iters_testmod.c
+++ b/tools/testing/selftests/bpf/progs/iters_testmod.c
@@ -135,7 +135,7 @@ int iter_ret_rcu_test_protected(const void *ctx)
}
SEC("?fentry.s/" SYS_PREFIX "sys_getpgid")
-__failure __msg("R1 type=rcu_ptr_or_null_ expected=")
+__failure __msg("Possibly NULL pointer passed to trusted R1")
int iter_ret_rcu_test_type(const void *ctx)
{
struct task_struct *p;
@@ -158,7 +158,7 @@ int iter_ret_rcu_test_protected_nostruct(const void *ctx)
}
SEC("?fentry.s/" SYS_PREFIX "sys_getpgid")
-__failure __msg("R1 type=rdonly_rcu_mem_or_null expected=")
+__failure __msg("Possibly NULL pointer passed to trusted R1")
int iter_ret_rcu_test_type_nostruct(const void *ctx)
{
void *p;
diff --git a/tools/testing/selftests/bpf/progs/map_kptr_fail.c b/tools/testing/selftests/bpf/progs/map_kptr_fail.c
index 5e25ca806060..60c14e185856 100644
--- a/tools/testing/selftests/bpf/progs/map_kptr_fail.c
+++ b/tools/testing/selftests/bpf/progs/map_kptr_fail.c
@@ -149,7 +149,7 @@ int reject_bad_type_match(struct __sk_buff *ctx)
}
SEC("?tc")
-__failure __msg("R1 type=untrusted_ptr_or_null_ expected=percpu_ptr_")
+__failure __msg("Possibly NULL pointer passed to trusted R1")
int marked_as_untrusted_or_null(struct __sk_buff *ctx)
{
struct map_value *v;
@@ -217,7 +217,7 @@ int reject_kptr_xchg_on_unref(struct __sk_buff *ctx)
}
SEC("?tc")
-__failure __msg("R1 type=rcu_ptr_or_null_ expected=percpu_ptr_")
+__failure __msg("Possibly NULL pointer passed to trusted R1")
int mark_ref_as_untrusted_or_null(struct __sk_buff *ctx)
{
struct map_value *v;
@@ -364,7 +364,7 @@ int kptr_xchg_ref_state(struct __sk_buff *ctx)
}
SEC("?tc")
-__failure __msg("Possibly NULL pointer passed to helper R2")
+__failure __msg("Possibly NULL pointer passed to trusted R2")
int kptr_xchg_possibly_null(struct __sk_buff *ctx)
{
struct prog_test_ref_kfunc *p;
diff --git a/tools/testing/selftests/bpf/progs/verifier_ctx.c b/tools/testing/selftests/bpf/progs/verifier_ctx.c
index 7856dad3d1f3..9d42ba824408 100644
--- a/tools/testing/selftests/bpf/progs/verifier_ctx.c
+++ b/tools/testing/selftests/bpf/progs/verifier_ctx.c
@@ -208,7 +208,7 @@ __naked void null_check_7_ctx_bind(void)
SEC("cgroup/post_bind4")
__description("pass ctx or null check, 8: null (bind)")
-__failure __msg("R1 type=scalar expected=ctx")
+__failure __msg("Possibly NULL pointer passed to trusted R1")
__naked void null_check_8_null_bind(void)
{
asm volatile (" \
diff --git a/tools/testing/selftests/bpf/progs/verifier_helper_access_var_len.c b/tools/testing/selftests/bpf/progs/verifier_helper_access_var_len.c
index 343fc08d9747..d1452ef6f2f9 100644
--- a/tools/testing/selftests/bpf/progs/verifier_helper_access_var_len.c
+++ b/tools/testing/selftests/bpf/progs/verifier_helper_access_var_len.c
@@ -621,7 +621,7 @@ l0_%=: exit; \
SEC("tracepoint")
__description("helper access to variable memory: size = 0 not allowed on NULL (!ARG_PTR_TO_MEM_OR_NULL)")
-__failure __msg("R1 type=scalar expected=fp")
+__failure __msg("Possibly NULL pointer passed to trusted R1")
__naked void ptr_to_mem_or_null_8(void)
{
asm volatile (" \
@@ -637,7 +637,7 @@ __naked void ptr_to_mem_or_null_8(void)
SEC("tracepoint")
__description("helper access to variable memory: size > 0 not allowed on NULL (!ARG_PTR_TO_MEM_OR_NULL)")
-__failure __msg("R1 type=scalar expected=fp")
+__failure __msg("Possibly NULL pointer passed to trusted R1")
__naked void ptr_to_mem_or_null_9(void)
{
asm volatile (" \
diff --git a/tools/testing/selftests/bpf/progs/verifier_live_stack.c b/tools/testing/selftests/bpf/progs/verifier_live_stack.c
index 401152b2b64f..bc3dfdc1a536 100644
--- a/tools/testing/selftests/bpf/progs/verifier_live_stack.c
+++ b/tools/testing/selftests/bpf/progs/verifier_live_stack.c
@@ -246,7 +246,7 @@ static __used __naked void read_first_param2(void)
SEC("socket")
__flag(BPF_F_TEST_STATE_FREQ)
__failure
-__msg("R1 type=scalar expected=map_ptr")
+__msg("Possibly NULL pointer passed to trusted R1")
__naked void caller_stack_pruning_callback(void)
{
asm volatile (
diff --git a/tools/testing/selftests/bpf/progs/verifier_map_in_map.c b/tools/testing/selftests/bpf/progs/verifier_map_in_map.c
index d3be69a9a755..621248a02a1f 100644
--- a/tools/testing/selftests/bpf/progs/verifier_map_in_map.c
+++ b/tools/testing/selftests/bpf/progs/verifier_map_in_map.c
@@ -154,8 +154,7 @@ l0_%=: r0 = 0; \
SEC("socket")
__description("forgot null checking on the inner map pointer")
-__failure __msg("R1 type=map_ptr_or_null expected=map_ptr")
-__msg("map_ptr_or_null, but this argument accepts map_ptr")
+__failure __msg("Possibly NULL pointer passed to trusted R1")
__failure_unpriv
__naked void on_the_inner_map_pointer(void)
{
diff --git a/tools/testing/selftests/bpf/progs/verifier_map_lookup_refine.c b/tools/testing/selftests/bpf/progs/verifier_map_lookup_refine.c
index c01abf54923d..4b1eadddd89c 100644
--- a/tools/testing/selftests/bpf/progs/verifier_map_lookup_refine.c
+++ b/tools/testing/selftests/bpf/progs/verifier_map_lookup_refine.c
@@ -58,7 +58,7 @@ int mapofmaps_value_as_helper_mem_buf(struct __sk_buff *skb)
}
SEC("?tc")
-__failure __msg("type=map_ptr_or_null expected=fp")
+__failure __msg("Possibly NULL pointer passed to trusted R1")
int mapofmaps_value_as_helper_fixed_mem(struct __sk_buff *skb)
{
char th[sizeof(struct tcphdr)] = {};
diff --git a/tools/testing/selftests/bpf/progs/verifier_ref_tracking.c b/tools/testing/selftests/bpf/progs/verifier_ref_tracking.c
index 199ad18f8eb5..f3fad911b5bc 100644
--- a/tools/testing/selftests/bpf/progs/verifier_ref_tracking.c
+++ b/tools/testing/selftests/bpf/progs/verifier_ref_tracking.c
@@ -344,7 +344,7 @@ __naked void potential_reference_to_system_key(void)
SEC("tc")
__description("reference tracking: release reference without check")
-__failure __msg("type=sock_or_null expected=sock")
+__failure __msg("Possibly NULL pointer passed to trusted R1")
__naked void tracking_release_reference_without_check(void)
{
asm volatile (
@@ -363,7 +363,7 @@ __naked void tracking_release_reference_without_check(void)
SEC("tc")
__description("reference tracking: release reference to sock_common without check")
-__failure __msg("type=sock_common_or_null expected=sock")
+__failure __msg("Possibly NULL pointer passed to trusted R1")
__naked void to_sock_common_without_check(void)
{
asm volatile (
diff --git a/tools/testing/selftests/bpf/progs/verifier_sock.c b/tools/testing/selftests/bpf/progs/verifier_sock.c
index 4f2f3209eec8..d59b2f905671 100644
--- a/tools/testing/selftests/bpf/progs/verifier_sock.c
+++ b/tools/testing/selftests/bpf/progs/verifier_sock.c
@@ -110,7 +110,7 @@ l0_%=: r0 = *(u32*)(r1 + %[bpf_sock_type]); \
SEC("cgroup/skb")
__description("bpf_sk_fullsock(skb->sk): no !skb->sk check")
-__failure __msg("type=sock_common_or_null expected=sock_common")
+__failure __msg("Possibly NULL pointer passed to trusted R1")
__failure_unpriv
__naked void sk_no_skb_sk_check_1(void)
{
@@ -466,7 +466,7 @@ l1_%=: r0 = *(u32*)(r0 + %[bpf_sock_rx_queue_mapping__end]);\
SEC("cgroup/skb")
__description("bpf_tcp_sock(skb->sk): no !skb->sk check")
-__failure __msg("type=sock_common_or_null expected=sock_common")
+__failure __msg("Possibly NULL pointer passed to trusted R1")
__failure_unpriv
__naked void sk_no_skb_sk_check_2(void)
{
diff --git a/tools/testing/selftests/bpf/verifier/map_kptr.c b/tools/testing/selftests/bpf/verifier/map_kptr.c
index 4b39f8472f9b..1efaff296b7c 100644
--- a/tools/testing/selftests/bpf/verifier/map_kptr.c
+++ b/tools/testing/selftests/bpf/verifier/map_kptr.c
@@ -311,7 +311,7 @@
.prog_type = BPF_PROG_TYPE_SCHED_CLS,
.fixup_map_kptr = { 1 },
.result = REJECT,
- .errstr = "R1 type=rcu_ptr_or_null_ expected=percpu_ptr_",
+ .errstr = "Possibly NULL pointer passed to trusted R1",
},
{
"map_kptr: ref: reject off != 0",
--
2.52.0
^ permalink raw reply related [flat|nested] 54+ messages in thread
* [PATCH bpf-next v1 16/22] bpf: Drop redundant BTF pointer helper write rejection
2026-09-05 22:00 [PATCH bpf-next v1 00/22] bpf: Unify helper and kfunc argument checks Amery Hung
` (14 preceding siblings ...)
2026-09-05 22:01 ` [PATCH bpf-next v1 15/22] bpf: Consolidate nullable argument validation Amery Hung
@ 2026-09-05 22:01 ` Amery Hung
2026-09-05 23:08 ` bot+bpf-ci
2026-09-05 22:01 ` [PATCH bpf-next v1 17/22] bpf: Consolidate helper and kfunc PTR_TO_BTF_ID argument matching Amery Hung
` (6 subsequent siblings)
22 siblings, 1 reply; 54+ messages in thread
From: Amery Hung @ 2026-09-05 22:01 UTC (permalink / raw)
To: bpf
Cc: alexei.starovoitov, andrii, daniel, eddyz87, memxor, ameryhung,
kernel-team
check_reg_type() rejects a trusted PTR_TO_BTF_ID admitted by
mem_types unless the argument carries MEM_RDONLY. This duplicates
argument-specific memory access validation and is too conservative for
map keys and input map values, whose read direction is not represented
by MEM_RDONLY.
Since commit fa75b7c85b0d ("bpf: Enforce write checks for BTF pointer
helper access"), check_helper_mem_access() forwards the requested access
type to check_ptr_to_btf_access(). Map keys always request read access.
Map values request write access only when tagged with MEM_WRITE.
ARG_PTR_TO_MEM prototypes must carry either MEM_RDONLY or MEM_WRITE,
and size arguments use the direction of their associated memory
argument. btf_struct_access() therefore checks the effective access
direction in every case.
Drop the early rejection and keep check_reg_type() limited to register
admission.
Signed-off-by: Amery Hung <ameryhung@gmail.com>
---
kernel/bpf/verifier.c | 10 +---------
1 file changed, 1 insertion(+), 9 deletions(-)
diff --git a/kernel/bpf/verifier.c b/kernel/bpf/verifier.c
index 1f5f975c73a7..09a51d67ab7e 100644
--- a/kernel/bpf/verifier.c
+++ b/kernel/bpf/verifier.c
@@ -8469,16 +8469,8 @@ static int check_reg_type(struct bpf_verifier_env *env, struct bpf_reg_state *re
if (base_type(reg->type) != PTR_TO_BTF_ID)
return 0;
- if (compatible == &mem_types) {
- if (!(arg_type & MEM_RDONLY)) {
- verbose(env,
- "%s() may write into memory pointed by %s type=%s\n",
- meta->func_name,
- reg_arg_name(env, argno), reg_type_str(env, reg->type));
- return -EACCES;
- }
+ if (compatible == &mem_types)
return 0;
- }
switch ((int)reg->type) {
case PTR_TO_BTF_ID:
--
2.52.0
^ permalink raw reply related [flat|nested] 54+ messages in thread
* [PATCH bpf-next v1 17/22] bpf: Consolidate helper and kfunc PTR_TO_BTF_ID argument matching
2026-09-05 22:00 [PATCH bpf-next v1 00/22] bpf: Unify helper and kfunc argument checks Amery Hung
` (15 preceding siblings ...)
2026-09-05 22:01 ` [PATCH bpf-next v1 16/22] bpf: Drop redundant BTF pointer helper write rejection Amery Hung
@ 2026-09-05 22:01 ` Amery Hung
2026-09-05 22:01 ` [PATCH bpf-next v1 18/22] bpf: Admit kfunc argument registers through check_reg_type() Amery Hung
` (5 subsequent siblings)
22 siblings, 0 replies; 54+ messages in thread
From: Amery Hung @ 2026-09-05 22:01 UTC (permalink / raw)
To: bpf
Cc: alexei.starovoitov, andrii, daniel, eddyz87, memxor, ameryhung,
kernel-team
Keep check_reg_type() focused on register admission. Helpers currently
match BTF-ID arguments there, while kfuncs use
process_kf_arg_ptr_to_btf_id(). Both paths ultimately call
btf_struct_ids_match().
Introduce process_arg_ptr_to_btf_id() for helpers, kfuncs, and global
subprograms. Callers provide the expected BTF and ID and retain their
call-specific metadata handling. Group the helper compatible-ID, poison,
and bpf_kptr_xchg() handling in a helper-only block in the
ARG_PTR_TO_BTF_ID case, leaving the common matcher outside it.
Derive strict matching from the generated argument type. This limits
KF_RELEASE strictness to the argument marked OBJ_RELEASE while
preserving the bpf_sk_release() exception and kfunc no-cast-alias rule.
Remove the post-admission BTF and nullability switch from
check_reg_type(), leaving it responsible for register admission. The
compatibility tables already limit helper MEM_ALLOC inputs to
ARG_PTR_TO_SPIN_LOCK and ARG_KPTR_XCHG_DEST, while the kptr source is
admitted only for bpf_kptr_xchg(). Drop the redundant helper-ID
whitelist, pointer-offset check, and constant-offset assertion.
The bpf_kptr_xchg() source match now follows offset validation, so a
source within a referenced object reports the release zero-offset error
before the kptr type error. Update the affected selftests and use
call-neutral wording for BTF mismatch diagnostics.
Signed-off-by: Amery Hung <ameryhung@gmail.com>
---
kernel/bpf/verifier.c | 220 +++++++-----------
.../testing/selftests/bpf/prog_tests/bpf_nf.c | 14 +-
.../selftests/bpf/progs/map_kptr_fail.c | 2 +-
.../selftests/bpf/progs/verifier_vfs_reject.c | 2 +-
tools/testing/selftests/bpf/verifier/calls.c | 2 +-
.../testing/selftests/bpf/verifier/map_kptr.c | 2 +-
6 files changed, 96 insertions(+), 146 deletions(-)
diff --git a/kernel/bpf/verifier.c b/kernel/bpf/verifier.c
index 09a51d67ab7e..bd3fea62aa02 100644
--- a/kernel/bpf/verifier.c
+++ b/kernel/bpf/verifier.c
@@ -8203,6 +8203,10 @@ static int resolve_func_arg_type(struct bpf_verifier_env *env,
struct bpf_reg_state *reg, u32 arg,
struct bpf_call_arg_meta *meta, int insn_idx,
enum bpf_arg_type *arg_type, u32 *arg_size);
+static int process_arg_ptr_to_btf_id(struct bpf_verifier_env *env, struct bpf_reg_state *reg,
+ argno_t argno, enum bpf_arg_type arg_type,
+ const struct btf *arg_btf, u32 arg_btf_id,
+ struct bpf_call_arg_meta *meta, int insn_idx);
struct bpf_reg_types {
const enum bpf_reg_type types[10];
@@ -8404,13 +8408,12 @@ static const char *bpf_diag_expected_reg_types(struct bpf_verifier_env *env,
}
static int check_reg_type(struct bpf_verifier_env *env, struct bpf_reg_state *reg, argno_t argno,
- enum bpf_arg_type arg_type, const u32 *arg_btf_id,
- struct bpf_call_arg_meta *meta)
+ enum bpf_arg_type arg_type, struct bpf_call_arg_meta *meta)
{
enum bpf_reg_type expected, type = reg->type;
const struct bpf_reg_types *compatible;
const char *actual, *accepted;
- int i, j, err;
+ int i, j;
compatible = compatible_reg_types[base_type(arg_type)];
if (!compatible) {
@@ -8466,90 +8469,6 @@ static int check_reg_type(struct bpf_verifier_env *env, struct bpf_reg_state *re
return -EACCES;
found:
- if (base_type(reg->type) != PTR_TO_BTF_ID)
- return 0;
-
- if (compatible == &mem_types)
- return 0;
-
- switch ((int)reg->type) {
- case PTR_TO_BTF_ID:
- case PTR_TO_BTF_ID | PTR_TRUSTED:
- case PTR_TO_BTF_ID | PTR_TRUSTED | PTR_MAYBE_NULL:
- case PTR_TO_BTF_ID | MEM_RCU:
- case PTR_TO_BTF_ID | PTR_MAYBE_NULL:
- case PTR_TO_BTF_ID | PTR_MAYBE_NULL | MEM_RCU:
- {
- /* For bpf_sk_release, it needs to match against first member
- * 'struct sock_common', hence make an exception for it. This
- * allows bpf_sk_release to work for multiple socket types.
- */
- bool strict_type_match = arg_type_is_release(arg_type) &&
- !is_helper_call(meta, BPF_FUNC_sk_release);
-
- if (!arg_btf_id) {
- if (!compatible->btf_id) {
- verifier_bug(env, "missing arg compatible BTF ID");
- return -EFAULT;
- }
- arg_btf_id = compatible->btf_id;
- }
-
- if (is_helper_call(meta, BPF_FUNC_kptr_xchg)) {
- if (map_kptr_match_type(env, meta->kptr_field, reg, reg_from_argno(argno)))
- return -EACCES;
- } else {
- if (arg_btf_id == BPF_PTR_POISON) {
- verbose(env, "verifier internal error:");
- verbose(env, "%s has non-overwritten BPF_PTR_POISON type\n",
- reg_arg_name(env, argno));
- return -EACCES;
- }
-
- err = __check_ptr_off_reg(env, reg, argno, true);
- if (err)
- return err;
-
- if (!btf_struct_ids_match(&env->log, reg->btf, reg->btf_id,
- reg->var_off.value, btf_vmlinux, *arg_btf_id,
- strict_type_match, !type_is_alloc(reg->type))) {
- verbose(env, "%s is of type %s but %s is expected\n",
- reg_arg_name(env, argno),
- btf_type_name(reg->btf, reg->btf_id),
- btf_type_name(btf_vmlinux, *arg_btf_id));
- return -EACCES;
- }
- }
- break;
- }
- case PTR_TO_BTF_ID | MEM_ALLOC:
- case PTR_TO_BTF_ID | MEM_PERCPU | MEM_ALLOC:
- case PTR_TO_BTF_ID | MEM_ALLOC | NON_OWN_REF:
- case PTR_TO_BTF_ID | MEM_ALLOC | NON_OWN_REF | MEM_RCU:
- if (!is_helper_call(meta, BPF_FUNC_spin_lock) &&
- !is_helper_call(meta, BPF_FUNC_spin_unlock) &&
- !is_helper_call(meta, BPF_FUNC_kptr_xchg)) {
- verifier_bug(env, "unimplemented handling of MEM_ALLOC");
- return -EFAULT;
- }
- /* Check if local kptr in src arg matches kptr in dst arg */
- if (is_helper_call(meta, BPF_FUNC_kptr_xchg)) {
- int regno = reg_from_argno(argno);
-
- if (regno == BPF_REG_2 &&
- map_kptr_match_type(env, meta->kptr_field, reg, regno))
- return -EACCES;
- }
- break;
- case PTR_TO_BTF_ID | MEM_PERCPU:
- case PTR_TO_BTF_ID | MEM_PERCPU | MEM_RCU:
- case PTR_TO_BTF_ID | MEM_PERCPU | PTR_TRUSTED:
- /* Handled by helper specific checks */
- break;
- default:
- verifier_bug(env, "invalid PTR_TO_BTF_ID register for type match");
- return -EFAULT;
- }
return 0;
}
@@ -8826,7 +8745,6 @@ static int check_func_arg(struct bpf_verifier_env *env, u32 arg,
enum bpf_arg_type arg_type = fn->arg_type[arg];
int regno = reg_from_argno(argno);
enum bpf_reg_type type = reg->type;
- const u32 *arg_btf_id = NULL;
u32 arg_size = arg_type & MEM_FIXED_SIZE ? fn->arg_size[arg] : 0;
u32 key_size;
int err = 0;
@@ -8871,12 +8789,7 @@ static int check_func_arg(struct bpf_verifier_env *env, u32 arg,
if (err)
return err;
- /* arg_btf_id and arg_size are in a union. */
- if (base_type(arg_type) == ARG_PTR_TO_BTF_ID ||
- base_type(arg_type) == ARG_PTR_TO_SPIN_LOCK)
- arg_btf_id = fn->arg_btf_id[arg];
-
- err = check_reg_type(env, reg, argno, arg_type, arg_btf_id, meta);
+ err = check_reg_type(env, reg, argno, arg_type, meta);
if (err)
return err;
@@ -8974,6 +8887,44 @@ static int check_func_arg(struct bpf_verifier_env *env, u32 arg,
arg_type & MEM_WRITE ? BPF_WRITE : BPF_READ,
false, meta, NULL);
break;
+ case ARG_PTR_TO_BTF_ID:
+ case ARG_PTR_TO_BTF_ID_SOCK_COMMON:
+ {
+ const u32 *arg_btf_id = fn->arg_btf_id[arg];
+
+ if (!meta->btf) {
+ const struct bpf_reg_types *compatible;
+
+ if (base_type(reg->type) != PTR_TO_BTF_ID)
+ break;
+
+ if (is_helper_call(meta, BPF_FUNC_kptr_xchg))
+ return map_kptr_match_type(env, meta->kptr_field, reg, regno) ?
+ -EACCES : 0;
+
+ if (!arg_btf_id) {
+ compatible = compatible_reg_types[base_type(arg_type)];
+ if (!compatible->btf_id) {
+ verifier_bug(env, "missing arg compatible BTF ID");
+ return -EFAULT;
+ }
+ arg_btf_id = compatible->btf_id;
+ }
+ if (arg_btf_id == BPF_PTR_POISON) {
+ verbose(env, "verifier internal error:");
+ verbose(env, "%s has non-overwritten BPF_PTR_POISON type\n",
+ reg_arg_name(env, argno));
+ return -EACCES;
+ }
+ }
+
+ err = process_arg_ptr_to_btf_id(env, reg, argno, arg_type,
+ btf_vmlinux, *arg_btf_id,
+ meta, insn_idx);
+ if (err < 0)
+ return err;
+ break;
+ }
case ARG_PTR_TO_PERCPU_BTF_ID:
if (!reg->btf_id) {
verbose(env, "Helper has invalid btf_id in %s\n",
@@ -9917,8 +9868,12 @@ static int btf_check_func_arg_match(struct bpf_verifier_env *env, int subprog,
if (bpf_register_is_null(reg) && type_may_be_null(arg->arg_type))
continue;
- err = check_reg_type(env, reg, argno, arg->arg_type, &arg->btf_id, &meta);
+ err = check_reg_type(env, reg, argno, arg->arg_type, &meta);
err = err ?: check_func_arg_reg_off(env, reg, argno, arg->arg_type);
+ if (!err && base_type(reg->type) == PTR_TO_BTF_ID)
+ err = process_arg_ptr_to_btf_id(env, reg, argno, arg->arg_type,
+ btf_vmlinux, arg->btf_id,
+ &meta, env->insn_idx);
if (err)
return err;
} else {
@@ -12415,32 +12370,28 @@ static int gen_kfunc_arg_proto(struct bpf_verifier_env *env, struct bpf_call_arg
return check_arg_prog_aux(env, proto) ? 0 : -EINVAL;
}
-static int process_kf_arg_ptr_to_btf_id(struct bpf_verifier_env *env,
- struct bpf_reg_state *reg,
- const struct btf_type *ref_t,
- const char *ref_tname, u32 ref_id,
- struct bpf_call_arg_meta *meta,
- int arg, argno_t argno)
+static int process_arg_ptr_to_btf_id(struct bpf_verifier_env *env, struct bpf_reg_state *reg,
+ argno_t argno, enum bpf_arg_type arg_type,
+ const struct btf *arg_btf, u32 arg_btf_id,
+ struct bpf_call_arg_meta *meta, int insn_idx)
{
- const struct btf_type *reg_ref_t;
- bool strict_type_match = false;
+ bool taking_projection, struct_same, strict_type_match = false;
+ const struct btf_type *arg_t, *reg_t;
+ const char *arg_tname, *reg_tname;
const struct btf *reg_btf;
- const char *reg_ref_tname;
- bool taking_projection;
- bool struct_same;
- u32 reg_ref_id;
+ u32 reg_btf_id;
if (base_type(reg->type) == PTR_TO_BTF_ID) {
reg_btf = reg->btf;
- reg_ref_id = reg->btf_id;
+ reg_btf_id = reg->btf_id;
} else {
reg_btf = btf_vmlinux;
- reg_ref_id = *reg2btf_ids[base_type(reg->type)];
+ reg_btf_id = *reg2btf_ids[base_type(reg->type)];
}
- /* Enforce strict type matching for calls to kfuncs that are acquiring
- * or releasing a reference, or are no-cast aliases. We do _not_
- * enforce strict matching for kfuncs by default,
+ /*
+ * Enforce strict type matching for arguments that release a reference,
+ * or are no-cast aliases. We do _not_ enforce strict matching by default,
* as we want to enable BPF programs to pass types that are bitwise
* equivalent without forcing them to explicitly cast with something
* like bpf_cast_to_kern_ctx().
@@ -12462,27 +12413,30 @@ static int process_kf_arg_ptr_to_btf_id(struct bpf_verifier_env *env,
* btf_struct_ids_match() to walk the struct at the 0th offset, and
* resolve types.
*/
- if ((is_kfunc_release(meta) && reg_is_referenced(env, reg)) ||
- btf_type_ids_nocast_alias(&env->log, reg_btf, reg_ref_id, meta->btf, ref_id))
+ if ((arg_type_is_release(arg_type) && !is_helper_call(meta, BPF_FUNC_sk_release)) ||
+ (meta->btf && btf_type_ids_nocast_alias(&env->log, reg_btf, reg_btf_id,
+ arg_btf, arg_btf_id)))
strict_type_match = true;
- WARN_ON_ONCE(is_kfunc_release(meta) && !tnum_is_const(reg->var_off));
+ arg_t = btf_type_skip_modifiers(arg_btf, arg_btf_id, &arg_btf_id);
+ arg_tname = btf_name_by_offset(arg_btf, arg_t->name_off);
+ reg_t = btf_type_skip_modifiers(reg_btf, reg_btf_id, ®_btf_id);
+ reg_tname = btf_name_by_offset(reg_btf, reg_t->name_off);
+
+ struct_same = btf_struct_ids_match(&env->log, reg_btf, reg_btf_id,
+ reg->var_off.value, arg_btf, arg_btf_id,
+ strict_type_match, !type_is_alloc(reg->type));
- reg_ref_t = btf_type_skip_modifiers(reg_btf, reg_ref_id, ®_ref_id);
- reg_ref_tname = btf_name_by_offset(reg_btf, reg_ref_t->name_off);
- struct_same = btf_struct_ids_match(&env->log, reg_btf, reg_ref_id, reg->var_off.value,
- meta->btf, ref_id, strict_type_match,
- !type_is_alloc(reg->type));
/* If kfunc is accepting a projection type (ie. __sk_buff), it cannot
* actually use it -- it must cast to the underlying type. So we allow
* caller to pass in the underlying type.
*/
- taking_projection = btf_is_projection_of(ref_tname, reg_ref_tname);
+ taking_projection = meta->btf && btf_is_projection_of(arg_tname, reg_tname);
if (!taking_projection && !struct_same) {
- verbose(env, "kernel function %s %s expected pointer to %s %s but %s has a pointer to %s %s\n",
+ verbose(env, "%s %s expected pointer to %s %s but %s has a pointer to %s %s\n",
meta->func_name, reg_arg_name(env, argno),
- btf_type_str(ref_t), ref_tname, reg_arg_name(env, argno),
- btf_type_str(reg_ref_t), reg_ref_tname);
+ btf_type_str(arg_t), arg_tname,
+ reg_arg_name(env, argno), btf_type_str(reg_t), reg_tname);
return -EINVAL;
}
return 0;
@@ -12980,7 +12934,7 @@ static bool check_css_task_iter_allowlist(struct bpf_verifier_env *env)
static int check_kfunc_args(struct bpf_verifier_env *env, struct bpf_call_arg_meta *meta,
int insn_idx)
{
- const char *func_name = meta->func_name, *ref_tname;
+ const char *func_name = meta->func_name;
struct bpf_func_state *caller = cur_func(env);
struct bpf_reg_state *regs = cur_regs(env);
const struct btf *btf = meta->btf;
@@ -13002,7 +12956,7 @@ static int check_kfunc_args(struct bpf_verifier_env *env, struct bpf_call_arg_me
for (i = 0; i < nargs; i++) {
struct bpf_reg_state *reg = get_func_arg_reg(caller, regs, i);
enum bpf_arg_type arg_type = meta->fn->arg_type[i];
- const struct btf_type *t, *ref_t;
+ const struct btf_type *t;
argno_t argno = argno_from_arg(i + 1);
int regno = reg_from_argno(argno);
u32 ref_id = args[i].type;
@@ -13018,10 +12972,8 @@ static int check_kfunc_args(struct bpf_verifier_env *env, struct bpf_call_arg_me
t = btf_type_skip_modifiers(btf, args[i].type, NULL);
- if (btf_type_is_ptr(t)) {
- ref_t = btf_type_skip_modifiers(btf, t->type, &ref_id);
- ref_tname = btf_name_by_offset(btf, ref_t->name_off);
- }
+ if (btf_type_is_ptr(t))
+ btf_type_skip_modifiers(btf, t->type, &ref_id);
ret = check_func_arg_nullability(env, reg, argno, arg_type, meta, insn_idx);
if (ret < 0)
@@ -13047,10 +12999,8 @@ static int check_kfunc_args(struct bpf_verifier_env *env, struct bpf_call_arg_me
if (bpf_register_is_null(reg) && type_may_be_null(arg_type))
continue;
- if (base_type(arg_type) == ARG_PTR_TO_BTF_ID) {
- /* gen_kfunc_arg_proto() resolved the expected BTF ID once. */
+ if (base_type(arg_type) == ARG_PTR_TO_BTF_ID)
ref_id = *meta->fn->arg_btf_id[i];
- }
ret = resolve_func_arg_type(env, reg, i, meta, insn_idx, &arg_type, &arg_size);
if (ret < 0)
@@ -13335,8 +13285,8 @@ static int check_kfunc_args(struct bpf_verifier_env *env, struct bpf_call_arg_me
}
}
- ret = process_kf_arg_ptr_to_btf_id(env, reg, ref_t, ref_tname,
- ref_id, meta, i, argno);
+ ret = process_arg_ptr_to_btf_id(env, reg, argno, arg_type, btf,
+ ref_id, meta, insn_idx);
if (ret < 0)
return ret;
break;
diff --git a/tools/testing/selftests/bpf/prog_tests/bpf_nf.c b/tools/testing/selftests/bpf/prog_tests/bpf_nf.c
index 14d4c1793aed..d74a9db54c9a 100644
--- a/tools/testing/selftests/bpf/prog_tests/bpf_nf.c
+++ b/tools/testing/selftests/bpf/prog_tests/bpf_nf.c
@@ -13,13 +13,13 @@ struct {
const char *prog_name;
const char *err_msg;
} test_bpf_nf_fail_tests[] = {
- { "alloc_release", "kernel function bpf_ct_release R1 expected pointer to STRUCT nf_conn but" },
- { "insert_insert", "kernel function bpf_ct_insert_entry R1 expected pointer to STRUCT nf_conn___init but" },
- { "lookup_insert", "kernel function bpf_ct_insert_entry R1 expected pointer to STRUCT nf_conn___init but" },
- { "set_timeout_after_insert", "kernel function bpf_ct_set_timeout R1 expected pointer to STRUCT nf_conn___init but" },
- { "set_status_after_insert", "kernel function bpf_ct_set_status R1 expected pointer to STRUCT nf_conn___init but" },
- { "change_timeout_after_alloc", "kernel function bpf_ct_change_timeout R1 expected pointer to STRUCT nf_conn but" },
- { "change_status_after_alloc", "kernel function bpf_ct_change_status R1 expected pointer to STRUCT nf_conn but" },
+ { "alloc_release", "bpf_ct_release R1 expected pointer to STRUCT nf_conn but" },
+ { "insert_insert", "bpf_ct_insert_entry R1 expected pointer to STRUCT nf_conn___init but" },
+ { "lookup_insert", "bpf_ct_insert_entry R1 expected pointer to STRUCT nf_conn___init but" },
+ { "set_timeout_after_insert", "bpf_ct_set_timeout R1 expected pointer to STRUCT nf_conn___init but" },
+ { "set_status_after_insert", "bpf_ct_set_status R1 expected pointer to STRUCT nf_conn___init but" },
+ { "change_timeout_after_alloc", "bpf_ct_change_timeout R1 expected pointer to STRUCT nf_conn but" },
+ { "change_status_after_alloc", "bpf_ct_change_status R1 expected pointer to STRUCT nf_conn but" },
{ "write_not_allowlisted_field", "no write support to nf_conn at off" },
{ "lookup_null_bpf_tuple", "Possibly NULL pointer passed to trusted R2" },
{ "lookup_null_bpf_opts", "Possibly NULL pointer passed to trusted R4" },
diff --git a/tools/testing/selftests/bpf/progs/map_kptr_fail.c b/tools/testing/selftests/bpf/progs/map_kptr_fail.c
index 60c14e185856..d1ff9e7e87e3 100644
--- a/tools/testing/selftests/bpf/progs/map_kptr_fail.c
+++ b/tools/testing/selftests/bpf/progs/map_kptr_fail.c
@@ -291,7 +291,7 @@ int reject_bad_type_xchg(struct __sk_buff *ctx)
}
SEC("?tc")
-__failure __msg("invalid kptr access, R2 type=trusted_ptr_prog_test_ref_kfunc")
+__failure __msg("R2 must have zero offset when passed to release func")
int reject_member_of_ref_xchg(struct __sk_buff *ctx)
{
struct prog_test_ref_kfunc *ref_ptr;
diff --git a/tools/testing/selftests/bpf/progs/verifier_vfs_reject.c b/tools/testing/selftests/bpf/progs/verifier_vfs_reject.c
index 8f0c45421f89..ff08aa75d6f7 100644
--- a/tools/testing/selftests/bpf/progs/verifier_vfs_reject.c
+++ b/tools/testing/selftests/bpf/progs/verifier_vfs_reject.c
@@ -128,7 +128,7 @@ int BPF_PROG(path_d_path_kfunc_untrusted_from_current)
}
SEC("lsm.s/file_open")
-__failure __msg("kernel function bpf_path_d_path R1 expected pointer to STRUCT path but R1 has a pointer to STRUCT file")
+__failure __msg("bpf_path_d_path R1 expected pointer to STRUCT path but R1 has a pointer to STRUCT file")
int BPF_PROG(path_d_path_kfunc_type_mismatch, struct file *file)
{
bpf_path_d_path((struct path *)&file->f_task_work, buf, sizeof(buf));
diff --git a/tools/testing/selftests/bpf/verifier/calls.c b/tools/testing/selftests/bpf/verifier/calls.c
index eb6e3baef412..d730215e520b 100644
--- a/tools/testing/selftests/bpf/verifier/calls.c
+++ b/tools/testing/selftests/bpf/verifier/calls.c
@@ -152,7 +152,7 @@
},
.prog_type = BPF_PROG_TYPE_SCHED_CLS,
.result = REJECT,
- .errstr = "kernel function bpf_kfunc_call_memb1_release R1 expected pointer",
+ .errstr = "bpf_kfunc_call_memb1_release R1 expected pointer",
.fixup_kfunc_btf_id = {
{ "bpf_kfunc_call_memb_acquire", 1 },
{ "bpf_kfunc_call_memb1_release", 5 },
diff --git a/tools/testing/selftests/bpf/verifier/map_kptr.c b/tools/testing/selftests/bpf/verifier/map_kptr.c
index 1efaff296b7c..345cecc722a3 100644
--- a/tools/testing/selftests/bpf/verifier/map_kptr.c
+++ b/tools/testing/selftests/bpf/verifier/map_kptr.c
@@ -342,7 +342,7 @@
.prog_type = BPF_PROG_TYPE_SCHED_CLS,
.fixup_map_kptr = { 1 },
.result = REJECT,
- .errstr = "invalid kptr access, R2 type=ptr_prog_test_ref_kfunc expected=ptr_prog_test_member",
+ .errstr = "R2 must have zero offset when passed to release func",
},
{
"map_kptr: ref: reference state created and released on xchg",
--
2.52.0
^ permalink raw reply related [flat|nested] 54+ messages in thread
* [PATCH bpf-next v1 18/22] bpf: Admit kfunc argument registers through check_reg_type()
2026-09-05 22:00 [PATCH bpf-next v1 00/22] bpf: Unify helper and kfunc argument checks Amery Hung
` (16 preceding siblings ...)
2026-09-05 22:01 ` [PATCH bpf-next v1 17/22] bpf: Consolidate helper and kfunc PTR_TO_BTF_ID argument matching Amery Hung
@ 2026-09-05 22:01 ` Amery Hung
2026-09-05 23:23 ` bot+bpf-ci
2026-09-05 22:01 ` [PATCH bpf-next v1 19/22] selftests/bpf: Test kfunc packet memory direct writes Amery Hung
` (4 subsequent siblings)
22 siblings, 1 reply; 54+ messages in thread
From: Amery Hung @ 2026-09-05 22:01 UTC (permalink / raw)
To: bpf
Cc: alexei.starovoitov, andrii, daniel, eddyz87, memxor, ameryhung,
kernel-team
check_kfunc_args() open-codes exact register-type tests in most of its
per-argument cases, duplicating what compatible_reg_types[] already
expresses for helpers. This leaves two admission paths and prevents the
helper and kfunc loops from converging.
Runtime argument resolution now converts a scalar-struct BTF argument
to fixed-size memory before register admission. Give the remaining
kfunc-only argument kinds compatibility entries and run
check_reg_type() once before the per-kind switch.
For ARG_PTR_TO_BTF_ID, let check_reg_type() admit BTF-backed register
types and reject incompatible register classes with its standard
diagnostic. Remove the now-unused lookup_reg2btf_ids(). Exact BTF
identity and trust requirements remain checked later by
process_arg_ptr_to_btf_id(). ARG_IGNORE and ARG_PTR_TO_PROG_AUX remain
skipped because the verifier does not read those arguments from the
program.
Iterator arguments use the stack-pointer table. Graph nodes and
ARG_PTR_TO_REFCOUNTED_KPTR share a table for owning and borrowed
allocated-object forms; their switch cases retain the API-specific
ownership and BTF-record validation.
Rename timer_types to map_value_types now that ARG_PTR_TO_WORKQUEUE and
ARG_PTR_TO_TASK_WORK share it. Similarly, rename spin_lock_types to
map_value_or_alloc_obj_types because graph roots and resource spin
locks share its map-value-or-allocated-object admission.
Moving admission checks into check_reg_type() must not discard the
structured call-argument diagnostics emitted by the individual cases.
Add bpf_diag_arg_type_plain() alongside bpf_diag_reg_type_plain() and
use it to preserve the existing per-kind Pass suggestions where
available. Other argument kinds retain the generic suggestion. The
reason continues to report the actual register type and all accepted
register types.
Two behavior changes fall out of running admission first:
- ARG_CONST_MEM_SIZE reaches process_const_arg(), and through it
mark_chain_precision(), only after the register is known to be a
scalar. Passing a pointer as a __szk argument used to reach
backtrack_insn() with a non-scalar and trip the backtracking-misuse
verifier bug.
- ARG_CONST_MAP_PTR no longer needs its own type_may_be_null() test,
because check_reg_type() compares whole register types.
Every kfunc argument that is not explicitly ignored now passes through
check_reg_type(), followed by the common register-offset check in the
same order as a helper argument.
Signed-off-by: Amery Hung <ameryhung@gmail.com>
---
kernel/bpf/diagnostics.c | 29 +++
kernel/bpf/diagnostics.h | 1 +
kernel/bpf/verifier.c | 233 +++++-------------
.../selftests/bpf/prog_tests/kfunc_call.c | 2 +-
.../testing/selftests/bpf/progs/arena_kfunc.c | 2 +-
.../selftests/bpf/progs/cpumask_failure.c | 2 +-
tools/testing/selftests/bpf/progs/irq.c | 4 +-
tools/testing/selftests/bpf/progs/iters.c | 6 +-
.../selftests/bpf/progs/iters_testmod.c | 3 +-
.../bpf/progs/mem_rdonly_untrusted.c | 3 +-
.../testing/selftests/bpf/progs/rbtree_fail.c | 4 +-
.../bpf/progs/refcounted_kptr_fail.c | 4 +-
.../selftests/bpf/progs/res_spin_lock_fail.c | 2 +-
.../testing/selftests/bpf/progs/stream_fail.c | 2 +-
.../selftests/bpf/progs/task_work_fail.c | 2 +-
.../bpf/progs/test_kfunc_dynptr_param.c | 2 +-
.../testing/selftests/bpf/progs/wq_failures.c | 4 +-
tools/testing/selftests/bpf/verifier/calls.c | 2 +-
18 files changed, 108 insertions(+), 199 deletions(-)
diff --git a/kernel/bpf/diagnostics.c b/kernel/bpf/diagnostics.c
index b682fd2be443..c740ae070e61 100644
--- a/kernel/bpf/diagnostics.c
+++ b/kernel/bpf/diagnostics.c
@@ -960,6 +960,35 @@ const char *bpf_diag_reg_type_plain(struct bpf_verifier_env *env, enum bpf_reg_t
}
}
+const char *bpf_diag_arg_type_plain(enum bpf_arg_type type)
+{
+ switch (base_type(type)) {
+ case ARG_MEM_SIZE:
+ case ARG_CONST_MEM_SIZE:
+ return "an integer scalar length for this memory argument";
+ case ARG_PTR_TO_CTX:
+ return "the original program context pointer or preserve it before modifying registers";
+ case ARG_SCALAR:
+ case ARG_CONST_SCALAR:
+ case ARG_CONST_ALLOC_SIZE_OR_ZERO:
+ return "an integer scalar value for this argument, not a pointer or resource object";
+ case ARG_PTR_TO_CONST_STR:
+ return "a constant string pointer that the verifier recognizes, such as a string stored in a read-only map value";
+ case ARG_PTR_TO_DYNPTR:
+ return "the address of a stack dynptr object, or use a const dynptr pointer returned by the verifier-supported path";
+ case ARG_PTR_TO_ALLOC_BTF_ID:
+ return "a pointer returned by the matching BPF object allocation path";
+ case ARG_PTR_TO_REFCOUNTED_KPTR:
+ return "an owning or non-owning pointer to a BPF-managed object containing a bpf_refcount field";
+ case ARG_PTR_TO_ITER:
+ return "the address of a stack iterator object for iterator new, next, and destroy calls";
+ case ARG_PTR_TO_IRQ_FLAG:
+ return "the same stack slot used by bpf_local_irq_save() or bpf_res_spin_lock_irqsave()";
+ default:
+ return "a value with one of the accepted pointer or scalar types for this call";
+ }
+}
+
static const char *diag_arg_ordinal(int argno)
{
switch (argno) {
diff --git a/kernel/bpf/diagnostics.h b/kernel/bpf/diagnostics.h
index d1b79945008a..a4102fb049ec 100644
--- a/kernel/bpf/diagnostics.h
+++ b/kernel/bpf/diagnostics.h
@@ -51,6 +51,7 @@ const char *bpf_diag_vfmt(struct bpf_verifier_env *env, const char *fmt, va_list
const char *bpf_diag_fmt(struct bpf_verifier_env *env, const char *fmt, ...) __printf(2, 3);
const char *bpf_diag_fmt_btf_type(struct bpf_verifier_env *env, const struct btf *btf, u32 type_id);
const char *bpf_diag_reg_type_plain(struct bpf_verifier_env *env, enum bpf_reg_type type);
+const char *bpf_diag_arg_type_plain(enum bpf_arg_type type);
u64 bpf_diag_event_log_save(struct bpf_verifier_env *env);
void bpf_diag_event_log_restore(struct bpf_verifier_env *env, u64 log_pos);
u32 bpf_diag_irq_depth(const struct bpf_verifier_state *state);
diff --git a/kernel/bpf/verifier.c b/kernel/bpf/verifier.c
index bd3fea62aa02..cad49ea5fdfa 100644
--- a/kernel/bpf/verifier.c
+++ b/kernel/bpf/verifier.c
@@ -5134,18 +5134,6 @@ static u32 *reg2btf_ids[__BPF_REG_TYPE_MAX] = {
[CONST_PTR_TO_MAP] = btf_bpf_map_id,
};
-static enum bpf_reg_type lookup_reg2btf_ids(u32 ref_id)
-{
- enum bpf_reg_type type;
-
- for (type = 0; type < __BPF_REG_TYPE_MAX; type++) {
- if (reg2btf_ids[type] && *reg2btf_ids[type] == ref_id)
- return type;
- }
-
- return NOT_INIT;
-}
-
static bool is_trusted_reg(struct bpf_verifier_env *env, const struct bpf_reg_state *reg)
{
/* A referenced register is always trusted. */
@@ -8201,7 +8189,7 @@ static int resolve_map_arg_type(struct bpf_verifier_env *env,
static int resolve_func_arg_type(struct bpf_verifier_env *env,
struct bpf_reg_state *reg, u32 arg,
- struct bpf_call_arg_meta *meta, int insn_idx,
+ struct bpf_call_arg_meta *meta,
enum bpf_arg_type *arg_type, u32 *arg_size);
static int process_arg_ptr_to_btf_id(struct bpf_verifier_env *env, struct bpf_reg_state *reg,
argno_t argno, enum bpf_arg_type arg_type,
@@ -8251,7 +8239,7 @@ static const struct bpf_reg_types mem_types = {
},
};
-static const struct bpf_reg_types spin_lock_types = {
+static const struct bpf_reg_types map_value_or_alloc_obj_types = {
.types = {
PTR_TO_MAP_VALUE,
PTR_TO_BTF_ID | MEM_ALLOC,
@@ -8280,7 +8268,29 @@ static const struct bpf_reg_types percpu_btf_ptr_types = {
static const struct bpf_reg_types func_ptr_types = { .types = { PTR_TO_FUNC } };
static const struct bpf_reg_types stack_ptr_types = { .types = { PTR_TO_STACK } };
static const struct bpf_reg_types const_str_ptr_types = { .types = { PTR_TO_MAP_VALUE } };
-static const struct bpf_reg_types timer_types = { .types = { PTR_TO_MAP_VALUE } };
+static const struct bpf_reg_types map_value_types = { .types = { PTR_TO_MAP_VALUE } };
+static const struct bpf_reg_types arena_types = {
+ .types = {
+ PTR_TO_ARENA,
+ SCALAR_VALUE,
+ }
+};
+
+static const struct bpf_reg_types alloc_obj_types = {
+ .types = {
+ PTR_TO_BTF_ID | MEM_ALLOC,
+ PTR_TO_BTF_ID | MEM_ALLOC | MEM_PERCPU,
+ }
+};
+
+static const struct bpf_reg_types alloc_obj_or_non_own_ref_types = {
+ .types = {
+ PTR_TO_BTF_ID | MEM_ALLOC,
+ PTR_TO_BTF_ID | MEM_ALLOC | NON_OWN_REF,
+ PTR_TO_BTF_ID | MEM_ALLOC | NON_OWN_REF | MEM_RCU,
+ }
+};
+
static const struct bpf_reg_types kptr_xchg_dest_types = {
.types = {
PTR_TO_MAP_VALUE,
@@ -8310,16 +8320,32 @@ static const struct bpf_reg_types *compatible_reg_types[__BPF_ARG_TYPE_MAX] = {
#endif
[ARG_PTR_TO_SOCKET] = &fullsock_types,
[ARG_PTR_TO_BTF_ID] = &btf_ptr_types,
- [ARG_PTR_TO_SPIN_LOCK] = &spin_lock_types,
+ [ARG_PTR_TO_SPIN_LOCK] = &map_value_or_alloc_obj_types,
[ARG_PTR_TO_MEM] = &mem_types,
[ARG_PTR_TO_RINGBUF_MEM] = &ringbuf_mem_types,
[ARG_PTR_TO_PERCPU_BTF_ID] = &percpu_btf_ptr_types,
[ARG_PTR_TO_FUNC] = &func_ptr_types,
[ARG_PTR_TO_STACK] = &stack_ptr_types,
[ARG_PTR_TO_CONST_STR] = &const_str_ptr_types,
- [ARG_PTR_TO_TIMER] = &timer_types,
+ [ARG_PTR_TO_TIMER] = &map_value_types,
[ARG_KPTR_XCHG_DEST] = &kptr_xchg_dest_types,
[ARG_PTR_TO_DYNPTR] = &dynptr_types,
+ /* Argument kinds only a kfunc can have. */
+ [ARG_SCALAR] = &scalar_types,
+ [ARG_CONST_SCALAR] = &scalar_types,
+ [ARG_CONST_MEM_SIZE] = &scalar_types,
+ [ARG_PTR_TO_ALLOC_BTF_ID] = &alloc_obj_types,
+ [ARG_PTR_TO_REFCOUNTED_KPTR] = &alloc_obj_or_non_own_ref_types,
+ [ARG_PTR_TO_ITER] = &stack_ptr_types,
+ [ARG_PTR_TO_LIST_HEAD] = &map_value_or_alloc_obj_types,
+ [ARG_PTR_TO_LIST_NODE] = &alloc_obj_or_non_own_ref_types,
+ [ARG_PTR_TO_RB_ROOT] = &map_value_or_alloc_obj_types,
+ [ARG_PTR_TO_RB_NODE] = &alloc_obj_or_non_own_ref_types,
+ [ARG_PTR_TO_RES_SPIN_LOCK] = &map_value_or_alloc_obj_types,
+ [ARG_PTR_TO_WORKQUEUE] = &map_value_types,
+ [ARG_PTR_TO_TASK_WORK] = &map_value_types,
+ [ARG_PTR_TO_IRQ_FLAG] = &stack_ptr_types,
+ [ARG_PTR_TO_ARENA] = &arena_types,
};
static void bpf_diag_call_arg(struct bpf_verifier_env *env, u32 insn_idx, argno_t argno,
@@ -8420,6 +8446,9 @@ static int check_reg_type(struct bpf_verifier_env *env, struct bpf_reg_state *re
verifier_bug(env, "unsupported arg type %d", arg_type);
return -EFAULT;
}
+ if (meta->btf && base_type(arg_type) == ARG_PTR_TO_BTF_ID &&
+ (base_type(type) == PTR_TO_BTF_ID || reg2btf_ids[base_type(type)]))
+ goto found;
/* ARG_PTR_TO_MEM + RDONLY is compatible with PTR_TO_MEM and PTR_TO_MEM + RDONLY,
* but ARG_PTR_TO_MEM is compatible only with PTR_TO_MEM and NOT with PTR_TO_MEM + RDONLY
@@ -8463,7 +8492,7 @@ static int check_reg_type(struct bpf_verifier_env *env, struct bpf_reg_state *re
actual = bpf_diag_fmt(env, "%s", reg_type_str(env, reg->type));
accepted = bpf_diag_expected_reg_types(env, compatible->types, i);
bpf_diag_call_arg_fmt(env, env->insn_idx, argno, meta->func_name,
- "Pass a value with one of the accepted pointer or scalar types for this call.",
+ bpf_diag_fmt(env, "Pass %s.", bpf_diag_arg_type_plain(arg_type)),
"it has type %s, but this argument accepts %s",
actual, accepted);
return -EACCES;
@@ -8775,7 +8804,7 @@ static int check_func_arg(struct bpf_verifier_env *env, u32 arg,
return -EACCES;
}
- err = resolve_func_arg_type(env, reg, arg, meta, insn_idx, &arg_type, &arg_size);
+ err = resolve_func_arg_type(env, reg, arg, meta, &arg_type, &arg_size);
if (err)
return err;
@@ -11820,7 +11849,7 @@ static bool btf_type_is_scalar_struct(struct bpf_verifier_env *env,
static int resolve_func_arg_type(struct bpf_verifier_env *env,
struct bpf_reg_state *reg, u32 arg,
- struct bpf_call_arg_meta *meta, int insn_idx,
+ struct bpf_call_arg_meta *meta,
enum bpf_arg_type *arg_type, u32 *arg_size)
{
argno_t argno = argno_from_arg(arg + 1);
@@ -11845,24 +11874,8 @@ static int resolve_func_arg_type(struct bpf_verifier_env *env,
ref_t = btf_type_skip_modifiers(btf, ref_id, &ref_id);
ref_tname = btf_name_by_offset(btf, ref_t->name_off);
- if (!btf_type_is_scalar_struct(env, btf, ref_t)) {
- enum bpf_reg_type reg2btf_type = lookup_reg2btf_ids(ref_id);
- const char *expected_type;
-
- verbose(env, "%s is %s expected %s %s",
- reg_arg_name(env, argno), reg_type_str(env, reg->type),
- btf_type_str(ref_t), ref_tname);
- if (reg2btf_type != NOT_INIT)
- verbose(env, " or %s", reg_type_str(env, reg2btf_type));
- verbose(env, "\n");
- expected_type = bpf_diag_fmt_btf_type(env, btf, ref_id);
- bpf_diag_call_arg_fmt(env, insn_idx, argno, meta->func_name,
- "Pass a verifier-tracked pointer to the expected kernel object type, not a pointer to stack storage or another memory buffer.",
- "the function expects a pointer to %s, but this argument is %s and cannot be used as that kernel object pointer",
- expected_type,
- bpf_diag_reg_type_plain(env, reg->type));
- return -EINVAL;
- }
+ if (!btf_type_is_scalar_struct(env, btf, ref_t))
+ return 0;
resolve_ret = btf_resolve_size(btf, ref_t, arg_size);
if (IS_ERR(resolve_ret)) {
@@ -13002,25 +13015,20 @@ static int check_kfunc_args(struct bpf_verifier_env *env, struct bpf_call_arg_me
if (base_type(arg_type) == ARG_PTR_TO_BTF_ID)
ref_id = *meta->fn->arg_btf_id[i];
- ret = resolve_func_arg_type(env, reg, i, meta, insn_idx, &arg_type, &arg_size);
+ ret = resolve_func_arg_type(env, reg, i, meta, &arg_type, &arg_size);
if (ret < 0)
return ret;
+
+ ret = check_reg_type(env, reg, argno, arg_type, meta);
+ if (ret < 0)
+ return ret;
+
ret = check_func_arg_reg_off(env, reg, argno, arg_type);
if (ret < 0)
return ret;
switch (base_type(arg_type)) {
case ARG_CONST_SCALAR:
- if (reg->type != SCALAR_VALUE) {
- verbose(env, "%s is not a scalar\n", reg_arg_name(env, argno));
- bpf_diag_call_arg_fmt(env, insn_idx, argno, func_name,
- "Pass an integer scalar value for this argument, not a pointer or resource object.",
- "the kfunc expects an integer scalar, but %s is %s",
- reg_arg_name(env, argno),
- bpf_diag_reg_type_plain(env, reg->type));
- return -EINVAL;
- }
-
ret = process_const_arg(env, reg, argno, meta);
if (ret < 0) {
if (ret == -EINVAL)
@@ -13032,27 +13040,8 @@ static int check_kfunc_args(struct bpf_verifier_env *env, struct bpf_call_arg_me
}
break;
case ARG_SCALAR:
- if (reg->type != SCALAR_VALUE) {
- verbose(env, "%s is not a scalar\n", reg_arg_name(env, argno));
- bpf_diag_call_arg_fmt(env, insn_idx, argno, func_name,
- "Pass an integer scalar value for this argument, not a pointer or resource object.",
- "the kfunc expects an integer scalar, but %s is %s",
- reg_arg_name(env, argno),
- bpf_diag_reg_type_plain(env, reg->type));
- return -EINVAL;
- }
break;
case ARG_CONST_ALLOC_SIZE_OR_ZERO:
- if (reg->type != SCALAR_VALUE) {
- verbose(env, "%s is not a scalar\n", reg_arg_name(env, argno));
- bpf_diag_call_arg_fmt(env, insn_idx, argno, func_name,
- "Pass an integer scalar value for this argument, not a pointer or resource object.",
- "the kfunc expects an integer scalar, but %s is %s",
- reg_arg_name(env, argno),
- bpf_diag_reg_type_plain(env, reg->type));
- return -EINVAL;
- }
-
if (is_kfunc_arg_scalar_with_name(btf, &args[i], "rdonly_buf_size"))
meta->r0_rdonly = true;
ret = process_const_alloc_mem_size(env, reg, argno, &meta->ret_mem);
@@ -13066,17 +13055,6 @@ static int check_kfunc_args(struct bpf_verifier_env *env, struct bpf_call_arg_me
}
break;
case ARG_PTR_TO_CTX:
- if (reg->type != PTR_TO_CTX) {
- verbose(env, "%s expected pointer to ctx, but got %s\n",
- reg_arg_name(env, argno), reg_type_str(env, reg->type));
- bpf_diag_call_arg_fmt(env, insn_idx, argno, func_name,
- "Pass the original program context pointer or preserve it before modifying registers.",
- "the kfunc expects a context pointer, but %s is %s",
- reg_arg_name(env, argno),
- bpf_diag_reg_type_plain(env, reg->type));
- return -EINVAL;
- }
-
if (is_kfunc_call(meta, special_kfunc_list[KF_bpf_cast_to_kern_ctx])) {
ret = get_kern_ctx_btf_id(&env->log, resolve_prog_type(env->prog));
if (ret < 0)
@@ -13085,11 +13063,6 @@ static int check_kfunc_args(struct bpf_verifier_env *env, struct bpf_call_arg_me
}
break;
case ARG_PTR_TO_ARENA:
- if (reg->type != PTR_TO_ARENA && reg->type != SCALAR_VALUE) {
- verbose(env, "%s is not a pointer to arena or scalar\n",
- reg_arg_name(env, argno));
- return -EINVAL;
- }
break;
case ARG_PTR_TO_ALLOC_BTF_ID:
if (reg->type == (PTR_TO_BTF_ID | MEM_ALLOC)) {
@@ -13104,15 +13077,6 @@ static int check_kfunc_args(struct bpf_verifier_env *env, struct bpf_call_arg_me
reg_arg_name(env, argno));
return -EINVAL;
}
- } else {
- verbose(env, "%s expected pointer to allocated object\n",
- reg_arg_name(env, argno));
- bpf_diag_call_arg_fmt(env, insn_idx, argno, func_name,
- "Pass a pointer returned by the matching BPF object allocation path.",
- "the kfunc expects an allocated object pointer, but %s is %s",
- reg_arg_name(env, argno),
- bpf_diag_reg_type_plain(env, reg->type));
- return -EINVAL;
}
if (!reg_is_referenced(env, reg)) {
verbose(env, "allocated object must be referenced\n");
@@ -13161,12 +13125,6 @@ static int check_kfunc_args(struct bpf_verifier_env *env, struct bpf_call_arg_me
return ret;
break;
case ARG_PTR_TO_LIST_HEAD:
- if (reg->type != PTR_TO_MAP_VALUE &&
- reg->type != (PTR_TO_BTF_ID | MEM_ALLOC)) {
- verbose(env, "%s expected pointer to map value or allocated object\n",
- reg_arg_name(env, argno));
- return -EINVAL;
- }
if (reg->type == (PTR_TO_BTF_ID | MEM_ALLOC) &&
!reg_is_referenced(env, reg)) {
verbose(env, "allocated object must be referenced\n");
@@ -13177,12 +13135,6 @@ static int check_kfunc_args(struct bpf_verifier_env *env, struct bpf_call_arg_me
return ret;
break;
case ARG_PTR_TO_RB_ROOT:
- if (reg->type != PTR_TO_MAP_VALUE &&
- reg->type != (PTR_TO_BTF_ID | MEM_ALLOC)) {
- verbose(env, "%s expected pointer to map value or allocated object\n",
- reg_arg_name(env, argno));
- return -EINVAL;
- }
if (reg->type == (PTR_TO_BTF_ID | MEM_ALLOC) &&
!reg_is_referenced(env, reg)) {
verbose(env, "allocated object must be referenced\n");
@@ -13242,12 +13194,6 @@ static int check_kfunc_args(struct bpf_verifier_env *env, struct bpf_call_arg_me
return ret;
break;
case ARG_CONST_MAP_PTR:
- if (base_type(reg->type) != CONST_PTR_TO_MAP ||
- type_may_be_null(reg->type)) {
- verbose(env, "pointer in %s isn't map pointer\n",
- reg_arg_name(env, argno));
- return -EINVAL;
- }
ret = process_map_ptr_arg(env, reg, argno, meta);
if (ret < 0)
return ret;
@@ -13336,16 +13282,6 @@ static int check_kfunc_args(struct bpf_verifier_env *env, struct bpf_call_arg_me
argno_t buff_argno = argno_from_arg(i);
enum bpf_mem_size_failure failure;
- if (reg->type != SCALAR_VALUE) {
- verbose(env, "%s is not a scalar\n", reg_arg_name(env, argno));
- bpf_diag_call_arg_fmt(env, insn_idx, argno, func_name,
- "Pass an integer scalar length for this memory argument.",
- "the kfunc expects a scalar memory size, but %s is %s",
- reg_arg_name(env, argno),
- bpf_diag_reg_type_plain(env, reg->type));
- return -EINVAL;
- }
-
if (bpf_register_is_null(buff_reg))
break;
@@ -13383,23 +13319,9 @@ static int check_kfunc_args(struct bpf_verifier_env *env, struct bpf_call_arg_me
break;
}
case ARG_PTR_TO_FUNC:
- if (reg->type != PTR_TO_FUNC) {
- verbose(env, "%s expected pointer to func\n", reg_arg_name(env, argno));
- return -EINVAL;
- }
meta->subprogno = reg->subprogno;
break;
case ARG_PTR_TO_REFCOUNTED_KPTR:
- if (!type_is_ptr_alloc_obj(reg->type)) {
- verbose(env, "%s is neither owning or non-owning ref\n",
- reg_arg_name(env, argno));
- bpf_diag_call_arg_fmt(env, insn_idx, argno, func_name,
- "Pass an owning or non-owning pointer to a BPF-managed object containing a bpf_refcount field.",
- "the kfunc expects a pointer to a BPF-managed refcounted object, but %s is %s",
- reg_arg_name(env, argno),
- bpf_diag_reg_type_plain(env, reg->type));
- return -EINVAL;
- }
if (!type_is_non_owning_ref(reg->type))
meta->arg_owning_ref = true;
@@ -13419,61 +13341,26 @@ static int check_kfunc_args(struct bpf_verifier_env *env, struct bpf_call_arg_me
meta->arg_btf_id = reg->btf_id;
break;
case ARG_PTR_TO_CONST_STR:
- if (reg->type != PTR_TO_MAP_VALUE) {
- verbose(env, "%s doesn't point to a const string\n",
- reg_arg_name(env, argno));
- bpf_diag_call_arg_fmt(env, insn_idx, argno, func_name,
- "Pass a constant string pointer that the verifier recognizes, such as a string stored in a read-only map value.",
- "the kfunc expects a pointer to a constant string stored in verifier-known memory, but %s is %s",
- reg_arg_name(env, argno),
- bpf_diag_reg_type_plain(env, reg->type));
- return -EINVAL;
- }
ret = check_arg_const_str(env, reg, argno);
if (ret)
return ret;
break;
case ARG_PTR_TO_WORKQUEUE:
- if (reg->type != PTR_TO_MAP_VALUE) {
- verbose(env, "%s doesn't point to a map value\n",
- reg_arg_name(env, argno));
- return -EINVAL;
- }
ret = check_map_field_pointer(env, reg, argno, BPF_WORKQUEUE, &meta->map);
if (ret < 0)
return ret;
break;
case ARG_PTR_TO_TIMER:
- if (reg->type != PTR_TO_MAP_VALUE) {
- verbose(env, "%s doesn't point to a map value\n",
- reg_arg_name(env, argno));
- return -EINVAL;
- }
ret = process_timer_func(env, reg, argno, &meta->map);
if (ret < 0)
return ret;
break;
case ARG_PTR_TO_TASK_WORK:
- if (reg->type != PTR_TO_MAP_VALUE) {
- verbose(env, "%s doesn't point to a map value\n",
- reg_arg_name(env, argno));
- return -EINVAL;
- }
ret = check_map_field_pointer(env, reg, argno, BPF_TASK_WORK, &meta->map);
if (ret < 0)
return ret;
break;
case ARG_PTR_TO_IRQ_FLAG:
- if (reg->type != PTR_TO_STACK) {
- verbose(env, "%s doesn't point to an irq flag on stack\n",
- reg_arg_name(env, argno));
- bpf_diag_call_arg_fmt(env, insn_idx, argno, func_name,
- "Pass the same stack slot used by bpf_local_irq_save() or bpf_res_spin_lock_irqsave().",
- "the kfunc expects a stack pointer to an IRQ flag slot, but %s is %s",
- reg_arg_name(env, argno),
- bpf_diag_reg_type_plain(env, reg->type));
- return -EINVAL;
- }
ret = process_irq_flag(env, reg, argno, meta);
if (ret < 0)
return ret;
@@ -13482,12 +13369,6 @@ static int check_kfunc_args(struct bpf_verifier_env *env, struct bpf_call_arg_me
{
int flags = PROCESS_RES_LOCK;
- if (reg->type != PTR_TO_MAP_VALUE && reg->type != (PTR_TO_BTF_ID | MEM_ALLOC)) {
- verbose(env, "%s doesn't point to map value or allocated object\n",
- reg_arg_name(env, argno));
- return -EINVAL;
- }
-
if (!is_bpf_res_spin_lock_kfunc(meta->func_id))
return -EFAULT;
if (is_kfunc_call(meta, special_kfunc_list[KF_bpf_res_spin_lock]) ||
diff --git a/tools/testing/selftests/bpf/prog_tests/kfunc_call.c b/tools/testing/selftests/bpf/prog_tests/kfunc_call.c
index 2b39cc1b09f9..0063e60d6f2f 100644
--- a/tools/testing/selftests/bpf/prog_tests/kfunc_call.c
+++ b/tools/testing/selftests/bpf/prog_tests/kfunc_call.c
@@ -70,7 +70,7 @@ static struct kfunc_test_params kfunc_tests[] = {
TC_FAIL(kfunc_call_test_get_mem_fail_oversized, 0, "allocation size exceeds u32 max"),
TC_FAIL(kfunc_call_test_get_mem_fail_not_const, 0, "is not a const"),
TC_FAIL(kfunc_call_test_mem_acquire_fail, 0, "acquire kernel function does not return PTR_TO_BTF_ID"),
- TC_FAIL(kfunc_call_test_pointer_arg_type_mismatch, 0, "R1 expected pointer to ctx, but got scalar"),
+ TC_FAIL(kfunc_call_test_pointer_arg_type_mismatch, 0, "R1 type=scalar expected=ctx"),
TC_FAIL(kfunc_call_test_spin_lock_unsafe, 0, "function calls are not allowed while holding a lock"),
/* success cases */
diff --git a/tools/testing/selftests/bpf/progs/arena_kfunc.c b/tools/testing/selftests/bpf/progs/arena_kfunc.c
index 50609f3b0564..6578cf12fa27 100644
--- a/tools/testing/selftests/bpf/progs/arena_kfunc.c
+++ b/tools/testing/selftests/bpf/progs/arena_kfunc.c
@@ -205,7 +205,7 @@ int arena_arg_no_arena(void *ctx)
SEC("syscall")
__arch_x86_64
__arch_arm64
-__failure __msg("is not a pointer to arena or scalar")
+__failure __msg("R1 type=fp expected=arena, scalar")
int arena_arg_bad_reg(void *ctx)
{
u64 buf = 0;
diff --git a/tools/testing/selftests/bpf/progs/cpumask_failure.c b/tools/testing/selftests/bpf/progs/cpumask_failure.c
index 6d730535eb95..76a3cba6f23c 100644
--- a/tools/testing/selftests/bpf/progs/cpumask_failure.c
+++ b/tools/testing/selftests/bpf/progs/cpumask_failure.c
@@ -243,7 +243,7 @@ int BPF_PROG(test_populate_invalid_destination, struct task_struct *task, u64 cl
}
SEC("tp_btf/task_newtask")
-__failure __msg("leads to invalid memory access")
+__failure __msg("R2 type=scalar expected=fp")
int BPF_PROG(test_populate_invalid_source, struct task_struct *task, u64 clone_flags)
{
void *garbage = (void *)0x123456;
diff --git a/tools/testing/selftests/bpf/progs/irq.c b/tools/testing/selftests/bpf/progs/irq.c
index a4a007866a33..53df6d248e26 100644
--- a/tools/testing/selftests/bpf/progs/irq.c
+++ b/tools/testing/selftests/bpf/progs/irq.c
@@ -15,7 +15,7 @@ struct bpf_res_spin_lock lockA __hidden SEC(".data.A");
struct bpf_res_spin_lock lockB __hidden SEC(".data.B");
SEC("?tc")
-__failure __msg("R1 doesn't point to an irq flag on stack")
+__failure __msg("R1 type=map_value expected=fp")
int irq_save_bad_arg(struct __sk_buff *ctx)
{
bpf_local_irq_save(&global_flags);
@@ -23,7 +23,7 @@ int irq_save_bad_arg(struct __sk_buff *ctx)
}
SEC("?tc")
-__failure __msg("R1 doesn't point to an irq flag on stack")
+__failure __msg("R1 type=map_value expected=fp")
int irq_restore_bad_arg(struct __sk_buff *ctx)
{
bpf_local_irq_restore(&global_flags);
diff --git a/tools/testing/selftests/bpf/progs/iters.c b/tools/testing/selftests/bpf/progs/iters.c
index 62d7df9e80be..e4d099c533aa 100644
--- a/tools/testing/selftests/bpf/progs/iters.c
+++ b/tools/testing/selftests/bpf/progs/iters.c
@@ -1688,7 +1688,7 @@ int iter_subprog_check_stacksafe(const void *ctx)
struct bpf_iter_num global_it;
SEC("raw_tp")
-__failure __msg("R1 expected pointer to an iterator on stack")
+__failure __msg("R1 type=map_value expected=fp")
int iter_new_bad_arg(const void *ctx)
{
bpf_iter_num_new(&global_it, 0, 1);
@@ -1696,7 +1696,7 @@ int iter_new_bad_arg(const void *ctx)
}
SEC("raw_tp")
-__failure __msg("R1 expected pointer to an iterator on stack")
+__failure __msg("R1 type=map_value expected=fp")
int iter_next_bad_arg(const void *ctx)
{
bpf_iter_num_next(&global_it);
@@ -1704,7 +1704,7 @@ int iter_next_bad_arg(const void *ctx)
}
SEC("raw_tp")
-__failure __msg("R1 expected pointer to an iterator on stack")
+__failure __msg("R1 type=map_value expected=fp")
int iter_destroy_bad_arg(const void *ctx)
{
bpf_iter_num_destroy(&global_it);
diff --git a/tools/testing/selftests/bpf/progs/iters_testmod.c b/tools/testing/selftests/bpf/progs/iters_testmod.c
index 5a3ff65e8234..f65cc9766633 100644
--- a/tools/testing/selftests/bpf/progs/iters_testmod.c
+++ b/tools/testing/selftests/bpf/progs/iters_testmod.c
@@ -105,8 +105,7 @@ int iter_next_rcu_not_trusted(const void *ctx)
}
SEC("raw_tp/sys_enter")
-__failure __msg("R1 cannot write into rdonly_mem")
-/* Message should not be 'R1 cannot write into rdonly_trusted_mem' */
+__failure __msg("R1 type=rdonly_mem expected=fp")
int iter_next_ptr_mem_not_trusted(const void *ctx)
{
struct bpf_iter_num num_it;
diff --git a/tools/testing/selftests/bpf/progs/mem_rdonly_untrusted.c b/tools/testing/selftests/bpf/progs/mem_rdonly_untrusted.c
index 3e0d4f687aaa..23019023511a 100644
--- a/tools/testing/selftests/bpf/progs/mem_rdonly_untrusted.c
+++ b/tools/testing/selftests/bpf/progs/mem_rdonly_untrusted.c
@@ -118,8 +118,7 @@ int atomic_rmw_not_ok(void *ctx)
SEC("socket")
__failure
-__msg("invalid access to memory, mem_size=0 off=0 size=4")
-__msg("R1 min value is outside of the allowed memory range")
+__msg("R1 type=rdonly_untrusted_mem expected=fp")
int kfunc_param_not_ok(void *ctx)
{
int *p;
diff --git a/tools/testing/selftests/bpf/progs/rbtree_fail.c b/tools/testing/selftests/bpf/progs/rbtree_fail.c
index 555379952dcc..80494e00ede3 100644
--- a/tools/testing/selftests/bpf/progs/rbtree_fail.c
+++ b/tools/testing/selftests/bpf/progs/rbtree_fail.c
@@ -179,7 +179,7 @@ long rbtree_api_use_unchecked_remove_retval(void *ctx)
}
SEC("?tc")
-__failure __msg("bpf_rbtree_remove can only take non-owning or refcounted bpf_rb_node pointer")
+__failure __msg("R2 type=scalar expected=ptr_, ptr_, rcu_ptr_")
long rbtree_api_add_release_unlock_escape(void *ctx)
{
struct node_data *n;
@@ -203,7 +203,7 @@ long rbtree_api_add_release_unlock_escape(void *ctx)
}
SEC("?tc")
-__failure __msg("bpf_rbtree_remove can only take non-owning or refcounted bpf_rb_node pointer")
+__failure __msg("R2 type=scalar expected=ptr_, ptr_, rcu_ptr_")
long rbtree_api_first_release_unlock_escape(void *ctx)
{
struct bpf_rb_node *res;
diff --git a/tools/testing/selftests/bpf/progs/refcounted_kptr_fail.c b/tools/testing/selftests/bpf/progs/refcounted_kptr_fail.c
index eaaed0859f94..8a98e901382d 100644
--- a/tools/testing/selftests/bpf/progs/refcounted_kptr_fail.c
+++ b/tools/testing/selftests/bpf/progs/refcounted_kptr_fail.c
@@ -82,8 +82,8 @@ long refcount_acquire_maybe_null(void *ctx)
}
SEC("?tc")
-__failure __msg("R1 is neither owning or non-owning ref")
-__msg("expects a pointer to a BPF-managed refcounted object, but R1 is a context pointer")
+__failure __msg("R1 type=ctx expected=ptr_, ptr_, rcu_ptr_")
+__msg("type ctx, but this argument accepts ptr_, ptr_, rcu_ptr_")
long refcount_acquire_non_object(void *ctx)
{
return bpf_refcount_acquire(ctx) != NULL;
diff --git a/tools/testing/selftests/bpf/progs/res_spin_lock_fail.c b/tools/testing/selftests/bpf/progs/res_spin_lock_fail.c
index 330682a88c16..8fd591bd1f6c 100644
--- a/tools/testing/selftests/bpf/progs/res_spin_lock_fail.c
+++ b/tools/testing/selftests/bpf/progs/res_spin_lock_fail.c
@@ -24,7 +24,7 @@ struct bpf_spin_lock lock __hidden SEC(".data.A");
struct bpf_res_spin_lock res_lock __hidden SEC(".data.B");
SEC("?tc")
-__failure __msg("point to map value or allocated object")
+__failure __msg("R1 type=untrusted_ptr_ expected=map_value, ptr_")
int res_spin_lock_arg(struct __sk_buff *ctx)
{
struct arr_elem *elem;
diff --git a/tools/testing/selftests/bpf/progs/stream_fail.c b/tools/testing/selftests/bpf/progs/stream_fail.c
index 21428bb1ee59..10ebb4a7f105 100644
--- a/tools/testing/selftests/bpf/progs/stream_fail.c
+++ b/tools/testing/selftests/bpf/progs/stream_fail.c
@@ -23,7 +23,7 @@ int stream_vprintk_scalar_arg(void *ctx)
}
SEC("syscall")
-__failure __msg("R2 doesn't point to a const string")
+__failure __msg("R2 type=ctx expected=map_value")
int stream_vprintk_string_arg(void *ctx)
{
bpf_stream_vprintk(BPF_STDOUT, ctx, NULL, 0);
diff --git a/tools/testing/selftests/bpf/progs/task_work_fail.c b/tools/testing/selftests/bpf/progs/task_work_fail.c
index 3186e7b4b24e..bc56bdaca780 100644
--- a/tools/testing/selftests/bpf/progs/task_work_fail.c
+++ b/tools/testing/selftests/bpf/progs/task_work_fail.c
@@ -58,7 +58,7 @@ int mismatch_map(struct pt_regs *args)
}
SEC("perf_event")
-__failure __msg("R2 doesn't point to a map value")
+__failure __msg("R2 type=fp expected=map_value")
int no_map_task_work(struct pt_regs *args)
{
struct task_struct *task;
diff --git a/tools/testing/selftests/bpf/progs/test_kfunc_dynptr_param.c b/tools/testing/selftests/bpf/progs/test_kfunc_dynptr_param.c
index bf48fc43c7ab..f7a83e502454 100644
--- a/tools/testing/selftests/bpf/progs/test_kfunc_dynptr_param.c
+++ b/tools/testing/selftests/bpf/progs/test_kfunc_dynptr_param.c
@@ -40,7 +40,7 @@ int BPF_PROG(not_valid_dynptr, int cmd, union bpf_attr *attr, unsigned int size,
}
SEC("?lsm.s/bpf")
-__failure __msg("R1 expected pointer to stack or const struct bpf_dynptr")
+__failure __msg("R1 type=map_value expected=fp, dynptr_ptr")
int BPF_PROG(not_ptr_to_stack, int cmd, union bpf_attr *attr, unsigned int size, bool kernel)
{
static struct bpf_dynptr val;
diff --git a/tools/testing/selftests/bpf/progs/wq_failures.c b/tools/testing/selftests/bpf/progs/wq_failures.c
index 32dc8827e128..bd30217579d4 100644
--- a/tools/testing/selftests/bpf/progs/wq_failures.c
+++ b/tools/testing/selftests/bpf/progs/wq_failures.c
@@ -48,7 +48,7 @@ __log_level(2)
__flag(BPF_F_TEST_STATE_FREQ)
__failure
__msg(": (85) call bpf_wq_init#") /* anchor message */
-__msg("pointer in R2 isn't map pointer")
+__msg("R2 type=fp expected=map_ptr")
long test_wq_init_nomap(void *ctx)
{
struct bpf_wq *wq;
@@ -98,7 +98,7 @@ __failure
* is a correct bpf_wq pointer.
*/
__msg(": (85) call bpf_wq_set_callback#") /* anchor message */
-__msg("R1 doesn't point to a map value")
+__msg("R1 type=fp expected=map_value")
long test_wrong_wq_pointer(void *ctx)
{
int key = 0;
diff --git a/tools/testing/selftests/bpf/verifier/calls.c b/tools/testing/selftests/bpf/verifier/calls.c
index d730215e520b..0930ef862697 100644
--- a/tools/testing/selftests/bpf/verifier/calls.c
+++ b/tools/testing/selftests/bpf/verifier/calls.c
@@ -76,7 +76,7 @@
},
.prog_type = BPF_PROG_TYPE_SCHED_CLS,
.result = REJECT,
- .errstr = "R1 expected pointer to ctx, but got fp",
+ .errstr = "R1 type=fp expected=ctx",
.fixup_kfunc_btf_id = {
{ "bpf_kfunc_call_test_pass_ctx", 2 },
},
--
2.52.0
^ permalink raw reply related [flat|nested] 54+ messages in thread
* [PATCH bpf-next v1 19/22] selftests/bpf: Test kfunc packet memory direct writes
2026-09-05 22:00 [PATCH bpf-next v1 00/22] bpf: Unify helper and kfunc argument checks Amery Hung
` (17 preceding siblings ...)
2026-09-05 22:01 ` [PATCH bpf-next v1 18/22] bpf: Admit kfunc argument registers through check_reg_type() Amery Hung
@ 2026-09-05 22:01 ` Amery Hung
2026-09-05 22:44 ` bot+bpf-ci
2026-09-05 22:01 ` [PATCH bpf-next v1 20/22] bpf: Consolidate function call pkt_access validation Amery Hung
` (3 subsequent siblings)
22 siblings, 1 reply; 54+ messages in thread
From: Amery Hung @ 2026-09-05 22:01 UTC (permalink / raw)
To: bpf
Cc: alexei.starovoitov, andrii, daniel, eddyz87, memxor, ameryhung,
kernel-team
A kfunc memory argument may be backed by packet data. Exercise this
with bpf_skb_ct_lookup(), which writes errors through its opts memory
argument. Verify that the verifier marks the program as directly
writing packet data, causing TC to emit its writable-skb prologue.
Match the semantic prologue sequence while allowing
architecture-specific zero extensions and the endian-specific
cloned-bit mask.
Signed-off-by: Amery Hung <ameryhung@gmail.com>
---
.../selftests/bpf/prog_tests/verifier.c | 2 +
.../bpf/progs/verifier_kfunc_packet_access.c | 47 +++++++++++++++++++
2 files changed, 49 insertions(+)
create mode 100644 tools/testing/selftests/bpf/progs/verifier_kfunc_packet_access.c
diff --git a/tools/testing/selftests/bpf/prog_tests/verifier.c b/tools/testing/selftests/bpf/prog_tests/verifier.c
index f7f94ccebce2..b97381448248 100644
--- a/tools/testing/selftests/bpf/prog_tests/verifier.c
+++ b/tools/testing/selftests/bpf/prog_tests/verifier.c
@@ -54,6 +54,7 @@
#include "verifier_iterating_callbacks.skel.h"
#include "verifier_jeq_infer_not_null.skel.h"
#include "verifier_jit_convergence.skel.h"
+#include "verifier_kfunc_packet_access.skel.h"
#include "verifier_ld_ind.skel.h"
#include "verifier_ldsx.skel.h"
#include "verifier_leak_ptr.skel.h"
@@ -218,6 +219,7 @@ void test_verifier_int_ptr(void) { RUN(verifier_int_ptr); }
void test_verifier_iterating_callbacks(void) { RUN(verifier_iterating_callbacks); }
void test_verifier_jeq_infer_not_null(void) { RUN(verifier_jeq_infer_not_null); }
void test_verifier_jit_convergence(void) { RUN(verifier_jit_convergence); }
+void test_verifier_kfunc_packet_access(void) { RUN_TESTS(verifier_kfunc_packet_access); }
void test_verifier_load_acquire(void) { RUN(verifier_load_acquire); }
void test_verifier_ld_ind(void) { RUN(verifier_ld_ind); }
void test_verifier_ldsx(void) { RUN(verifier_ldsx); }
diff --git a/tools/testing/selftests/bpf/progs/verifier_kfunc_packet_access.c b/tools/testing/selftests/bpf/progs/verifier_kfunc_packet_access.c
new file mode 100644
index 000000000000..9ae63c9eab02
--- /dev/null
+++ b/tools/testing/selftests/bpf/progs/verifier_kfunc_packet_access.c
@@ -0,0 +1,47 @@
+// SPDX-License-Identifier: GPL-2.0
+
+#include <vmlinux.h>
+#include <bpf/bpf_helpers.h>
+#include "bpf_misc.h"
+
+struct nf_conn *bpf_skb_ct_lookup(struct __sk_buff *skb_ctx,
+ struct bpf_sock_tuple *bpf_tuple,
+ u32 tuple__sz, struct bpf_ct_opts *opts,
+ u32 opts__sz) __ksym;
+void bpf_ct_release(struct nf_conn *nfct) __ksym;
+
+char _license[] SEC("license") = "GPL";
+
+SEC("tc")
+__description("kfunc packet write requests writable skb")
+__success
+/* bpf_unclone_prologue() */
+__xlated("r6 = *(u8 *)(r1 +{{[0-9]+}})")
+__xlated("...")
+__xlated("w6 &= {{1|128}}")
+__xlated("...")
+__xlated("if r6 == 0x0 goto")
+__xlated("r6 = r1")
+__xlated("r2 ^= r2")
+__xlated("call")
+__xlated("if r0 == 0x0 goto")
+__xlated("w0 = 2")
+__xlated("...")
+__xlated("exit")
+__xlated("r1 = r6")
+int kfunc_packet_write(struct __sk_buff *skb)
+{
+ void *data_end = (void *)(long)skb->data_end;
+ void *data = (void *)(long)skb->data;
+ struct bpf_sock_tuple tuple = {};
+ struct nf_conn *nfct;
+
+ if (data + sizeof(struct bpf_ct_opts) > data_end)
+ return 0;
+
+ /* An invalid tuple size makes bpf_skb_ct_lookup() write opts->error. */
+ nfct = bpf_skb_ct_lookup(skb, &tuple, 1, data, sizeof(struct bpf_ct_opts));
+ if (nfct)
+ bpf_ct_release(nfct);
+ return 0;
+}
--
2.52.0
^ permalink raw reply related [flat|nested] 54+ messages in thread
* [PATCH bpf-next v1 20/22] bpf: Consolidate function call pkt_access validation
2026-09-05 22:00 [PATCH bpf-next v1 00/22] bpf: Unify helper and kfunc argument checks Amery Hung
` (18 preceding siblings ...)
2026-09-05 22:01 ` [PATCH bpf-next v1 19/22] selftests/bpf: Test kfunc packet memory direct writes Amery Hung
@ 2026-09-05 22:01 ` Amery Hung
2026-09-05 22:01 ` [PATCH bpf-next v1 21/22] bpf: Consolidate release argument validation Amery Hung
` (2 subsequent siblings)
22 siblings, 0 replies; 54+ messages in thread
From: Amery Hung @ 2026-09-05 22:01 UTC (permalink / raw)
To: bpf
Cc: alexei.starovoitov, andrii, daniel, eddyz87, memxor, ameryhung,
kernel-team
check_func_arg() checks whether a helper permits packet pointers before
dispatching argument-specific memory validation. check_kfunc_args() has
no equivalent check, even though kfunc memory arguments may be backed by
packet data.
Move packet-access validation to check_helper_mem_access(), where the
access direction is known and helper, kfunc, and global-subprogram memory
arguments converge. Pass call metadata there so helpers continue to
require bpf_func_proto::pkt_access, while writes through kfunc and global
subprogram arguments use the program-type policy and set
env->seen_direct_write.
Keep call metadata when variable-size memory disables raw mode by
clearing arg_raw_mem.regno instead, and pass it through the map-key path
as well.
This also makes kfunc and global-subprogram packet writes request the
required writable-packet prologue and rejects them for program types
that only support direct packet reads.
Signed-off-by: Amery Hung <ameryhung@gmail.com>
---
kernel/bpf/verifier.c | 24 +++++++++----------
.../bpf/progs/verifier_helper_packet_access.c | 4 ++--
2 files changed, 13 insertions(+), 15 deletions(-)
diff --git a/kernel/bpf/verifier.c b/kernel/bpf/verifier.c
index cad49ea5fdfa..487ad1ee4179 100644
--- a/kernel/bpf/verifier.c
+++ b/kernel/bpf/verifier.c
@@ -4849,7 +4849,7 @@ static int check_map_access(struct bpf_verifier_env *env, struct bpf_reg_state *
}
static bool may_access_direct_pkt_data(struct bpf_verifier_env *env,
- const struct bpf_func_proto *fn,
+ const struct bpf_call_arg_meta *meta,
enum bpf_access_type t)
{
enum bpf_prog_type prog_type = resolve_prog_type(env->prog);
@@ -4873,10 +4873,11 @@ static bool may_access_direct_pkt_data(struct bpf_verifier_env *env,
case BPF_PROG_TYPE_LWT_XMIT:
case BPF_PROG_TYPE_SK_SKB:
case BPF_PROG_TYPE_SK_MSG:
- if (fn)
- return fn->pkt_access;
+ if (meta && !meta->btf && meta->func_id)
+ return meta->fn->pkt_access;
- env->seen_direct_write = true;
+ if (t == BPF_WRITE)
+ env->seen_direct_write = true;
return true;
case BPF_PROG_TYPE_CGROUP_SOCKOPT:
@@ -7048,6 +7049,10 @@ static int check_helper_mem_access(struct bpf_verifier_env *env, struct bpf_reg_
switch (base_type(reg->type)) {
case PTR_TO_PACKET:
case PTR_TO_PACKET_META:
+ if (!may_access_direct_pkt_data(env, meta, access_type)) {
+ verbose(env, "function access to the packet is not allowed\n");
+ return -EACCES;
+ }
return check_packet_access(env, reg, argno, 0, access_size,
zero_size_allowed);
case PTR_TO_MAP_KEY:
@@ -7164,7 +7169,7 @@ static int check_mem_size_reg(struct bpf_verifier_env *env,
* the memory that the helper could just partially fill up.
*/
if (!tnum_is_const(size_reg->var_off))
- meta = NULL;
+ meta->arg_raw_mem.regno = 0;
if (reg_smin(size_reg) < 0) {
verbose(env, "%s min value is negative, either use unsigned or 'var &= const'\n",
@@ -8773,7 +8778,6 @@ static int check_func_arg(struct bpf_verifier_env *env, u32 arg,
struct bpf_reg_state *reg = get_func_arg_reg(caller, regs, arg);
enum bpf_arg_type arg_type = fn->arg_type[arg];
int regno = reg_from_argno(argno);
- enum bpf_reg_type type = reg->type;
u32 arg_size = arg_type & MEM_FIXED_SIZE ? fn->arg_size[arg] : 0;
u32 key_size;
int err = 0;
@@ -8798,12 +8802,6 @@ static int check_func_arg(struct bpf_verifier_env *env, u32 arg,
return 0;
}
- if (type_is_pkt_pointer(type) &&
- !may_access_direct_pkt_data(env, fn, BPF_READ)) {
- verbose(env, "helper access to the packet is not allowed\n");
- return -EACCES;
- }
-
err = resolve_func_arg_type(env, reg, arg, meta, &arg_type, &arg_size);
if (err)
return err;
@@ -8876,7 +8874,7 @@ static int check_func_arg(struct bpf_verifier_env *env, u32 arg,
return -EFAULT;
}
key_size = meta->map.ptr->key_size;
- err = check_helper_mem_access(env, reg, argno, key_size, BPF_READ, false, NULL,
+ err = check_helper_mem_access(env, reg, argno, key_size, BPF_READ, false, meta,
NULL);
if (err)
return err;
diff --git a/tools/testing/selftests/bpf/progs/verifier_helper_packet_access.c b/tools/testing/selftests/bpf/progs/verifier_helper_packet_access.c
index 71cee3f58324..12786b72c694 100644
--- a/tools/testing/selftests/bpf/progs/verifier_helper_packet_access.c
+++ b/tools/testing/selftests/bpf/progs/verifier_helper_packet_access.c
@@ -258,7 +258,7 @@ l0_%=: r0 = 0; \
SEC("tc")
__description("helper access to packet: test11, cls unsuitable helper 1")
-__failure __msg("helper access to the packet")
+__failure __msg("function access to the packet")
__naked void test11_cls_unsuitable_helper_1(void)
{
asm volatile (" \
@@ -283,7 +283,7 @@ l0_%=: r0 = 0; \
SEC("tc")
__description("helper access to packet: test12, cls unsuitable helper 2")
-__failure __msg("helper access to the packet")
+__failure __msg("function access to the packet")
__naked void test12_cls_unsuitable_helper_2(void)
{
asm volatile (" \
--
2.52.0
^ permalink raw reply related [flat|nested] 54+ messages in thread
* [PATCH bpf-next v1 21/22] bpf: Consolidate release argument validation
2026-09-05 22:00 [PATCH bpf-next v1 00/22] bpf: Unify helper and kfunc argument checks Amery Hung
` (19 preceding siblings ...)
2026-09-05 22:01 ` [PATCH bpf-next v1 20/22] bpf: Consolidate function call pkt_access validation Amery Hung
@ 2026-09-05 22:01 ` Amery Hung
2026-09-05 23:08 ` bot+bpf-ci
2026-09-05 22:01 ` [PATCH bpf-next v1 22/22] bpf: Check helper and kfunc arguments in one path Amery Hung
2026-09-12 3:20 ` [PATCH bpf-next v1 00/22] bpf: Unify helper and kfunc argument checks patchwork-bot+netdevbpf
22 siblings, 1 reply; 54+ messages in thread
From: Amery Hung @ 2026-09-05 22:01 UTC (permalink / raw)
To: bpf
Cc: alexei.starovoitov, andrii, daniel, eddyz87, memxor, ameryhung,
kernel-team
Helper and kfunc argument verification both require a live owning
reference for non-dynptr release arguments. Whether that reference may
be NULL is expressed by PTR_MAYBE_NULL in helper prototypes and the
__nullable BTF suffix for kfuncs. Factor the shared ownership policy
into check_func_arg_release().
Drop the helper-only rejection of maybe-null owning references. This
allows bpf_kptr_xchg() to accept its explicitly nullable source
directly, matching nullable kfunc release arguments. The verifier
consumes the reference regardless of its runtime nullness. Update the
tests so an owned nullable source succeeds while a nullable non-owning
source remains rejected for lacking ownership.
Use common legacy and structured diagnostic text for both call kinds.
Tailor the structured suggestion to describe a pointer for helpers and
include the expected BTF type for kfuncs. Resolve that type lazily from
the kfunc BTF parameter only when reporting an ownership failure. The
kfunc path now keys the check directly on the cached OBJ_RELEASE
argument flag instead of comparing the argument register against
meta->release_regno, and uses the cached argument kind for the dynptr
exemption.
This is another prerequisite for routing both call types through
check_func_arg().
Signed-off-by: Amery Hung <ameryhung@gmail.com>
---
kernel/bpf/verifier.c | 79 ++++++++++---------
.../selftests/bpf/prog_tests/cb_refs.c | 4 +-
.../selftests/bpf/progs/cgrp_kfunc_failure.c | 6 +-
.../selftests/bpf/progs/cpumask_failure.c | 2 +-
.../selftests/bpf/progs/map_kptr_fail.c | 4 +-
.../selftests/bpf/progs/task_kfunc_failure.c | 6 +-
.../bpf/progs/verifier_global_ptr_args.c | 2 +-
.../bpf/progs/verifier_ref_tracking.c | 2 +-
.../selftests/bpf/progs/verifier_sock.c | 6 +-
.../selftests/bpf/progs/verifier_vfs_reject.c | 2 +-
.../selftests/bpf/progs/wakeup_source_fail.c | 2 +-
11 files changed, 61 insertions(+), 54 deletions(-)
diff --git a/kernel/bpf/verifier.c b/kernel/bpf/verifier.c
index 487ad1ee4179..6c5d7e544f84 100644
--- a/kernel/bpf/verifier.c
+++ b/kernel/bpf/verifier.c
@@ -8418,6 +8418,43 @@ static int check_func_arg_nullability(struct bpf_verifier_env *env,
return -EACCES;
}
+static int check_func_arg_release(struct bpf_verifier_env *env, struct bpf_reg_state *reg,
+ argno_t argno, enum bpf_arg_type arg_type,
+ struct bpf_call_arg_meta *meta, int insn_idx)
+{
+ const char *expected_type = "pointer";
+
+ if (!arg_type_is_release(arg_type))
+ return 0;
+
+ if (arg_type_is_dynptr(arg_type) || reg_is_referenced(env, reg) ||
+ bpf_register_is_null(reg))
+ return 0;
+
+ verbose(env, "release function %s expects referenced PTR_TO_BTF_ID passed to %s\n",
+ meta->func_name, reg_arg_name(env, argno));
+
+ if (meta->btf) {
+ const struct btf_param *btf_arg;
+ const struct btf_type *t;
+ u32 ref_id;
+
+ btf_arg = &btf_params(meta->func_proto)[arg_idx_from_argno(argno)];
+ ref_id = btf_arg->type;
+ t = btf_type_skip_modifiers(meta->btf, btf_arg->type, NULL);
+ if (btf_type_is_ptr(t))
+ btf_type_skip_modifiers(meta->btf, t->type, &ref_id);
+ expected_type = bpf_diag_fmt(env, "value of type %s",
+ bpf_diag_fmt_btf_type(env, meta->btf, ref_id));
+ }
+
+ bpf_diag_call_arg_fmt(env, insn_idx, argno, meta->func_name,
+ bpf_diag_fmt(env, "Pass the resource-owning %s returned by the matching acquire call, or avoid the release function after ownership has already been transferred or released.",
+ expected_type),
+ "release functions require a value that owns a live resource returned by a matching acquire function");
+ return -EINVAL;
+}
+
static const char *bpf_diag_expected_reg_types(struct bpf_verifier_env *env,
const enum bpf_reg_type *types, int count)
{
@@ -8825,29 +8862,9 @@ static int check_func_arg(struct bpf_verifier_env *env, u32 arg,
return err;
skip_type_check:
- if (arg_type_is_release(arg_type)) {
- if (type_may_be_null(reg->type)) {
- verbose(env, "Possibly NULL pointer passed to trusted %s\n",
- reg_arg_name(env, argno));
- bpf_diag_call_arg(
- env, insn_idx, argno, meta->func_name,
- "the pointer may be NULL, but this call requires a non-NULL pointer",
- "Add a NULL check and make the call only on the non-NULL path.");
- return -EACCES;
- }
-
- if (!arg_type_is_dynptr(arg_type) &&
- !reg_is_referenced(env, reg) && !bpf_register_is_null(reg)) {
- verbose(env,
- "release helper %s expects referenced PTR_TO_BTF_ID passed to %s\n",
- meta->func_name, reg_arg_name(env, argno));
- bpf_diag_call_arg(
- env, insn_idx, argno, meta->func_name,
- "release helpers require a value that owns a live resource returned by a matching acquire helper",
- "Pass the resource-owning pointer returned by the matching acquire helper, and avoid calling the release helper after ownership has already been transferred or released.");
- return -EINVAL;
- }
- }
+ err = check_func_arg_release(env, reg, argno, arg_type, meta, insn_idx);
+ if (err)
+ return err;
if (reg_is_referenced(env, reg))
update_ref_obj(&meta->ref_obj, reg);
@@ -12990,19 +13007,9 @@ static int check_kfunc_args(struct bpf_verifier_env *env, struct bpf_call_arg_me
if (ret < 0)
return ret;
- if (regno == meta->release_regno && !is_kfunc_arg_dynptr(meta->btf, &args[i]) &&
- !reg_is_referenced(env, reg) && !bpf_register_is_null(reg)) {
- const char *expected_type;
-
- expected_type = bpf_diag_fmt_btf_type(env, btf, ref_id);
- verbose(env, "release kfunc %s expects referenced PTR_TO_BTF_ID passed to %s\n",
- func_name, reg_arg_name(env, argno));
- bpf_diag_call_arg_fmt(env, insn_idx, argno, func_name,
- "Pass the resource-owning pointer returned by the matching acquire kfunc, and avoid calling the release kfunc after ownership has already been transferred or released.",
- "release kfuncs require a resource-owning value of type %s returned by a matching acquire kfunc",
- expected_type);
- return -EINVAL;
- }
+ ret = check_func_arg_release(env, reg, argno, arg_type, meta, insn_idx);
+ if (ret < 0)
+ return ret;
if (reg_is_referenced(env, reg))
update_ref_obj(&meta->ref_obj, reg);
diff --git a/tools/testing/selftests/bpf/prog_tests/cb_refs.c b/tools/testing/selftests/bpf/prog_tests/cb_refs.c
index 50ea3d72d446..8743f52e755d 100644
--- a/tools/testing/selftests/bpf/prog_tests/cb_refs.c
+++ b/tools/testing/selftests/bpf/prog_tests/cb_refs.c
@@ -11,8 +11,8 @@ struct {
const char *prog_name;
const char *err_msg;
} cb_refs_tests[] = {
- { "underflow_prog", "release kfunc bpf_kfunc_call_test_release expects referenced PTR_TO_BTF_ID passed to R1" },
- { "leak_prog", "Possibly NULL pointer passed to trusted R2" },
+ { "underflow_prog", "release function bpf_kfunc_call_test_release expects referenced PTR_TO_BTF_ID passed to R1" },
+ { "leak_prog", "Unreleased reference id=4 alloc_insn=33" },
{ "nested_cb", "Unreleased reference id=4 alloc_insn=2" }, /* alloc_insn=2{4,5} */
{ "non_cb_transfer_ref", "Unreleased reference id=4 alloc_insn=1" }, /* alloc_insn=1{1,2} */
};
diff --git a/tools/testing/selftests/bpf/progs/cgrp_kfunc_failure.c b/tools/testing/selftests/bpf/progs/cgrp_kfunc_failure.c
index efe7bcae70f8..8de2206875ab 100644
--- a/tools/testing/selftests/bpf/progs/cgrp_kfunc_failure.c
+++ b/tools/testing/selftests/bpf/progs/cgrp_kfunc_failure.c
@@ -154,7 +154,7 @@ int BPF_PROG(cgrp_kfunc_xchg_unreleased, struct cgroup *cgrp, const char *path)
}
SEC("tp_btf/cgroup_mkdir")
-__failure __msg("release kfunc bpf_cgroup_release expects referenced PTR_TO_BTF_ID passed to R1")
+__failure __msg("release function bpf_cgroup_release expects referenced PTR_TO_BTF_ID passed to R1")
int BPF_PROG(cgrp_kfunc_rcu_get_release, struct cgroup *cgrp, const char *path)
{
struct cgroup *kptr;
@@ -191,7 +191,7 @@ int BPF_PROG(cgrp_kfunc_release_untrusted, struct cgroup *cgrp, const char *path
}
SEC("tp_btf/cgroup_mkdir")
-__failure __msg("release kfunc bpf_cgroup_release expects referenced PTR_TO_BTF_ID passed to R1")
+__failure __msg("release function bpf_cgroup_release expects referenced PTR_TO_BTF_ID passed to R1")
int BPF_PROG(cgrp_kfunc_release_fp, struct cgroup *cgrp, const char *path)
{
struct cgroup *acquired = (struct cgroup *)&path;
@@ -237,7 +237,7 @@ int BPF_PROG(cgrp_kfunc_release_null, struct cgroup *cgrp, const char *path)
}
SEC("tp_btf/cgroup_mkdir")
-__failure __msg("release kfunc bpf_cgroup_release expects referenced PTR_TO_BTF_ID passed to R1")
+__failure __msg("release function bpf_cgroup_release expects referenced PTR_TO_BTF_ID passed to R1")
int BPF_PROG(cgrp_kfunc_release_unacquired, struct cgroup *cgrp, const char *path)
{
/* Cannot release trusted cgroup pointer which was not acquired. */
diff --git a/tools/testing/selftests/bpf/progs/cpumask_failure.c b/tools/testing/selftests/bpf/progs/cpumask_failure.c
index 76a3cba6f23c..c89c88db39d1 100644
--- a/tools/testing/selftests/bpf/progs/cpumask_failure.c
+++ b/tools/testing/selftests/bpf/progs/cpumask_failure.c
@@ -183,7 +183,7 @@ int BPF_PROG(test_global_mask_no_null_check, struct task_struct *task, u64 clone
}
SEC("tp_btf/task_newtask")
-__failure __msg("Possibly NULL pointer passed to trusted R2")
+__failure __msg("release function bpf_kptr_xchg expects referenced PTR_TO_BTF_ID passed to R2")
int BPF_PROG(test_global_mask_rcu_no_null_check, struct task_struct *task, u64 clone_flags)
{
struct bpf_cpumask *prev, *curr;
diff --git a/tools/testing/selftests/bpf/progs/map_kptr_fail.c b/tools/testing/selftests/bpf/progs/map_kptr_fail.c
index d1ff9e7e87e3..4b64f1dc7b81 100644
--- a/tools/testing/selftests/bpf/progs/map_kptr_fail.c
+++ b/tools/testing/selftests/bpf/progs/map_kptr_fail.c
@@ -252,7 +252,7 @@ int reject_untrusted_store_to_ref(struct __sk_buff *ctx)
}
SEC("?tc")
-__failure __msg("release helper bpf_kptr_xchg expects referenced PTR_TO_BTF_ID passed to R2")
+__failure __msg("release function bpf_kptr_xchg expects referenced PTR_TO_BTF_ID passed to R2")
int reject_untrusted_xchg(struct __sk_buff *ctx)
{
struct prog_test_ref_kfunc *p;
@@ -364,7 +364,7 @@ int kptr_xchg_ref_state(struct __sk_buff *ctx)
}
SEC("?tc")
-__failure __msg("Possibly NULL pointer passed to trusted R2")
+__success
int kptr_xchg_possibly_null(struct __sk_buff *ctx)
{
struct prog_test_ref_kfunc *p;
diff --git a/tools/testing/selftests/bpf/progs/task_kfunc_failure.c b/tools/testing/selftests/bpf/progs/task_kfunc_failure.c
index f96b0c13ed1a..639276591902 100644
--- a/tools/testing/selftests/bpf/progs/task_kfunc_failure.c
+++ b/tools/testing/selftests/bpf/progs/task_kfunc_failure.c
@@ -179,7 +179,7 @@ int BPF_PROG(task_kfunc_release_untrusted, struct task_struct *task, u64 clone_f
}
SEC("tp_btf/task_newtask")
-__failure __msg("release kfunc bpf_task_release expects referenced PTR_TO_BTF_ID passed to R1")
+__failure __msg("release function bpf_task_release expects referenced PTR_TO_BTF_ID passed to R1")
int BPF_PROG(task_kfunc_release_fp, struct task_struct *task, u64 clone_flags)
{
struct task_struct *acquired = (struct task_struct *)&clone_flags;
@@ -225,7 +225,7 @@ int BPF_PROG(task_kfunc_release_null, struct task_struct *task, u64 clone_flags)
}
SEC("tp_btf/task_newtask")
-__failure __msg("release kfunc bpf_task_release expects referenced PTR_TO_BTF_ID passed to R1")
+__failure __msg("release function bpf_task_release expects referenced PTR_TO_BTF_ID passed to R1")
int BPF_PROG(task_kfunc_release_unacquired, struct task_struct *task, u64 clone_flags)
{
/* Cannot release trusted task pointer which was not acquired. */
@@ -353,7 +353,7 @@ int BPF_PROG(task_access_comm4, struct task_struct *task, const char *buf, bool
}
SEC("tp_btf/task_newtask")
-__failure __msg("release kfunc bpf_task_release expects referenced PTR_TO_BTF_ID passed to R1")
+__failure __msg("release function bpf_task_release expects referenced PTR_TO_BTF_ID passed to R1")
int BPF_PROG(task_kfunc_release_in_map, struct task_struct *task, u64 clone_flags)
{
struct task_struct *local;
diff --git a/tools/testing/selftests/bpf/progs/verifier_global_ptr_args.c b/tools/testing/selftests/bpf/progs/verifier_global_ptr_args.c
index 0bdeb7bc4687..87604020ba21 100644
--- a/tools/testing/selftests/bpf/progs/verifier_global_ptr_args.c
+++ b/tools/testing/selftests/bpf/progs/verifier_global_ptr_args.c
@@ -153,7 +153,7 @@ __weak int subprog_trusted_destroy(struct task_struct *task __arg_trusted)
SEC("?tp_btf/task_newtask")
__failure __log_level(2)
-__msg("release kfunc bpf_task_release expects referenced PTR_TO_BTF_ID passed to R1")
+__msg("release function bpf_task_release expects referenced PTR_TO_BTF_ID passed to R1")
int BPF_PROG(trusted_destroy_fail, struct task_struct *task, u64 clone_flags)
{
return subprog_trusted_destroy(task);
diff --git a/tools/testing/selftests/bpf/progs/verifier_ref_tracking.c b/tools/testing/selftests/bpf/progs/verifier_ref_tracking.c
index f3fad911b5bc..799db6f5713b 100644
--- a/tools/testing/selftests/bpf/progs/verifier_ref_tracking.c
+++ b/tools/testing/selftests/bpf/progs/verifier_ref_tracking.c
@@ -1288,7 +1288,7 @@ l1_%=: r1 = r6; \
SEC("tc")
__description("reference tracking: bpf_sk_release(listen_sk)")
-__failure __msg("release helper bpf_sk_release expects referenced PTR_TO_BTF_ID passed to R1")
+__failure __msg("release function bpf_sk_release expects referenced PTR_TO_BTF_ID passed to R1")
__naked void bpf_sk_release_listen_sk(void)
{
asm volatile (
diff --git a/tools/testing/selftests/bpf/progs/verifier_sock.c b/tools/testing/selftests/bpf/progs/verifier_sock.c
index d59b2f905671..2a136c917680 100644
--- a/tools/testing/selftests/bpf/progs/verifier_sock.c
+++ b/tools/testing/selftests/bpf/progs/verifier_sock.c
@@ -603,7 +603,7 @@ l2_%=: r0 = *(u32*)(r0 + %[bpf_tcp_sock_snd_cwnd]); \
SEC("tc")
__description("bpf_sk_release(skb->sk)")
-__failure __msg("release helper bpf_sk_release expects referenced PTR_TO_BTF_ID passed to R1")
+__failure __msg("release function bpf_sk_release expects referenced PTR_TO_BTF_ID passed to R1")
__naked void bpf_sk_release_skb_sk(void)
{
asm volatile (" \
@@ -620,7 +620,7 @@ l0_%=: r0 = 0; \
SEC("tc")
__description("bpf_sk_release(bpf_sk_fullsock(skb->sk))")
-__failure __msg("release helper bpf_sk_release expects referenced PTR_TO_BTF_ID passed to R1")
+__failure __msg("release function bpf_sk_release expects referenced PTR_TO_BTF_ID passed to R1")
__naked void bpf_sk_fullsock_skb_sk(void)
{
asm volatile (" \
@@ -644,7 +644,7 @@ l1_%=: r1 = r0; \
SEC("tc")
__description("bpf_sk_release(bpf_tcp_sock(skb->sk))")
-__failure __msg("release helper bpf_sk_release expects referenced PTR_TO_BTF_ID passed to R1")
+__failure __msg("release function bpf_sk_release expects referenced PTR_TO_BTF_ID passed to R1")
__naked void bpf_tcp_sock_skb_sk(void)
{
asm volatile (" \
diff --git a/tools/testing/selftests/bpf/progs/verifier_vfs_reject.c b/tools/testing/selftests/bpf/progs/verifier_vfs_reject.c
index ff08aa75d6f7..7d4d894b203e 100644
--- a/tools/testing/selftests/bpf/progs/verifier_vfs_reject.c
+++ b/tools/testing/selftests/bpf/progs/verifier_vfs_reject.c
@@ -80,7 +80,7 @@ int BPF_PROG(get_task_exe_file_kfunc_unreleased)
}
SEC("lsm.s/file_open")
-__failure __msg("release kfunc bpf_put_file expects referenced PTR_TO_BTF_ID passed to R1")
+__failure __msg("release function bpf_put_file expects referenced PTR_TO_BTF_ID passed to R1")
int BPF_PROG(put_file_kfunc_unacquired, struct file *file)
{
/* Can't release an unacquired pointer. */
diff --git a/tools/testing/selftests/bpf/progs/wakeup_source_fail.c b/tools/testing/selftests/bpf/progs/wakeup_source_fail.c
index d4d0f1610853..ff9ea5aa80ad 100644
--- a/tools/testing/selftests/bpf/progs/wakeup_source_fail.c
+++ b/tools/testing/selftests/bpf/progs/wakeup_source_fail.c
@@ -42,7 +42,7 @@ int wakeup_source_access_lock_fields(void *ctx)
}
SEC("syscall")
-__failure __msg("release kfunc bpf_wakeup_sources_read_unlock expects referenced PTR_TO_BTF_ID passed to R1")
+__failure __msg("release function bpf_wakeup_sources_read_unlock expects referenced PTR_TO_BTF_ID passed to R1")
int wakeup_source_unlock_no_lock(void *ctx)
{
struct bpf_ws_lock *lock = (void *)0x1;
--
2.52.0
^ permalink raw reply related [flat|nested] 54+ messages in thread
* [PATCH bpf-next v1 22/22] bpf: Check helper and kfunc arguments in one path
2026-09-05 22:00 [PATCH bpf-next v1 00/22] bpf: Unify helper and kfunc argument checks Amery Hung
` (20 preceding siblings ...)
2026-09-05 22:01 ` [PATCH bpf-next v1 21/22] bpf: Consolidate release argument validation Amery Hung
@ 2026-09-05 22:01 ` Amery Hung
2026-09-05 22:33 ` sashiko-bot
2026-09-10 21:53 ` Alexei Starovoitov
2026-09-12 3:20 ` [PATCH bpf-next v1 00/22] bpf: Unify helper and kfunc argument checks patchwork-bot+netdevbpf
22 siblings, 2 replies; 54+ messages in thread
From: Amery Hung @ 2026-09-05 22:01 UTC (permalink / raw)
To: bpf
Cc: alexei.starovoitov, andrii, daniel, eddyz87, memxor, ameryhung,
kernel-team
Generated kfunc prototypes now carry the argument kinds and metadata
needed by the helper argument checker, but kfunc calls still duplicate
the per-argument loop and switch.
Add check_func_args() to determine the argument count, validate outgoing
kfunc stack arguments, and drive check_func_arg() for both call kinds.
Move the remaining kfunc-only argument handlers, including the dynptr
clone runtime refinement, into check_func_arg(), derive each kfunc BTF
referent there once, and skip check_reg_arg() for stack-passed
arguments that check_outgoing_stack_args() already validated. Keep
special-kfunc ID checks behind small helpers so their table can remain
in place.
Keep the intentional differences selected by call metadata: helpers
retain their privileged ARG_SCALAR pointer-leak behavior, while kfunc
scalars use normal register admission. The shared ARG_PTR_TO_BTF_ID
case retains helper-only compatible-ID and bpf_kptr_xchg() handling,
performs kfunc trusted/RCU provenance checks, and selects the expected
BTF based on call kind. Helper and kfunc memory arguments likewise
retain their access modes and share fixed-memory and memory/size
diagnostics.
Treat an accepted nullable NULL as a completed argument check, matching
the former kfunc continue path. The skipped helper-specific processing
is either a no-op for NULL or deferred to the paired size argument.
Signed-off-by: Amery Hung <ameryhung@gmail.com>
---
kernel/bpf/verifier.c | 918 +++++++++---------
.../selftests/bpf/prog_tests/cb_refs.c | 2 +-
.../selftests/bpf/progs/cgrp_kfunc_failure.c | 4 +-
.../selftests/bpf/progs/task_kfunc_failure.c | 6 +-
.../selftests/bpf/progs/verifier_vfs_reject.c | 2 +-
.../selftests/bpf/progs/wakeup_source_fail.c | 2 +-
6 files changed, 444 insertions(+), 490 deletions(-)
diff --git a/kernel/bpf/verifier.c b/kernel/bpf/verifier.c
index 6c5d7e544f84..54656fb31bbe 100644
--- a/kernel/bpf/verifier.c
+++ b/kernel/bpf/verifier.c
@@ -7674,7 +7674,7 @@ static int process_dynptr_func(struct bpf_verifier_env *env, struct bpf_reg_stat
verbose(env, "Dynptr has to be an uninitialized dynptr\n");
bpf_diag_res(
env, insn_idx, "dynptr is already initialized",
- "This kfunc constructs a dynptr and requires an uninitialized dynptr stack slot, but the selected slot already holds dynptr state.",
+ "This function constructs a dynptr and requires an uninitialized dynptr stack slot, but the selected slot already holds dynptr state.",
"Use a fresh stack dynptr slot, or release/destroy the existing dynptr before reusing the slot.");
return -EINVAL;
}
@@ -7803,8 +7803,8 @@ static int process_iter_arg(struct bpf_verifier_env *env, struct bpf_reg_state *
reg_arg_name(env, argno));
bpf_diag_call_arg(
env, insn_idx, argno, meta->func_name,
- "the kfunc expects a recognized iterator state pointer, but this argument does not match a valid iterator type",
- "Pass the exact iterator state type expected by this kfunc.");
+ "the function expects a recognized iterator state pointer, but this argument does not match a valid iterator type",
+ "Pass the exact iterator state type expected by this function.");
return -EINVAL;
}
t = btf_type_by_id(meta->btf, btf_id);
@@ -8200,6 +8200,39 @@ static int process_arg_ptr_to_btf_id(struct bpf_verifier_env *env, struct bpf_re
argno_t argno, enum bpf_arg_type arg_type,
const struct btf *arg_btf, u32 arg_btf_id,
struct bpf_call_arg_meta *meta, int insn_idx);
+static bool is_kfunc_arg_nonown_allowed(const struct btf *btf,
+ const struct btf_param *arg);
+static bool is_kfunc_arg_scalar_with_name(const struct btf *btf,
+ const struct btf_param *arg,
+ const char *name);
+static bool is_bpf_cast_to_kern_ctx_kfunc(const struct bpf_call_arg_meta *meta);
+static bool is_bpf_dynptr_clone_kfunc(const struct bpf_call_arg_meta *meta);
+static bool is_bpf_iter_css_task_new_kfunc(const struct bpf_call_arg_meta *meta);
+static bool is_bpf_obj_drop_kfunc(u32 func_id);
+static bool is_bpf_percpu_obj_drop_kfunc(u32 func_id);
+static bool is_bpf_rbtree_add_kfunc(u32 func_id);
+static int get_bpf_res_spin_lock_kfunc_flags(const struct bpf_call_arg_meta *meta);
+static struct bpf_insn_aux_data *cur_aux(const struct bpf_verifier_env *env);
+static int process_irq_flag(struct bpf_verifier_env *env,
+ struct bpf_reg_state *reg, argno_t argno,
+ struct bpf_call_arg_meta *meta);
+static int process_kf_arg_ptr_to_list_head(struct bpf_verifier_env *env,
+ struct bpf_reg_state *reg,
+ argno_t argno,
+ struct bpf_call_arg_meta *meta);
+static int process_kf_arg_ptr_to_rbtree_root(struct bpf_verifier_env *env,
+ struct bpf_reg_state *reg,
+ argno_t argno,
+ struct bpf_call_arg_meta *meta);
+static int process_kf_arg_ptr_to_list_node(struct bpf_verifier_env *env,
+ struct bpf_reg_state *reg,
+ argno_t argno,
+ struct bpf_call_arg_meta *meta);
+static int process_kf_arg_ptr_to_rbtree_node(struct bpf_verifier_env *env,
+ struct bpf_reg_state *reg,
+ argno_t argno,
+ struct bpf_call_arg_meta *meta);
+static bool check_css_task_iter_allowlist(struct bpf_verifier_env *env);
struct bpf_reg_types {
const enum bpf_reg_type types[10];
@@ -8764,7 +8797,6 @@ static int get_constant_map_key(struct bpf_verifier_env *env,
}
static bool can_elide_value_nullness(const struct bpf_map *map);
-static struct bpf_insn_aux_data *cur_aux(const struct bpf_verifier_env *env);
static int process_map_ptr_arg(struct bpf_verifier_env *env, struct bpf_reg_state *reg,
argno_t argno, struct bpf_call_arg_meta *meta)
@@ -8808,6 +8840,7 @@ static int check_func_arg(struct bpf_verifier_env *env, u32 arg,
struct bpf_call_arg_meta *meta,
int insn_idx)
{
+ const struct btf_param *btf_arg = meta->btf ? &btf_params(meta->func_proto)[arg] : NULL;
const struct bpf_func_proto *fn = meta->fn;
struct bpf_func_state *caller = cur_func(env);
struct bpf_reg_state *regs = cur_regs(env);
@@ -8826,11 +8859,14 @@ static int check_func_arg(struct bpf_verifier_env *env, u32 arg,
if (arg_type == ARG_IGNORE)
return 0;
- err = check_reg_arg(env, regno, SRC_OP);
- if (err)
- return err;
+ if (regno >= 0) {
+ err = check_reg_arg(env, regno, SRC_OP);
+ if (err)
+ return err;
+ }
- if (arg_type == ARG_SCALAR) {
+ /* Preserve the legacy helper behavior for privileged pointer leaks. */
+ if (!meta->btf && arg_type == ARG_SCALAR) {
if (__is_pointer_value(env->allow_ptr_leaks, reg)) {
verbose(env, "%s leaks addr into helper function\n",
reg_arg_name(env, argno));
@@ -8844,10 +8880,7 @@ static int check_func_arg(struct bpf_verifier_env *env, u32 arg,
return err;
if (bpf_register_is_null(reg) && type_may_be_null(arg_type))
- /* A NULL register has a SCALAR_VALUE type, so skip
- * type checking.
- */
- goto skip_type_check;
+ return 0;
err = check_func_arg_nullability(env, reg, argno, arg_type, meta, insn_idx);
if (err)
@@ -8861,7 +8894,6 @@ static int check_func_arg(struct bpf_verifier_env *env, u32 arg,
if (err)
return err;
-skip_type_check:
err = check_func_arg_release(env, reg, argno, arg_type, meta, insn_idx);
if (err)
return err;
@@ -8870,6 +8902,19 @@ static int check_func_arg(struct bpf_verifier_env *env, u32 arg,
update_ref_obj(&meta->ref_obj, reg);
switch (base_type(arg_type)) {
+ case ARG_CONST_SCALAR:
+ err = process_const_arg(env, reg, argno, meta);
+ if (err < 0) {
+ if (err == -EINVAL)
+ bpf_diag_call_arg_fmt(env, insn_idx, argno, meta->func_name,
+ "Pass a compile-time constant or a value the verifier can prove is constant at this call.",
+ "the function requires this scalar argument to be a verifier-known constant, but %s is variable on this path",
+ reg_arg_name(env, argno));
+ return err;
+ }
+ break;
+ case ARG_SCALAR:
+ break;
case ARG_CONST_MAP_PTR:
/* bpf_map_xxx(map_ptr) call: remember that map_ptr */
err = process_map_ptr_arg(env, reg, argno, meta);
@@ -8935,6 +8980,7 @@ static int check_func_arg(struct bpf_verifier_env *env, u32 arg,
case ARG_PTR_TO_BTF_ID_SOCK_COMMON:
{
const u32 *arg_btf_id = fn->arg_btf_id[arg];
+ const struct btf *arg_btf = meta->btf ?: btf_vmlinux;
if (!meta->btf) {
const struct bpf_reg_types *compatible;
@@ -8962,9 +9008,40 @@ static int check_func_arg(struct bpf_verifier_env *env, u32 arg,
}
}
- err = process_arg_ptr_to_btf_id(env, reg, argno, arg_type,
- btf_vmlinux, *arg_btf_id,
- meta, insn_idx);
+ if (meta->btf && (!is_trusted_reg(env, reg) ||
+ bpf_type_has_unsafe_modifiers(reg->type))) {
+ if (!(arg_type & MEM_RCU)) {
+ const char *actual_type, *arg_name, *expected_type;
+
+ expected_type = bpf_diag_fmt_btf_type(env, arg_btf, *arg_btf_id);
+ verbose(env, "%s must be referenced or trusted\n",
+ reg_arg_name(env, argno));
+ arg_name = reg_arg_name(env, argno);
+ actual_type = bpf_diag_reg_type_plain(env, reg->type);
+ bpf_diag_call_arg_fmt(env, insn_idx, argno, meta->func_name,
+ "Pass a pointer acquired from a verifier-tracked source, or call this function only inside the required protection if it accepts RCU pointers.",
+ "the function requires a trusted or resource-owning pointer to %s, but %s is %s",
+ expected_type, arg_name, actual_type);
+ return -EINVAL;
+ }
+ if (!is_rcu_reg(reg)) {
+ const char *actual_type, *arg_name, *expected_type;
+
+ expected_type = bpf_diag_fmt_btf_type(env, arg_btf, *arg_btf_id);
+ verbose(env, "%s must be a rcu pointer\n",
+ reg_arg_name(env, argno));
+ arg_name = reg_arg_name(env, argno);
+ actual_type = bpf_diag_reg_type_plain(env, reg->type);
+ bpf_diag_call_arg_fmt(env, insn_idx, argno, meta->func_name,
+ "Use this function with a pointer that is valid in an RCU read lock region.",
+ "the function requires an RCU-protected pointer to %s, but %s is %s",
+ expected_type, arg_name, actual_type);
+ return -EINVAL;
+ }
+ }
+
+ err = process_arg_ptr_to_btf_id(env, reg, argno, arg_type, arg_btf,
+ *arg_btf_id, meta, insn_idx);
if (err < 0)
return err;
break;
@@ -9001,44 +9078,273 @@ static int check_func_arg(struct bpf_verifier_env *env, u32 arg,
if (err)
return err;
break;
+ case ARG_PTR_TO_CTX:
+ if (is_bpf_cast_to_kern_ctx_kfunc(meta)) {
+ err = get_kern_ctx_btf_id(&env->log, resolve_prog_type(env->prog));
+ if (err < 0)
+ return -EINVAL;
+ meta->ret_btf_id = err;
+ }
+ break;
+ case ARG_PTR_TO_ARENA:
+ break;
+ case ARG_PTR_TO_ALLOC_BTF_ID:
+ if (reg->type == (PTR_TO_BTF_ID | MEM_ALLOC)) {
+ if (!is_bpf_obj_drop_kfunc(meta->func_id)) {
+ verbose(env, "%s expected for bpf_obj_drop()\n",
+ reg_arg_name(env, argno));
+ return -EINVAL;
+ }
+ } else if (reg->type == (PTR_TO_BTF_ID | MEM_ALLOC | MEM_PERCPU)) {
+ if (!is_bpf_percpu_obj_drop_kfunc(meta->func_id)) {
+ verbose(env, "%s expected for bpf_percpu_obj_drop()\n",
+ reg_arg_name(env, argno));
+ return -EINVAL;
+ }
+ }
+ if (!reg_is_referenced(env, reg)) {
+ verbose(env, "allocated object must be referenced\n");
+ bpf_diag_call_arg_fmt(env, insn_idx, argno, meta->func_name,
+ "Pass the owned object pointer before it is released or transferred.",
+ "the allocated object pointer in %s must still carry verifier-tracked ownership, but this pointer no longer owns a live resource",
+ reg_arg_name(env, argno));
+ return -EINVAL;
+ }
+ if (meta->btf == btf_vmlinux) {
+ meta->arg_btf = reg->btf;
+ meta->arg_btf_id = reg->btf_id;
+ }
+ break;
case ARG_PTR_TO_FUNC:
meta->subprogno = reg->subprogno;
break;
case ARG_PTR_TO_MEM:
+ {
+ enum bpf_access_type access_type;
+ bool known_memory;
+
/* The access to this pointer is only checked when we hit the
* next is_mem_size argument below.
*/
- if (arg_type & MEM_FIXED_SIZE) {
- err = check_mem_reg(env, reg, argno, arg_size,
- arg_type & MEM_WRITE ? BPF_WRITE : BPF_READ, meta, NULL);
- if (err)
- return err;
- if (arg_type & MEM_ALIGNED)
- err = check_ptr_alignment(env, reg, 0, arg_size, true);
+ if (!(arg_type & MEM_FIXED_SIZE))
+ break;
+
+ access_type = arg_type & MEM_WRITE ? BPF_WRITE : BPF_READ;
+ if (meta->btf)
+ access_type = BPF_READ | BPF_WRITE;
+
+ err = check_mem_reg(env, reg, argno, arg_size, access_type, meta, &known_memory);
+ if (err < 0) {
+ if (known_memory)
+ bpf_diag_call_arg_fmt(env, insn_idx, argno, meta->func_name,
+ "Pass memory with at least the required number of accessible bytes and suitable read or write access.",
+ "the function expects %u bytes of memory, but the verifier cannot prove that %s provides a range of that size with the required read or write access",
+ arg_size,
+ bpf_diag_reg_type_plain(env, reg->type));
+ else
+ bpf_diag_call_arg_fmt(env, insn_idx, argno, meta->func_name,
+ "Pass stack, map, context, or other verifier-known memory of the expected type and size, not an integer cast to a pointer.",
+ "the function expects %u bytes of memory, but it is %s and not verifier-known memory",
+ arg_size,
+ bpf_diag_reg_type_plain(env, reg->type));
+ return err;
}
+ if (arg_type & MEM_ALIGNED)
+ err = check_ptr_alignment(env, reg, 0, arg_size, true);
break;
+ }
+ case ARG_CONST_MEM_SIZE:
+ err = process_const_arg(env, reg, argno, meta);
+ if (err < 0) {
+ if (err == -EINVAL)
+ bpf_diag_call_arg_fmt(env, insn_idx, argno, meta->func_name,
+ "Pass a compile-time constant or a value the verifier can prove is constant at this call.",
+ "the function requires this memory size to be a verifier-known constant, but %s is variable on this path",
+ reg_arg_name(env, argno));
+ return err;
+ }
+ fallthrough;
case ARG_MEM_SIZE:
- err = check_mem_size_reg(env, get_func_arg_reg(caller, regs, arg - 1), reg,
- argno_from_arg(arg), argno,
- fn->arg_type[arg - 1] & MEM_WRITE ? BPF_WRITE : BPF_READ,
- false, meta, NULL);
- break;
case ARG_MEM_SIZE_OR_ZERO:
- err = check_mem_size_reg(env, get_func_arg_reg(caller, regs, arg - 1), reg,
- argno_from_arg(arg), argno,
- fn->arg_type[arg - 1] & MEM_WRITE ? BPF_WRITE : BPF_READ,
- true, meta, NULL);
+ {
+ struct bpf_reg_state *buff_reg = get_func_arg_reg(caller, regs, arg - 1);
+ argno_t buff_argno = argno_from_arg(arg);
+ enum bpf_mem_size_failure failure;
+ u32 access_type;
+ bool zero_size_allowed;
+
+ if (meta->btf && bpf_register_is_null(buff_reg))
+ break;
+
+ access_type = fn->arg_type[arg - 1] & MEM_WRITE ? BPF_WRITE : BPF_READ;
+ if (meta->btf)
+ access_type = BPF_READ | BPF_WRITE;
+ zero_size_allowed = meta->btf || base_type(arg_type) == ARG_MEM_SIZE_OR_ZERO;
+
+ err = check_mem_size_reg(env, buff_reg, reg, buff_argno, argno,
+ access_type, zero_size_allowed, meta, &failure);
+ if (err < 0) {
+ const char *buff_arg, *size_arg;
+
+ buff_arg = bpf_diag_arg_name(env, buff_argno);
+ size_arg = bpf_diag_arg_name(env, argno);
+ verbose(env, "%s and ", reg_arg_name(env, buff_argno));
+ verbose(env, "%s memory, len pair leads to invalid memory access\n",
+ reg_arg_name(env, argno));
+ if (failure == BPF_MEM_SIZE_FAIL_MEMORY) {
+ bpf_diag_call_arg_fmt(env, insn_idx, buff_argno, meta->func_name,
+ "Pass a stack, map, context, or other verifier-known memory pointer, and keep the paired length within that object.",
+ "it is the memory pointer in a memory/length pair with %s, but %s does not provide a verifier-accessible range of the requested length",
+ size_arg, buff_arg);
+ } else if (failure == BPF_MEM_SIZE_FAIL_SIZE) {
+ if (reg_smin(reg) < 0)
+ bpf_diag_call_arg_fmt(env, insn_idx, argno, meta->func_name,
+ "Constrain the memory size to a non-negative value smaller than BPF_MAX_VAR_SIZ before this call.",
+ "the memory size in %s may be negative because its signed minimum is %lld",
+ size_arg, reg_smin(reg));
+ else if (!zero_size_allowed && reg_umin(reg) == 0)
+ bpf_diag_call_arg_fmt(env, insn_idx, argno, meta->func_name,
+ "Ensure the memory size is non-zero before this call.",
+ "the memory size in %s may be zero, but the function requires a non-zero size",
+ size_arg);
+ else
+ bpf_diag_call_arg_fmt(env, insn_idx, argno, meta->func_name,
+ "Constrain the memory size to a non-negative value smaller than BPF_MAX_VAR_SIZ before this call.",
+ "the memory size in %s may reach %llu bytes, but variable memory accesses must stay below %u bytes",
+ size_arg, reg_umax(reg), BPF_MAX_VAR_SIZ);
+ }
+ }
break;
- case ARG_PTR_TO_DYNPTR:
+ }
+ case ARG_PTR_TO_DYNPTR: {
+ if (is_bpf_dynptr_clone_kfunc(meta) &&
+ (arg_type & MEM_UNINIT)) {
+ enum bpf_dynptr_type parent_type = meta->dynptr.type;
+
+ if (parent_type == BPF_DYNPTR_TYPE_INVALID) {
+ verifier_bug(env, "no dynptr type for parent of clone");
+ return -EFAULT;
+ }
+
+ arg_type |= (unsigned int)get_dynptr_type_flag(parent_type);
+ }
+
err = process_dynptr_func(env, reg, argno, insn_idx, arg_type, meta);
if (err)
return err;
break;
+ }
+ case ARG_PTR_TO_ITER:
+ if (is_bpf_iter_css_task_new_kfunc(meta) &&
+ !check_css_task_iter_allowlist(env)) {
+ verbose(env, "css_task_iter is only allowed in bpf_lsm, bpf_iter and sleepable progs\n");
+ return -EINVAL;
+ }
+ err = process_iter_arg(env, reg, argno, insn_idx, meta);
+ if (err < 0)
+ return err;
+ break;
+ case ARG_PTR_TO_LIST_HEAD:
+ if (reg->type == (PTR_TO_BTF_ID | MEM_ALLOC) &&
+ !reg_is_referenced(env, reg)) {
+ verbose(env, "allocated object must be referenced\n");
+ return -EINVAL;
+ }
+ err = process_kf_arg_ptr_to_list_head(env, reg, argno, meta);
+ if (err < 0)
+ return err;
+ break;
+ case ARG_PTR_TO_RB_ROOT:
+ if (reg->type == (PTR_TO_BTF_ID | MEM_ALLOC) &&
+ !reg_is_referenced(env, reg)) {
+ verbose(env, "allocated object must be referenced\n");
+ return -EINVAL;
+ }
+ err = process_kf_arg_ptr_to_rbtree_root(env, reg, argno, meta);
+ if (err < 0)
+ return err;
+ break;
+ case ARG_PTR_TO_LIST_NODE:
+ if (!(is_kfunc_arg_nonown_allowed(meta->btf, btf_arg) &&
+ type_is_non_owning_ref(reg->type) && !reg_is_referenced(env, reg))) {
+ if (reg->type != (PTR_TO_BTF_ID | MEM_ALLOC)) {
+ verbose(env, "%s expected pointer to allocated object\n",
+ reg_arg_name(env, argno));
+ return -EINVAL;
+ }
+ if (!reg_is_referenced(env, reg)) {
+ verbose(env, "allocated object must be referenced\n");
+ return -EINVAL;
+ }
+ }
+ err = process_kf_arg_ptr_to_list_node(env, reg, argno, meta);
+ if (err < 0)
+ return err;
+ break;
+ case ARG_PTR_TO_RB_NODE:
+ if (is_bpf_rbtree_add_kfunc(meta->func_id)) {
+ if (reg->type != (PTR_TO_BTF_ID | MEM_ALLOC)) {
+ verbose(env, "%s expected pointer to allocated object\n",
+ reg_arg_name(env, argno));
+ return -EINVAL;
+ }
+ if (!reg_is_referenced(env, reg)) {
+ verbose(env, "allocated object must be referenced\n");
+ return -EINVAL;
+ }
+ } else {
+ if (!type_is_non_owning_ref(reg->type) &&
+ !reg_is_referenced(env, reg)) {
+ verbose(env, "%s can only take non-owning or refcounted bpf_rb_node pointer\n",
+ meta->func_name);
+ return -EINVAL;
+ }
+ if (in_rbtree_lock_required_cb(env)) {
+ verbose(env, "%s not allowed in rbtree cb\n", meta->func_name);
+ return -EINVAL;
+ }
+ }
+ err = process_kf_arg_ptr_to_rbtree_node(env, reg, argno, meta);
+ if (err < 0)
+ return err;
+ break;
case ARG_CONST_ALLOC_SIZE_OR_ZERO:
+ if (meta->btf && is_kfunc_arg_scalar_with_name(meta->btf, btf_arg,
+ "rdonly_buf_size"))
+ meta->r0_rdonly = true;
err = process_const_alloc_mem_size(env, reg, argno, &meta->ret_mem);
- if (err)
+ if (err < 0) {
+ if (meta->btf && err == -EINVAL)
+ bpf_diag_call_arg_fmt(env, insn_idx, argno, meta->func_name,
+ "Pass a verifier-known constant size for this function's buffer argument.",
+ "the function uses this argument as a return-buffer size, but %s is invalid or variable on this path",
+ reg_arg_name(env, argno));
return err;
+ }
+ break;
+ case ARG_PTR_TO_REFCOUNTED_KPTR:
+ {
+ struct btf_record *rec;
+
+ if (!type_is_non_owning_ref(reg->type))
+ meta->arg_owning_ref = true;
+
+ rec = reg_btf_record(reg);
+ if (!rec) {
+ verifier_bug(env, "Couldn't find btf_record");
+ return -EFAULT;
+ }
+
+ if (rec->refcount_off < 0) {
+ verbose(env, "%s doesn't point to a type with bpf_refcount field\n",
+ reg_arg_name(env, argno));
+ return -EINVAL;
+ }
+
+ meta->arg_btf = reg->btf;
+ meta->arg_btf_id = reg->btf_id;
break;
+ }
case ARG_PTR_TO_CONST_STR:
{
err = check_arg_const_str(env, reg, argno);
@@ -9046,6 +9352,33 @@ static int check_func_arg(struct bpf_verifier_env *env, u32 arg,
return err;
break;
}
+ case ARG_PTR_TO_WORKQUEUE:
+ err = check_map_field_pointer(env, reg, argno, BPF_WORKQUEUE, &meta->map);
+ if (err < 0)
+ return err;
+ break;
+ case ARG_PTR_TO_TASK_WORK:
+ err = check_map_field_pointer(env, reg, argno, BPF_TASK_WORK, &meta->map);
+ if (err < 0)
+ return err;
+ break;
+ case ARG_PTR_TO_IRQ_FLAG:
+ err = process_irq_flag(env, reg, argno, meta);
+ if (err < 0)
+ return err;
+ break;
+ case ARG_PTR_TO_RES_SPIN_LOCK:
+ {
+ int flags;
+
+ flags = get_bpf_res_spin_lock_kfunc_flags(meta);
+ if (!flags)
+ return -EFAULT;
+ err = process_spin_lock(env, reg, argno, flags);
+ if (err < 0)
+ return err;
+ break;
+ }
case ARG_KPTR_XCHG_DEST:
err = process_kptr_func(env, regno, meta);
if (err)
@@ -9056,6 +9389,37 @@ static int check_func_arg(struct bpf_verifier_env *env, u32 arg,
return err;
}
+static int check_func_args(struct bpf_verifier_env *env, struct bpf_call_arg_meta *meta,
+ int insn_idx)
+{
+ struct bpf_func_state *caller = cur_func(env);
+ const struct btf_param *args = NULL;
+ u32 arg, nargs = MAX_BPF_FUNC_REG_ARGS;
+ int err;
+
+ if (meta->btf) {
+ args = btf_params(meta->func_proto);
+ nargs = btf_type_vlen(meta->func_proto);
+ }
+
+ if (nargs > MAX_BPF_FUNC_REG_ARGS) {
+ err = check_outgoing_stack_args(env, caller, nargs, meta->func_name,
+ meta->btf, args);
+ if (err)
+ return err;
+ }
+
+ for (arg = 0; arg < nargs; arg++) {
+ if (meta->fn->arg_type[arg] == ARG_UNUSED)
+ break;
+ err = check_func_arg(env, arg, meta, insn_idx);
+ if (err)
+ return err;
+ }
+
+ return 0;
+}
+
static bool may_update_sockmap(struct bpf_verifier_env *env, int func_id)
{
enum bpf_attach_type eatype = env->prog->expected_attach_type;
@@ -11032,14 +11396,9 @@ static int check_helper_call(struct bpf_verifier_env *env, struct bpf_insn *insn
meta.func_id = func_id;
meta.func_name = func_id_name(func_id);
meta.fn = fn;
- /* check args */
- for (i = 0; i < MAX_BPF_FUNC_REG_ARGS; i++) {
- if (fn->arg_type[i] == ARG_UNUSED)
- break;
- err = check_func_arg(env, i, &meta, insn_idx);
- if (err)
- return err;
- }
+ err = check_func_args(env, &meta, insn_idx);
+ if (err)
+ return err;
err = record_func_map(env, &meta, func_id, insn_idx);
if (err)
@@ -11991,7 +12350,10 @@ enum special_kfunc_type {
KF_bpf_task_work_schedule_resume,
KF_bpf_arena_alloc_pages,
KF_bpf_arena_free_pages,
+ KF_bpf_arena_reserve_pages,
KF_bpf_session_is_return,
+ KF_bpf_stream_vprintk,
+ KF_bpf_stream_print_stack,
};
BTF_ID_LIST(special_kfunc_list)
@@ -12081,11 +12443,29 @@ BTF_ID(func, bpf_task_work_schedule_signal)
BTF_ID(func, bpf_task_work_schedule_resume)
BTF_ID(func, bpf_arena_alloc_pages)
BTF_ID(func, bpf_arena_free_pages)
+BTF_ID(func, bpf_arena_reserve_pages)
#ifdef CONFIG_BPF_EVENTS
BTF_ID(func, bpf_session_is_return)
#else
BTF_ID_UNUSED
#endif
+BTF_ID(func, bpf_stream_vprintk)
+BTF_ID(func, bpf_stream_print_stack)
+
+static bool is_bpf_cast_to_kern_ctx_kfunc(const struct bpf_call_arg_meta *meta)
+{
+ return is_kfunc_call(meta, special_kfunc_list[KF_bpf_cast_to_kern_ctx]);
+}
+
+static bool is_bpf_dynptr_clone_kfunc(const struct bpf_call_arg_meta *meta)
+{
+ return is_kfunc_call(meta, special_kfunc_list[KF_bpf_dynptr_clone]);
+}
+
+static bool is_bpf_iter_css_task_new_kfunc(const struct bpf_call_arg_meta *meta)
+{
+ return is_kfunc_call(meta, special_kfunc_list[KF_bpf_iter_css_task_new]);
+}
static bool is_bpf_obj_new_kfunc(u32 func_id)
{
@@ -12677,12 +13057,20 @@ static bool is_bpf_rbtree_api_kfunc(u32 btf_id)
btf_id == special_kfunc_list[KF_bpf_rbtree_right];
}
-static bool is_bpf_res_spin_lock_kfunc(u32 btf_id)
+static int get_bpf_res_spin_lock_kfunc_flags(const struct bpf_call_arg_meta *meta)
{
- return btf_id == special_kfunc_list[KF_bpf_res_spin_lock] ||
- btf_id == special_kfunc_list[KF_bpf_res_spin_unlock] ||
- btf_id == special_kfunc_list[KF_bpf_res_spin_lock_irqsave] ||
- btf_id == special_kfunc_list[KF_bpf_res_spin_unlock_irqrestore];
+ int flags = PROCESS_RES_LOCK;
+
+ if (is_kfunc_call(meta, special_kfunc_list[KF_bpf_res_spin_lock]) ||
+ is_kfunc_call(meta, special_kfunc_list[KF_bpf_res_spin_lock_irqsave]))
+ flags |= PROCESS_SPIN_LOCK;
+ else if (!is_kfunc_call(meta, special_kfunc_list[KF_bpf_res_spin_unlock]) &&
+ !is_kfunc_call(meta, special_kfunc_list[KF_bpf_res_spin_unlock_irqrestore]))
+ return 0;
+ if (is_kfunc_call(meta, special_kfunc_list[KF_bpf_res_spin_lock_irqsave]) ||
+ is_kfunc_call(meta, special_kfunc_list[KF_bpf_res_spin_unlock_irqrestore]))
+ flags |= PROCESS_LOCK_IRQ;
+ return flags;
}
static bool kfunc_spin_allowed(struct bpf_verifier_env *env, s32 func_id, s16 offset)
@@ -12959,440 +13347,6 @@ static bool check_css_task_iter_allowlist(struct bpf_verifier_env *env)
}
}
-static int check_kfunc_args(struct bpf_verifier_env *env, struct bpf_call_arg_meta *meta,
- int insn_idx)
-{
- const char *func_name = meta->func_name;
- struct bpf_func_state *caller = cur_func(env);
- struct bpf_reg_state *regs = cur_regs(env);
- const struct btf *btf = meta->btf;
- const struct btf_param *args;
- struct btf_record *rec;
- u32 i, nargs;
- int ret;
-
- args = (const struct btf_param *)(meta->func_proto + 1);
- nargs = btf_type_vlen(meta->func_proto);
-
- ret = check_outgoing_stack_args(env, caller, nargs, func_name, btf, args);
- if (ret)
- return ret;
-
- /* Check that BTF function arguments match actual types that the
- * verifier sees.
- */
- for (i = 0; i < nargs; i++) {
- struct bpf_reg_state *reg = get_func_arg_reg(caller, regs, i);
- enum bpf_arg_type arg_type = meta->fn->arg_type[i];
- const struct btf_type *t;
- argno_t argno = argno_from_arg(i + 1);
- int regno = reg_from_argno(argno);
- u32 ref_id = args[i].type;
- u32 arg_size = arg_type & MEM_FIXED_SIZE ? meta->fn->arg_size[i] : 0;
-
- if (arg_type == ARG_PTR_TO_PROG_AUX) {
- cur_aux(env)->arg_prog = regno;
- continue;
- }
-
- if (arg_type == ARG_IGNORE)
- continue;
-
- t = btf_type_skip_modifiers(btf, args[i].type, NULL);
-
- if (btf_type_is_ptr(t))
- btf_type_skip_modifiers(btf, t->type, &ref_id);
-
- ret = check_func_arg_nullability(env, reg, argno, arg_type, meta, insn_idx);
- if (ret < 0)
- return ret;
-
- ret = check_func_arg_release(env, reg, argno, arg_type, meta, insn_idx);
- if (ret < 0)
- return ret;
-
- if (reg_is_referenced(env, reg))
- update_ref_obj(&meta->ref_obj, reg);
-
- if (bpf_register_is_null(reg) && type_may_be_null(arg_type))
- continue;
-
- if (base_type(arg_type) == ARG_PTR_TO_BTF_ID)
- ref_id = *meta->fn->arg_btf_id[i];
-
- ret = resolve_func_arg_type(env, reg, i, meta, &arg_type, &arg_size);
- if (ret < 0)
- return ret;
-
- ret = check_reg_type(env, reg, argno, arg_type, meta);
- if (ret < 0)
- return ret;
-
- ret = check_func_arg_reg_off(env, reg, argno, arg_type);
- if (ret < 0)
- return ret;
-
- switch (base_type(arg_type)) {
- case ARG_CONST_SCALAR:
- ret = process_const_arg(env, reg, argno, meta);
- if (ret < 0) {
- if (ret == -EINVAL)
- bpf_diag_call_arg_fmt(env, insn_idx, argno, func_name,
- "Pass a compile-time constant or a value the verifier can prove is constant at this call.",
- "the kfunc requires this scalar argument to be a verifier-known constant, but %s is variable on this path",
- reg_arg_name(env, argno));
- return ret;
- }
- break;
- case ARG_SCALAR:
- break;
- case ARG_CONST_ALLOC_SIZE_OR_ZERO:
- if (is_kfunc_arg_scalar_with_name(btf, &args[i], "rdonly_buf_size"))
- meta->r0_rdonly = true;
- ret = process_const_alloc_mem_size(env, reg, argno, &meta->ret_mem);
- if (ret < 0) {
- if (ret == -EINVAL)
- bpf_diag_call_arg_fmt(env, insn_idx, argno, func_name,
- "Pass a verifier-known constant size for this kfunc buffer argument.",
- "the kfunc uses this argument as a return-buffer size, but %s is invalid or variable on this path",
- reg_arg_name(env, argno));
- return ret;
- }
- break;
- case ARG_PTR_TO_CTX:
- if (is_kfunc_call(meta, special_kfunc_list[KF_bpf_cast_to_kern_ctx])) {
- ret = get_kern_ctx_btf_id(&env->log, resolve_prog_type(env->prog));
- if (ret < 0)
- return -EINVAL;
- meta->ret_btf_id = ret;
- }
- break;
- case ARG_PTR_TO_ARENA:
- break;
- case ARG_PTR_TO_ALLOC_BTF_ID:
- if (reg->type == (PTR_TO_BTF_ID | MEM_ALLOC)) {
- if (!is_bpf_obj_drop_kfunc(meta->func_id)) {
- verbose(env, "%s expected for bpf_obj_drop()\n",
- reg_arg_name(env, argno));
- return -EINVAL;
- }
- } else if (reg->type == (PTR_TO_BTF_ID | MEM_ALLOC | MEM_PERCPU)) {
- if (!is_bpf_percpu_obj_drop_kfunc(meta->func_id)) {
- verbose(env, "%s expected for bpf_percpu_obj_drop()\n",
- reg_arg_name(env, argno));
- return -EINVAL;
- }
- }
- if (!reg_is_referenced(env, reg)) {
- verbose(env, "allocated object must be referenced\n");
- bpf_diag_call_arg_fmt(env, insn_idx, argno, func_name,
- "Pass the owned object pointer before it is released or transferred.",
- "the allocated object pointer in %s must still carry verifier-tracked ownership, but this pointer no longer owns a live resource",
- reg_arg_name(env, argno));
- return -EINVAL;
- }
- if (meta->btf == btf_vmlinux) {
- meta->arg_btf = reg->btf;
- meta->arg_btf_id = reg->btf_id;
- }
- break;
- case ARG_PTR_TO_DYNPTR:
- {
- enum bpf_arg_type dynptr_arg_type = arg_type;
-
- if (is_kfunc_call(meta, special_kfunc_list[KF_bpf_dynptr_clone]) &&
- (dynptr_arg_type & MEM_UNINIT)) {
- enum bpf_dynptr_type parent_type = meta->dynptr.type;
-
- if (parent_type == BPF_DYNPTR_TYPE_INVALID) {
- verifier_bug(env, "no dynptr type for parent of clone");
- return -EFAULT;
- }
-
- dynptr_arg_type |= (unsigned int)get_dynptr_type_flag(parent_type);
- }
-
- ret = process_dynptr_func(env, reg, argno, insn_idx,
- dynptr_arg_type, meta);
- if (ret < 0)
- return ret;
- break;
- }
- case ARG_PTR_TO_ITER:
- if (is_kfunc_call(meta, special_kfunc_list[KF_bpf_iter_css_task_new])) {
- if (!check_css_task_iter_allowlist(env)) {
- verbose(env, "css_task_iter is only allowed in bpf_lsm, bpf_iter and sleepable progs\n");
- return -EINVAL;
- }
- }
- ret = process_iter_arg(env, reg, argno, insn_idx, meta);
- if (ret < 0)
- return ret;
- break;
- case ARG_PTR_TO_LIST_HEAD:
- if (reg->type == (PTR_TO_BTF_ID | MEM_ALLOC) &&
- !reg_is_referenced(env, reg)) {
- verbose(env, "allocated object must be referenced\n");
- return -EINVAL;
- }
- ret = process_kf_arg_ptr_to_list_head(env, reg, argno, meta);
- if (ret < 0)
- return ret;
- break;
- case ARG_PTR_TO_RB_ROOT:
- if (reg->type == (PTR_TO_BTF_ID | MEM_ALLOC) &&
- !reg_is_referenced(env, reg)) {
- verbose(env, "allocated object must be referenced\n");
- return -EINVAL;
- }
- ret = process_kf_arg_ptr_to_rbtree_root(env, reg, argno, meta);
- if (ret < 0)
- return ret;
- break;
- case ARG_PTR_TO_LIST_NODE:
- if (is_kfunc_arg_nonown_allowed(btf, &args[i]) &&
- type_is_non_owning_ref(reg->type) && !reg_is_referenced(env, reg)) {
- /* Allow bpf_list_front/back return value for
- * __nonown_allowed list-node arguments.
- */
- goto check_ok;
- }
- if (reg->type != (PTR_TO_BTF_ID | MEM_ALLOC)) {
- verbose(env, "%s expected pointer to allocated object\n",
- reg_arg_name(env, argno));
- return -EINVAL;
- }
- if (!reg_is_referenced(env, reg)) {
- verbose(env, "allocated object must be referenced\n");
- return -EINVAL;
- }
-check_ok:
- ret = process_kf_arg_ptr_to_list_node(env, reg, argno, meta);
- if (ret < 0)
- return ret;
- break;
- case ARG_PTR_TO_RB_NODE:
- if (is_bpf_rbtree_add_kfunc(meta->func_id)) {
- if (reg->type != (PTR_TO_BTF_ID | MEM_ALLOC)) {
- verbose(env, "%s expected pointer to allocated object\n",
- reg_arg_name(env, argno));
- return -EINVAL;
- }
- if (!reg_is_referenced(env, reg)) {
- verbose(env, "allocated object must be referenced\n");
- return -EINVAL;
- }
- } else {
- if (!type_is_non_owning_ref(reg->type) &&
- !reg_is_referenced(env, reg)) {
- verbose(env, "%s can only take non-owning or refcounted bpf_rb_node pointer\n", func_name);
- return -EINVAL;
- }
- if (in_rbtree_lock_required_cb(env)) {
- verbose(env, "%s not allowed in rbtree cb\n", func_name);
- return -EINVAL;
- }
- }
-
- ret = process_kf_arg_ptr_to_rbtree_node(env, reg, argno, meta);
- if (ret < 0)
- return ret;
- break;
- case ARG_CONST_MAP_PTR:
- ret = process_map_ptr_arg(env, reg, argno, meta);
- if (ret < 0)
- return ret;
- break;
- case ARG_PTR_TO_BTF_ID:
- if (!is_trusted_reg(env, reg) ||
- bpf_type_has_unsafe_modifiers(reg->type)) {
- if (!(arg_type & MEM_RCU)) {
- const char *actual_type, *arg_name, *expected_type;
-
- expected_type = bpf_diag_fmt_btf_type(env, btf, ref_id);
- verbose(env, "%s must be referenced or trusted\n",
- reg_arg_name(env, argno));
- arg_name = reg_arg_name(env, argno);
- actual_type = bpf_diag_reg_type_plain(env, reg->type);
- bpf_diag_call_arg_fmt(env, insn_idx, argno, func_name,
- "Pass a pointer acquired from a verifier-tracked source, or call this kfunc only inside the required protection if it accepts RCU pointers.",
- "the kfunc requires a trusted or resource-owning pointer to %s, but %s is %s",
- expected_type, arg_name, actual_type);
- return -EINVAL;
- }
- if (!is_rcu_reg(reg)) {
- const char *actual_type, *arg_name, *expected_type;
-
- expected_type = bpf_diag_fmt_btf_type(env, btf, ref_id);
- verbose(env, "%s must be a rcu pointer\n",
- reg_arg_name(env, argno));
- arg_name = reg_arg_name(env, argno);
- actual_type = bpf_diag_reg_type_plain(env, reg->type);
- bpf_diag_call_arg_fmt(env, insn_idx, argno, func_name,
- "Use this kfunc with a pointer that is valid in an RCU read lock region.",
- "the kfunc requires an RCU-protected pointer to %s, but %s is %s",
- expected_type, arg_name, actual_type);
- return -EINVAL;
- }
- }
-
- ret = process_arg_ptr_to_btf_id(env, reg, argno, arg_type, btf,
- ref_id, meta, insn_idx);
- if (ret < 0)
- return ret;
- break;
- case ARG_PTR_TO_MEM:
- if (arg_type & MEM_FIXED_SIZE) {
- bool known_memory;
-
- ret = check_mem_reg(env, reg, argno, arg_size, BPF_READ | BPF_WRITE,
- meta, &known_memory);
- if (ret < 0) {
- const char *expected_type;
-
- expected_type = bpf_diag_fmt_btf_type(env, btf, ref_id);
- if (known_memory)
- bpf_diag_call_arg_fmt(
- env, insn_idx, argno, func_name,
- "Pass memory with at least the required number of accessible bytes and suitable read and write access.",
- "the kfunc expects %u bytes of memory for %s, but the verifier cannot prove that %s provides a readable and writable range of that size",
- arg_size, expected_type,
- bpf_diag_reg_type_plain(env, reg->type));
- else
- bpf_diag_call_arg_fmt(
- env, insn_idx, argno, func_name,
- "Pass stack, map, context, or other verifier-known memory of the expected type and size, not an integer cast to a pointer.",
- "the kfunc expects %u bytes of memory for %s, but it is %s and not verifier-known memory",
- arg_size, expected_type,
- bpf_diag_reg_type_plain(env, reg->type));
- return ret;
- }
- }
- break;
- case ARG_CONST_MEM_SIZE:
- ret = process_const_arg(env, reg, argno, meta);
- if (ret < 0) {
- if (ret == -EINVAL)
- bpf_diag_call_arg_fmt(env, insn_idx, argno, func_name,
- "Pass a compile-time constant or a value the verifier can prove is constant at this call.",
- "the kfunc requires this memory size to be a verifier-known constant, but %s is variable on this path",
- reg_arg_name(env, argno));
- return ret;
- }
- fallthrough;
- case ARG_MEM_SIZE:
- {
- struct bpf_reg_state *buff_reg = get_func_arg_reg(caller, regs, i - 1);
- struct bpf_reg_state *size_reg = reg;
- argno_t buff_argno = argno_from_arg(i);
- enum bpf_mem_size_failure failure;
-
- if (bpf_register_is_null(buff_reg))
- break;
-
- ret = check_mem_size_reg(env, buff_reg, size_reg, buff_argno, argno,
- BPF_READ | BPF_WRITE, true, meta, &failure);
- if (ret < 0) {
- const char *buff_arg, *size_arg;
-
- buff_arg = bpf_diag_arg_name(env, buff_argno);
- size_arg = bpf_diag_arg_name(env, argno);
- verbose(env, "%s and ", reg_arg_name(env, buff_argno));
- verbose(env, "%s memory, len pair leads to invalid memory access\n",
- reg_arg_name(env, argno));
- if (failure == BPF_MEM_SIZE_FAIL_MEMORY) {
- bpf_diag_call_arg_fmt(env, insn_idx, buff_argno, func_name,
- "Pass a stack, map, context, or other verifier-known memory pointer, and keep the paired length within that object.",
- "it is the memory pointer in a memory/length pair with %s, but %s does not describe verifier-readable memory for the requested length",
- size_arg, buff_arg);
- } else if (failure == BPF_MEM_SIZE_FAIL_SIZE) {
- if (reg_smin(size_reg) < 0)
- bpf_diag_call_arg_fmt(
- env, insn_idx, argno, func_name,
- "Constrain the memory size to a non-negative value smaller than BPF_MAX_VAR_SIZ before this call.",
- "the memory size in %s may be negative because its signed minimum is %lld",
- size_arg, reg_smin(size_reg));
- else
- bpf_diag_call_arg_fmt(
- env, insn_idx, argno, func_name,
- "Constrain the memory size to a non-negative value smaller than BPF_MAX_VAR_SIZ before this call.",
- "the memory size in %s may reach %llu bytes, but variable memory accesses must stay below %u bytes",
- size_arg, reg_umax(size_reg), BPF_MAX_VAR_SIZ);
- }
- return ret;
- }
- break;
- }
- case ARG_PTR_TO_FUNC:
- meta->subprogno = reg->subprogno;
- break;
- case ARG_PTR_TO_REFCOUNTED_KPTR:
- if (!type_is_non_owning_ref(reg->type))
- meta->arg_owning_ref = true;
-
- rec = reg_btf_record(reg);
- if (!rec) {
- verifier_bug(env, "Couldn't find btf_record");
- return -EFAULT;
- }
-
- if (rec->refcount_off < 0) {
- verbose(env, "%s doesn't point to a type with bpf_refcount field\n",
- reg_arg_name(env, argno));
- return -EINVAL;
- }
-
- meta->arg_btf = reg->btf;
- meta->arg_btf_id = reg->btf_id;
- break;
- case ARG_PTR_TO_CONST_STR:
- ret = check_arg_const_str(env, reg, argno);
- if (ret)
- return ret;
- break;
- case ARG_PTR_TO_WORKQUEUE:
- ret = check_map_field_pointer(env, reg, argno, BPF_WORKQUEUE, &meta->map);
- if (ret < 0)
- return ret;
- break;
- case ARG_PTR_TO_TIMER:
- ret = process_timer_func(env, reg, argno, &meta->map);
- if (ret < 0)
- return ret;
- break;
- case ARG_PTR_TO_TASK_WORK:
- ret = check_map_field_pointer(env, reg, argno, BPF_TASK_WORK, &meta->map);
- if (ret < 0)
- return ret;
- break;
- case ARG_PTR_TO_IRQ_FLAG:
- ret = process_irq_flag(env, reg, argno, meta);
- if (ret < 0)
- return ret;
- break;
- case ARG_PTR_TO_RES_SPIN_LOCK:
- {
- int flags = PROCESS_RES_LOCK;
-
- if (!is_bpf_res_spin_lock_kfunc(meta->func_id))
- return -EFAULT;
- if (is_kfunc_call(meta, special_kfunc_list[KF_bpf_res_spin_lock]) ||
- is_kfunc_call(meta, special_kfunc_list[KF_bpf_res_spin_lock_irqsave]))
- flags |= PROCESS_SPIN_LOCK;
- if (is_kfunc_call(meta, special_kfunc_list[KF_bpf_res_spin_lock_irqsave]) ||
- is_kfunc_call(meta,
- special_kfunc_list[KF_bpf_res_spin_unlock_irqrestore]))
- flags |= PROCESS_LOCK_IRQ;
- ret = process_spin_lock(env, reg, argno, flags);
- if (ret < 0)
- return ret;
- break;
- }
- }
- }
-
- return 0;
-}
int bpf_fetch_kfunc_arg_meta(struct bpf_verifier_env *env,
s32 func_id,
@@ -13885,7 +13839,7 @@ static int check_kfunc_call(struct bpf_verifier_env *env, struct bpf_insn *insn,
insn_aux->non_sleepable = true;
/* Check the arguments */
- err = check_kfunc_args(env, &meta, insn_idx);
+ err = check_func_args(env, &meta, insn_idx);
if (err < 0)
return err;
diff --git a/tools/testing/selftests/bpf/prog_tests/cb_refs.c b/tools/testing/selftests/bpf/prog_tests/cb_refs.c
index 8743f52e755d..504d395d5962 100644
--- a/tools/testing/selftests/bpf/prog_tests/cb_refs.c
+++ b/tools/testing/selftests/bpf/prog_tests/cb_refs.c
@@ -11,7 +11,7 @@ struct {
const char *prog_name;
const char *err_msg;
} cb_refs_tests[] = {
- { "underflow_prog", "release function bpf_kfunc_call_test_release expects referenced PTR_TO_BTF_ID passed to R1" },
+ { "underflow_prog", "R1 type=scalar expected=ptr_, trusted_ptr_, rcu_ptr_" },
{ "leak_prog", "Unreleased reference id=4 alloc_insn=33" },
{ "nested_cb", "Unreleased reference id=4 alloc_insn=2" }, /* alloc_insn=2{4,5} */
{ "non_cb_transfer_ref", "Unreleased reference id=4 alloc_insn=1" }, /* alloc_insn=1{1,2} */
diff --git a/tools/testing/selftests/bpf/progs/cgrp_kfunc_failure.c b/tools/testing/selftests/bpf/progs/cgrp_kfunc_failure.c
index 8de2206875ab..ede6a17d7da3 100644
--- a/tools/testing/selftests/bpf/progs/cgrp_kfunc_failure.c
+++ b/tools/testing/selftests/bpf/progs/cgrp_kfunc_failure.c
@@ -64,7 +64,7 @@ int BPF_PROG(cgrp_kfunc_acquire_no_null_check, struct cgroup *cgrp, const char *
}
SEC("tp_btf/cgroup_mkdir")
-__failure __msg("R1 is fp expected STRUCT cgroup")
+__failure __msg("R1 type=fp expected=ptr_, trusted_ptr_, rcu_ptr_")
int BPF_PROG(cgrp_kfunc_acquire_fp, struct cgroup *cgrp, const char *path)
{
struct cgroup *acquired, *stack_cgrp = (struct cgroup *)&path;
@@ -191,7 +191,7 @@ int BPF_PROG(cgrp_kfunc_release_untrusted, struct cgroup *cgrp, const char *path
}
SEC("tp_btf/cgroup_mkdir")
-__failure __msg("release function bpf_cgroup_release expects referenced PTR_TO_BTF_ID passed to R1")
+__failure __msg("R1 type=fp expected=ptr_, trusted_ptr_, rcu_ptr_")
int BPF_PROG(cgrp_kfunc_release_fp, struct cgroup *cgrp, const char *path)
{
struct cgroup *acquired = (struct cgroup *)&path;
diff --git a/tools/testing/selftests/bpf/progs/task_kfunc_failure.c b/tools/testing/selftests/bpf/progs/task_kfunc_failure.c
index 639276591902..12c8ac6099ca 100644
--- a/tools/testing/selftests/bpf/progs/task_kfunc_failure.c
+++ b/tools/testing/selftests/bpf/progs/task_kfunc_failure.c
@@ -50,7 +50,7 @@ int BPF_PROG(task_kfunc_acquire_untrusted, struct task_struct *task, u64 clone_f
}
SEC("tp_btf/task_newtask")
-__failure __msg("R1 is fp expected STRUCT task_struct")
+__failure __msg("R1 type=fp expected=ptr_, trusted_ptr_, rcu_ptr_")
int BPF_PROG(task_kfunc_acquire_fp, struct task_struct *task, u64 clone_flags)
{
struct task_struct *acquired, *stack_task = (struct task_struct *)&clone_flags;
@@ -179,7 +179,7 @@ int BPF_PROG(task_kfunc_release_untrusted, struct task_struct *task, u64 clone_f
}
SEC("tp_btf/task_newtask")
-__failure __msg("release function bpf_task_release expects referenced PTR_TO_BTF_ID passed to R1")
+__failure __msg("R1 type=fp expected=ptr_, trusted_ptr_, rcu_ptr_")
int BPF_PROG(task_kfunc_release_fp, struct task_struct *task, u64 clone_flags)
{
struct task_struct *acquired = (struct task_struct *)&clone_flags;
@@ -333,7 +333,7 @@ int BPF_PROG(task_access_comm2, struct task_struct *task, u64 clone_flags)
}
SEC("tp_btf/task_newtask")
-__failure __msg("write into memory")
+__failure __msg("only read is supported")
int BPF_PROG(task_access_comm3, struct task_struct *task, u64 clone_flags)
{
bpf_probe_read_kernel(task->comm, 16, task->comm);
diff --git a/tools/testing/selftests/bpf/progs/verifier_vfs_reject.c b/tools/testing/selftests/bpf/progs/verifier_vfs_reject.c
index 7d4d894b203e..b5f456d57669 100644
--- a/tools/testing/selftests/bpf/progs/verifier_vfs_reject.c
+++ b/tools/testing/selftests/bpf/progs/verifier_vfs_reject.c
@@ -28,7 +28,7 @@ int BPF_PROG(get_task_exe_file_kfunc_null)
}
SEC("lsm.s/inode_getxattr")
-__failure __msg("R1 is fp expected STRUCT task_struct")
+__failure __msg("R1 type=fp expected=ptr_, trusted_ptr_, rcu_ptr_")
int BPF_PROG(get_task_exe_file_kfunc_fp)
{
u64 x;
diff --git a/tools/testing/selftests/bpf/progs/wakeup_source_fail.c b/tools/testing/selftests/bpf/progs/wakeup_source_fail.c
index ff9ea5aa80ad..ec4e0f3ff792 100644
--- a/tools/testing/selftests/bpf/progs/wakeup_source_fail.c
+++ b/tools/testing/selftests/bpf/progs/wakeup_source_fail.c
@@ -42,7 +42,7 @@ int wakeup_source_access_lock_fields(void *ctx)
}
SEC("syscall")
-__failure __msg("release function bpf_wakeup_sources_read_unlock expects referenced PTR_TO_BTF_ID passed to R1")
+__failure __msg("R1 type=scalar expected=ptr_, trusted_ptr_, rcu_ptr_")
int wakeup_source_unlock_no_lock(void *ctx)
{
struct bpf_ws_lock *lock = (void *)0x1;
--
2.52.0
^ permalink raw reply related [flat|nested] 54+ messages in thread
* Re: [PATCH bpf-next v1 22/22] bpf: Check helper and kfunc arguments in one path
2026-09-05 22:01 ` [PATCH bpf-next v1 22/22] bpf: Check helper and kfunc arguments in one path Amery Hung
@ 2026-09-05 22:33 ` sashiko-bot
2026-09-11 20:59 ` Amery Hung
2026-09-10 21:53 ` Alexei Starovoitov
1 sibling, 1 reply; 54+ messages in thread
From: sashiko-bot @ 2026-09-05 22:33 UTC (permalink / raw)
To: Amery Hung; +Cc: bpf
Thank you for your contribution! Sashiko AI review found 1 potential issue(s) to consider:
- [Critical] Out-of-bounds reads on `meta->fn->arg_type` and `meta->fn->arg_btf_id` for kfuncs with more than 5 arguments, allowing bypass of eBPF verifier safety checks.
--
commit a1895fafaa5219b7b972d20a932c550f7862cec8
Author: Amery Hung <ameryhung@gmail.com>
bpf: Check helper and kfunc arguments in one path
This commit unifies the argument checking logic for BPF helpers and kfuncs
by introducing check_func_args(). It moves kfunc-specific argument handling
into the shared check_func_arg() path to avoid duplicating the argument
iteration and switch logic.
> diff --git a/kernel/bpf/verifier.c b/kernel/bpf/verifier.c
> index 6c5d7e544f844..54656fb31bbe7 100644
> --- a/kernel/bpf/verifier.c
> +++ b/kernel/bpf/verifier.c
[ ... ]
> @@ -8808,6 +8840,7 @@ static int check_func_arg(struct bpf_verifier_env *env, u32 arg,
> struct bpf_call_arg_meta *meta,
> int insn_idx)
> {
> + const struct btf_param *btf_arg = meta->btf ? &btf_params(meta->func_proto)[arg] : NULL;
> const struct bpf_func_proto *fn = meta->fn;
> struct bpf_func_state *caller = cur_func(env);
> struct bpf_reg_state *regs = cur_regs(env);
> argno_t argno = argno_from_arg(arg + 1);
> struct bpf_reg_state *reg = get_func_arg_reg(caller, regs, arg);
> enum bpf_arg_type arg_type = fn->arg_type[arg];
[Severity: Critical]
Can this read out of bounds when processing kfuncs with more than 5
arguments? The fn->arg_type array is sized for MAX_BPF_FUNC_ARGS (5), but
kfuncs can have more arguments. For stack arguments (arg >= 5), this appears
to read past the end of the array.
[ ... ]
> @@ -8935,6 +8980,7 @@ static int check_func_arg(struct bpf_verifier_env *env, u32 arg,
> case ARG_PTR_TO_BTF_ID_SOCK_COMMON:
> {
> const u32 *arg_btf_id = fn->arg_btf_id[arg];
[Severity: Critical]
Does this also read out of bounds for kfuncs with more than 5 arguments?
Similar to arg_type, the arg_btf_id array in struct bpf_func_proto is limited
to MAX_BPF_FUNC_ARGS. If a garbage pointer is dereferenced here, could it
crash the verifier?
[ ... ]
> @@ -9056,6 +9389,37 @@ static int check_func_arg(struct bpf_verifier_env *env, u32 arg,
> return err;
> }
>
> +static int check_func_args(struct bpf_verifier_env *env, struct bpf_call_arg_meta *meta,
> + int insn_idx)
> +{
> + struct bpf_func_state *caller = cur_func(env);
> + const struct btf_param *args = NULL;
> + u32 arg, nargs = MAX_BPF_FUNC_REG_ARGS;
> + int err;
> +
> + if (meta->btf) {
> + args = btf_params(meta->func_proto);
> + nargs = btf_type_vlen(meta->func_proto);
> + }
> +
> + if (nargs > MAX_BPF_FUNC_REG_ARGS) {
> + err = check_outgoing_stack_args(env, caller, nargs, meta->func_name,
> + meta->btf, args);
> + if (err)
> + return err;
> + }
> +
> + for (arg = 0; arg < nargs; arg++) {
> + if (meta->fn->arg_type[arg] == ARG_UNUSED)
> + break;
[Severity: Critical]
Could this loop early-exit and bypass verifier checks for stack arguments?
Since nargs can exceed 5 for kfuncs, the out-of-bounds read on
meta->fn->arg_type[arg] might evaluate to ARG_UNUSED (0). If this happens,
the loop breaks and skips the safety checks for all remaining stack
arguments, potentially allowing unverified memory access.
> + err = check_func_arg(env, arg, meta, insn_idx);
> + if (err)
> + return err;
> + }
> +
> + return 0;
> +}
> +
--
Sashiko AI review · https://sashiko.dev/#/patchset/20260905220117.922028-1-ameryhung@gmail.com?part=22
^ permalink raw reply [flat|nested] 54+ messages in thread
* Re: [PATCH bpf-next v1 19/22] selftests/bpf: Test kfunc packet memory direct writes
2026-09-05 22:01 ` [PATCH bpf-next v1 19/22] selftests/bpf: Test kfunc packet memory direct writes Amery Hung
@ 2026-09-05 22:44 ` bot+bpf-ci
2026-09-11 20:46 ` Amery Hung
0 siblings, 1 reply; 54+ messages in thread
From: bot+bpf-ci @ 2026-09-05 22:44 UTC (permalink / raw)
To: ameryhung, bpf
Cc: alexei.starovoitov, andrii, daniel, eddyz87, memxor, ameryhung,
kernel-team, ast, andrii, daniel, martin.lau, eddyz87,
yonghong.song, mason, ihor.solodrai
[-- Attachment #1: Type: text/plain, Size: 3765 bytes --]
> diff --git a/tools/testing/selftests/bpf/progs/verifier_kfunc_packet_access.c b/tools/testing/selftests/bpf/progs/verifier_kfunc_packet_access.c
> new file mode 100644
> index 000000000000..9ae63c9eab02
> --- /dev/null
> +++ b/tools/testing/selftests/bpf/progs/verifier_kfunc_packet_access.c
> @@ -0,0 +1,47 @@
> +// SPDX-License-Identifier: GPL-2.0
> +
> +#include <vmlinux.h>
> +#include <bpf/bpf_helpers.h>
> +#include "bpf_misc.h"
> +
> +struct nf_conn *bpf_skb_ct_lookup(struct __sk_buff *skb_ctx,
> + struct bpf_sock_tuple *bpf_tuple,
> + u32 tuple__sz, struct bpf_ct_opts *opts,
> + u32 opts__sz) __ksym;
> +void bpf_ct_release(struct nf_conn *nfct) __ksym;
> +
> +char _license[] SEC("license") = "GPL";
> +
> +SEC("tc")
> +__description("kfunc packet write requests writable skb")
> +__success
> +/* bpf_unclone_prologue() */
> +__xlated("r6 = *(u8 *)(r1 +{{[0-9]+}})")
> +__xlated("...")
> +__xlated("w6 &= {{1|128}}")
Is this test expected to pass at this commit? Looking at the verifier
code at this commit, the kfunc memory-argument path goes through
check_helper_mem_access() for PTR_TO_PACKET, which calls
check_packet_access() directly:
kernel/bpf/verifier.c:check_helper_mem_access():
case PTR_TO_PACKET:
case PTR_TO_PACKET_META:
return check_packet_access(env, reg, argno, 0, access_size,
zero_size_allowed);
This path never calls may_access_direct_pkt_data(), so
env->seen_direct_write remains false. Without seen_direct_write set,
net/core/filter.c:tc_cls_act_prologue() -> bpf_unclone_prologue()
returns 0:
net/core/filter.c:bpf_unclone_prologue():
if (!direct_write)
return 0;
The kfunc_packet_write() program performs no direct packet store, so
it won't trigger the may_access_direct_pkt_data() call that would set
env->seen_direct_write. This means no prologue instructions are emitted
and the __xlated() patterns from here to the end would fail to match.
This appears to be fixed by a later commit in the series that moves the
check into check_helper_mem_access(). Should this test be reordered to
appear after that commit to avoid breaking bisection?
> +__xlated("...")
> +__xlated("if r6 == 0x0 goto")
> +__xlated("r6 = r1")
> +__xlated("r2 ^= r2")
> +__xlated("call")
> +__xlated("if r0 == 0x0 goto")
> +__xlated("w0 = 2")
> +__xlated("...")
> +__xlated("exit")
> +__xlated("r1 = r6")
> +int kfunc_packet_write(struct __sk_buff *skb)
> +{
> + void *data_end = (void *)(long)skb->data_end;
> + void *data = (void *)(long)skb->data;
> + struct bpf_sock_tuple tuple = {};
> + struct nf_conn *nfct;
> +
> + if (data + sizeof(struct bpf_ct_opts) > data_end)
> + return 0;
> +
> + /* An invalid tuple size makes bpf_skb_ct_lookup() write opts->error. */
> + nfct = bpf_skb_ct_lookup(skb, &tuple, 1, data, sizeof(struct bpf_ct_opts));
> + if (nfct)
> + bpf_ct_release(nfct);
> + return 0;
> +}
Going back to the alternation pattern above:
> +__xlated("w6 &= {{1|128}}")
This isn't a bug, but does the regex match what the commit message
describes? The pattern text "w6 &= {{1|128}}" compiles to the POSIX ERE
"w6 &= 1|128" since tools/testing/selftests/bpf/test_loader.c only
escapes metacharacters outside {{ }}. Without grouping, this is
(w6 &= 1)|(128) due to '|' having lowest precedence, so the second
alternative matches a bare "128" anywhere in the xlated dump rather than
constraining the cloned-bit mask value. Should this be "w6 &= {{(1|128)}}"
to properly match the endian-specific mask?
---
AI reviewed your patch. Please fix the bug or email reply why it's not a bug.
See: https://github.com/kernel-patches/vmtest/blob/master/ci/claude/README.md
CI run summary: https://github.com/kernel-patches/bpf/actions/runs/33995442755
^ permalink raw reply [flat|nested] 54+ messages in thread
* Re: [PATCH bpf-next v1 02/22] bpf: Address check_func_arg() arguments by argno
2026-09-05 22:00 ` [PATCH bpf-next v1 02/22] bpf: Address check_func_arg() arguments by argno Amery Hung
@ 2026-09-05 22:44 ` bot+bpf-ci
2026-09-09 17:37 ` Amery Hung
0 siblings, 1 reply; 54+ messages in thread
From: bot+bpf-ci @ 2026-09-05 22:44 UTC (permalink / raw)
To: ameryhung, bpf
Cc: alexei.starovoitov, andrii, daniel, eddyz87, memxor, ameryhung,
kernel-team, ast, andrii, daniel, martin.lau, eddyz87,
yonghong.song, mason, ihor.solodrai
[-- Attachment #1: Type: text/plain, Size: 3840 bytes --]
> check_func_arg() derives every argument from BPF_REG_1 + arg. That works
> for helpers, which have at most five arguments, but kfunc arguments
> beyond the fifth are passed on the stack.
>
> Switch to the argno_t addressing used by check_kfunc_args(): obtain
> argument state with get_func_arg_reg(), use argno for diagnostics, and
> call check_reg_arg() only when the argument is held in a register. This
> prepares check_func_arg() to be shared with kfunc verification.
>
> There is no functional change for helpers. reg_arg_name() renders their
> register arguments as R%d, so the updated diagnostics remain unchanged.
The changelog lists three edits this patch makes, but only two of them
appear in the diff. The first two changes are present: "obtain argument
state with get_func_arg_reg()" and "use argno for diagnostics" are both
implemented. However, the third change, "call check_reg_arg() only when
the argument is held in a register", is not in this commit.
> diff --git a/kernel/bpf/verifier.c b/kernel/bpf/verifier.c
> index f49c90beefc5..30e1f7a7553e 100644
> --- a/kernel/bpf/verifier.c
> +++ b/kernel/bpf/verifier.c
> @@ -8769,10 +8769,12 @@ static int check_func_arg(struct bpf_verifier_env *env, u32 arg,
> int insn_idx)
> {
> const struct bpf_func_proto *fn = meta->fn;
> - u32 regno = BPF_REG_1 + arg;
> - struct bpf_reg_state *reg = reg_state(env, regno);
> + struct bpf_func_state *caller = cur_func(env);
> + struct bpf_reg_state *regs = cur_regs(env);
> + argno_t argno = argno_from_arg(arg + 1);
> + struct bpf_reg_state *reg = get_func_arg_reg(caller, regs, arg);
> enum bpf_arg_type arg_type = fn->arg_type[arg];
> - argno_t argno = argno_from_reg(regno);
> + int regno = reg_from_argno(argno);
> enum bpf_reg_type type = reg->type;
[ ... ]
> @@ -8786,9 +8788,9 @@ static int check_func_arg(struct bpf_verifier_env *env, u32 arg,
> return err;
>
> if (arg_type == ARG_ANYTHING) {
check_func_arg() still calls check_reg_arg() unconditionally:
check_func_arg() {
argno_t argno = argno_from_arg(arg + 1);
struct bpf_reg_state *reg = get_func_arg_reg(caller, regs, arg);
enum bpf_arg_type arg_type = fn->arg_type[arg];
int regno = reg_from_argno(argno);
...
err = check_reg_arg(env, regno, SRC_OP); /* no guard checking regno >= 0 */
if (err)
return err;
The guard that would implement the third changelog item appears 20 commits
later in the same series. Commit a490f49b0c4e ("bpf: Check helper and kfunc
arguments in one path") adds the conditional guard:
- err = check_reg_arg(env, regno, SRC_OP);
+ if (regno >= 0) {
+ err = check_reg_arg(env, regno, SRC_OP);
with its own changelog explaining it will "skip check_reg_arg() for
stack-passed arguments that check_outgoing_stack_args() already validated."
This matters for the stated goal of preparing check_func_arg() to be shared
with kfunc verification, because the new regno can now be negative.
reg_from_argno() returns -1 for an argno below -MAX_BPF_FUNC_REG_ARGS (for
a stack-passed kfunc argument), and __check_reg_arg() takes a u32 regno and
indexes regs[regno] with no bounds check, so a -1 would become an
out-of-bounds index.
There is no runtime regression at this commit: check_func_arg()'s only
caller is check_helper_call(), whose loop is "for (i = 0; i <
MAX_BPF_FUNC_REG_ARGS; i++)", so arg is always in [0,4] and regno is
always 1..5.
Would it be more accurate to drop the "call check_reg_arg() only when the
argument is held in a register" clause from this commit message, or move it
to the patch that actually implements it?
---
AI reviewed your patch. Please fix the bug or email reply why it's not a bug.
See: https://github.com/kernel-patches/vmtest/blob/master/ci/claude/README.md
CI run summary: https://github.com/kernel-patches/bpf/actions/runs/33995442755
^ permalink raw reply [flat|nested] 54+ messages in thread
* Re: [PATCH bpf-next v1 11/22] bpf: Set MEM_RCU when generating kfunc argument types
2026-09-05 22:01 ` [PATCH bpf-next v1 11/22] bpf: Set MEM_RCU when generating kfunc argument types Amery Hung
@ 2026-09-05 22:44 ` bot+bpf-ci
2026-09-09 18:41 ` Amery Hung
0 siblings, 1 reply; 54+ messages in thread
From: bot+bpf-ci @ 2026-09-05 22:44 UTC (permalink / raw)
To: ameryhung, bpf
Cc: alexei.starovoitov, andrii, daniel, eddyz87, memxor, ameryhung,
kernel-team, ast, andrii, daniel, martin.lau, eddyz87,
yonghong.song, mason, ihor.solodrai
[-- Attachment #1: Type: text/plain, Size: 2075 bytes --]
> diff --git a/kernel/bpf/verifier.c b/kernel/bpf/verifier.c
> index f43462a43ee9..c5b394e847e6 100644
> --- a/kernel/bpf/verifier.c
> +++ b/kernel/bpf/verifier.c
> @@ -12239,6 +12239,14 @@ get_kfunc_arg_type(struct bpf_verifier_env *env, struct bpf_call_arg_meta *meta,
> if (is_kfunc_release(meta) && arg == 0)
> arg_type |= OBJ_RELEASE;
>
> + /*
> + * A KF_RCU kfunc accepts an RCU-protected pointer where it would
> + * otherwise demand a referenced or trusted one. Only ARG_PTR_TO_BTF_ID
> + * looks at where its register came from, so leave the other kinds alone.
> + */
> + if (base_type(arg_type) == ARG_PTR_TO_BTF_ID && is_kfunc_rcu(meta))
> + arg_type |= MEM_RCU;
> +
This isn't a bug, but would it be clearer to state the actual reason
for the ARG_PTR_TO_BTF_ID guard here? The comment says only
ARG_PTR_TO_BTF_ID looks at register provenance, but several other
kinds handled in this same function do inspect provenance:
ARG_PTR_TO_ALLOC_BTF_ID and ARG_PTR_TO_REFCOUNTED_KPTR check
reg_is_referenced()/type_is_ptr_alloc_obj(), and
ARG_PTR_TO_LIST_NODE/ARG_PTR_TO_RB_NODE/ARG_PTR_TO_LIST_HEAD/ARG_PTR_TO_RB_ROOT
check type_is_non_owning_ref().
The guard is actually needed for the reason the changelog gives: to
prevent MEM_RCU from leaking into arguments whose handling compares
the whole arg_type, like the ARG_PTR_TO_CTX test in
check_func_arg_reg_off().
> return arg_type;
> }
>
> @@ -13174,7 +13182,7 @@ static int check_kfunc_args(struct bpf_verifier_env *env, struct bpf_call_arg_me
> reg2btf_ids[base_type(reg->type)]) {
> if (!is_trusted_reg(env, reg) ||
> bpf_type_has_unsafe_modifiers(reg->type)) {
> - if (!is_kfunc_rcu(meta)) {
> + if (!(arg_type & MEM_RCU)) {
> const char *expected_type;
>
> expected_type = bpf_diag_fmt_btf_type(env, btf, ref_id);
---
AI reviewed your patch. Please fix the bug or email reply why it's not a bug.
See: https://github.com/kernel-patches/vmtest/blob/master/ci/claude/README.md
CI run summary: https://github.com/kernel-patches/bpf/actions/runs/33995442755
^ permalink raw reply [flat|nested] 54+ messages in thread
* Re: [PATCH bpf-next v1 04/22] bpf: Only compare func_id against kfunc BTF IDs for kfunc calls
2026-09-05 22:00 ` [PATCH bpf-next v1 04/22] bpf: Only compare func_id against kfunc BTF IDs for kfunc calls Amery Hung
@ 2026-09-05 22:44 ` bot+bpf-ci
2026-09-09 17:47 ` Amery Hung
0 siblings, 1 reply; 54+ messages in thread
From: bot+bpf-ci @ 2026-09-05 22:44 UTC (permalink / raw)
To: ameryhung, bpf
Cc: alexei.starovoitov, andrii, daniel, eddyz87, memxor, ameryhung,
kernel-team, ast, andrii, daniel, martin.lau, eddyz87,
yonghong.song, mason, ihor.solodrai
[-- Attachment #1: Type: text/plain, Size: 1028 bytes --]
> bpf: Only compare func_id against kfunc BTF IDs for kfunc calls
>
> Helpers are identified by enum bpf_func_id, while kfuncs are identified
> by a BTF ID. Both values are stored in bpf_call_arg_meta::func_id, and
> a helper ID can have the same numeric value as a kfunc BTF ID.
>
> Introduce is_kfunc_call(), which first confirms that the metadata is for
> a kfunc through meta->btf, and use it for comparisons against the special
> kfunc BTF ID list. This complements is_helper_call() before later patches
> move these checks into paths shared by helpers and kfuncs.
>
> Signed-off-by: Amery Hung <ameryhung@gmail.com>
This looks like a bug fix for the helper/kfunc metadata unification.
Should this include:
Fixes: bf9c1b911f4d ("bpf: Unify helper and kfunc call argument meta")
---
AI reviewed your patch. Please fix the bug or email reply why it's not a bug.
See: https://github.com/kernel-patches/vmtest/blob/master/ci/claude/README.md
CI run summary: https://github.com/kernel-patches/bpf/actions/runs/33995442755
^ permalink raw reply [flat|nested] 54+ messages in thread
* Re: [PATCH bpf-next v1 08/22] bpf: Classify kfunc arguments the verifier ignores
2026-09-05 22:01 ` [PATCH bpf-next v1 08/22] bpf: Classify kfunc arguments the verifier ignores Amery Hung
@ 2026-09-05 22:44 ` bot+bpf-ci
2026-09-09 18:27 ` Amery Hung
0 siblings, 1 reply; 54+ messages in thread
From: bot+bpf-ci @ 2026-09-05 22:44 UTC (permalink / raw)
To: ameryhung, bpf
Cc: alexei.starovoitov, andrii, daniel, eddyz87, memxor, ameryhung,
kernel-team, ast, andrii, daniel, martin.lau, eddyz87,
yonghong.song, mason, ihor.solodrai
[-- Attachment #1: Type: text/plain, Size: 4325 bytes --]
> bpf: Classify kfunc arguments the verifier ignores
>
> The verifier does not inspect __ign arguments or the implicit
> arguments of a KF_IMPLICIT_ARGS kfunc. get_kfunc_arg_type() leaves
> them unclassified, so gen_kfunc_arg_proto() skips them and
> check_kfunc_args() repeats the same BTF predicates for every call.
>
> Add ARG_IGNORE and classify both cases in get_kfunc_arg_type(). The
> generated prototype can then record every argument, and the call site
> can consume the recorded type without deriving the classification
> again.
>
> No functional change.
>
> Signed-off-by: Amery Hung <ameryhung@gmail.com>
Does the "No functional change." line accurately describe this patch?
Looking at the parent commit 654bdd813138, the gen_kfunc_arg_proto() loop
began with:
kernel/bpf/verifier.c (at 654bdd813138):
for (i = 0; i < nargs; i++) {
if (is_kfunc_arg_ignore(btf, &args[i]) || is_kfunc_arg_implicit(meta, i))
continue;
arg_type = get_kfunc_arg_type(env, meta, args, i, nargs);
...
}
So get_kfunc_arg_type() was never called for any prog_aux argument, since
prog_aux arguments are always implicit. The proto->arg_type[aux_idx] stayed
ARG_UNUSED (0) and could never become ARG_PTR_TO_PROG_AUX.
Then check_kfunc_args() reads this proto:
kernel/bpf/verifier.c (at 654bdd813138):
enum bpf_arg_type arg_type = meta->fn->arg_type[i];
...
if (arg_type == ARG_PTR_TO_PROG_AUX) {
cur_aux(env)->arg_prog = regno;
continue;
}
With ARG_UNUSED recorded, arg_prog stayed 0.
The fixup in kernel/bpf/fixups.c is gated on aux->arg_prog:
kernel/bpf/fixups.c:
if (aux->arg_prog) {
insn_buf[0] = BPF_LD_IMM64(aux->arg_prog, (long)prog->aux);
...
}
So no BPF_LD_IMM64 was emitted and the kfunc was called with an uninitialized
register in place of prog->aux.
This patch fixes that by testing is_kfunc_arg_prog_aux() before the new
ARG_IGNORE test in get_kfunc_arg_type():
> diff --git a/kernel/bpf/verifier.c b/kernel/bpf/verifier.c
> index 22f5aff76b40..7416f1e16aa9 100644
> --- a/kernel/bpf/verifier.c
> +++ b/kernel/bpf/verifier.c
> @@ -12086,6 +12088,9 @@ get_kfunc_arg_type(struct bpf_verifier_env *env, struct bpf_call_arg_meta *meta,
> if (is_kfunc_arg_prog_aux(meta->btf, &args[arg]))
> return ARG_PTR_TO_PROG_AUX;
>
> + if (is_kfunc_arg_ignore(meta->btf, &args[arg]) || is_kfunc_arg_implicit(meta, arg))
> + return ARG_IGNORE;
> +
> t = btf_type_skip_modifiers(meta->btf, args[arg].type, NULL);
and dropping the continue from gen_kfunc_arg_proto():
> @@ -12215,7 +12220,6 @@ get_kfunc_arg_type(struct bpf_verifier_env *env, struct bpf_call_arg_meta *meta,
> static int gen_kfunc_arg_proto(struct bpf_verifier_env *env, struct bpf_call_arg_meta *meta,
> struct bpf_func_proto *proto)
> {
> - const struct btf *btf = meta->btf;
> const struct btf_param *args;
> u32 i, nargs;
> int arg_type;
> @@ -12234,9 +12238,6 @@ static int gen_kfunc_arg_proto(struct bpf_verifier_env *env, struct bpf_call_arg
> }
>
> for (i = 0; i < nargs; i++) {
> - if (is_kfunc_arg_ignore(btf, &args[i]) || is_kfunc_arg_implicit(meta, i))
> - continue;
> -
> arg_type = get_kfunc_arg_type(env, meta, args, i, nargs);
> if (arg_type < 0)
> return arg_type;
so ARG_PTR_TO_PROG_AUX is again recorded in the generated prototype and
arg_prog/BPF_LD_IMM64 are emitted.
This affects bpf_wq_set_callback(), bpf_task_work_schedule_signal()/_resume(),
bpf_stream_vprintk(), bpf_stream_print_stack() and all scx_bpf_* sched_ext
kfuncs.
Should this patch either be squashed with 654bdd813138 or have the "No
functional change." line qualified, since it repairs a bug where kfuncs with a
prog_aux argument were called with an uninitialized register?
Also, the ordering is load-bearing: swapping the two tests in
get_kfunc_arg_type() would silently reintroduce the broken code generation,
since prog_aux arguments are always implicit. Should a comment note that the
prog_aux test must stay ahead of the ARG_IGNORE test?
---
AI reviewed your patch. Please fix the bug or email reply why it's not a bug.
See: https://github.com/kernel-patches/vmtest/blob/master/ci/claude/README.md
CI run summary: https://github.com/kernel-patches/bpf/actions/runs/33995442755
^ permalink raw reply [flat|nested] 54+ messages in thread
* Re: [PATCH bpf-next v1 16/22] bpf: Drop redundant BTF pointer helper write rejection
2026-09-05 22:01 ` [PATCH bpf-next v1 16/22] bpf: Drop redundant BTF pointer helper write rejection Amery Hung
@ 2026-09-05 23:08 ` bot+bpf-ci
2026-09-09 20:48 ` Amery Hung
0 siblings, 1 reply; 54+ messages in thread
From: bot+bpf-ci @ 2026-09-05 23:08 UTC (permalink / raw)
To: ameryhung, bpf
Cc: alexei.starovoitov, andrii, daniel, eddyz87, memxor, ameryhung,
kernel-team, ast, andrii, daniel, martin.lau, eddyz87,
yonghong.song, mason, ihor.solodrai
[-- Attachment #1: Type: text/plain, Size: 3386 bytes --]
> commit 5188979f5e68c2684632ba13facdb46febabed5a
> Author: Amery Hung <ameryhung@gmail.com>
> Date: Sat Sep 5 15:01:11 2026 -0700
>
> bpf: Drop redundant BTF pointer helper write rejection
>
> check_reg_type() rejects a trusted PTR_TO_BTF_ID admitted by
> mem_types unless the argument carries MEM_RDONLY. This duplicates
> argument-specific memory access validation and is too conservative for
> map keys and input map values, whose read direction is not represented
> by MEM_RDONLY.
>
> Since commit fa75b7c85b0d ("bpf: Enforce write checks for BTF pointer
> helper access"), check_helper_mem_access() forwards the requested access
> type to check_ptr_to_btf_access(). Map keys always request read access.
> Map values request write access only when tagged with MEM_WRITE.
> ARG_PTR_TO_MEM prototypes must carry either MEM_RDONLY or MEM_WRITE,
> and size arguments use the direction of their associated memory
> argument. btf_struct_access() therefore checks the effective access
^^^^
> direction in every case.
Is btf_struct_access() the correct function name here?
btf_struct_access() in kernel/bpf/btf.c explicitly ignores the access
direction; the parameter is declared unused:
int btf_struct_access(struct bpf_verifier_log *log,
const struct bpf_reg_state *reg,
int off, int size, enum bpf_access_type atype __maybe_unused,
u32 *next_btf_id, enum bpf_type_flag *flag,
const char **field_name)
The direction is enforced one level up, in check_ptr_to_btf_access()
in kernel/bpf/verifier.c, which:
- rejects any non-read of a faultable pointer,
- for atype == BPF_WRITE dispatches to the program-type callback
env->ops->btf_struct_access() when one is installed, and
- otherwise rejects with "only read is supported" unless the register
is a program-allocated object.
Reading btf_struct_access() as the op pointer env->ops->btf_struct_access
(same name, so the sentence is ambiguous) does not make the claim correct
either: that op is NULL for most program types, and in that case the
direction check is check_ptr_to_btf_access()'s own "only read is
supported" rejection rather than any btf_struct_access().
Should the commit message say check_ptr_to_btf_access() instead?
>
> Drop the early rejection and keep check_reg_type() limited to register
> admission.
>
> Signed-off-by: Amery Hung <ameryhung@gmail.com>
This looks like a bug fix for commit fa75b7c85b0d. The commit message
explicitly references commit fa75b7c85b0d which introduced the validation
flow change that made the check_reg_type() logic become redundant and
overly conservative. After fa75b7c85b0d changed check_helper_mem_access()
to forward the requested access_type to check_ptr_to_btf_access(), the
early rejection in check_reg_type() for mem_types without MEM_RDONLY
became incorrect, blocking valid use cases for map keys and input map
values.
Should this include:
Fixes: fa75b7c85b0d ("bpf: Enforce write checks for BTF pointer helper access")
---
AI reviewed your patch. Please fix the bug or email reply why it's not a bug.
See: https://github.com/kernel-patches/vmtest/blob/master/ci/claude/README.md
CI run summary: https://github.com/kernel-patches/bpf/actions/runs/33995442755
^ permalink raw reply [flat|nested] 54+ messages in thread
* Re: [PATCH bpf-next v1 06/22] bpf: Unify kfunc argument kinds with enum bpf_arg_type
2026-09-05 22:01 ` [PATCH bpf-next v1 06/22] bpf: Unify kfunc argument kinds with enum bpf_arg_type Amery Hung
@ 2026-09-05 23:08 ` bot+bpf-ci
2026-09-09 18:04 ` Amery Hung
0 siblings, 1 reply; 54+ messages in thread
From: bot+bpf-ci @ 2026-09-05 23:08 UTC (permalink / raw)
To: ameryhung, bpf
Cc: alexei.starovoitov, andrii, daniel, eddyz87, memxor, ameryhung,
kernel-team, ast, andrii, daniel, martin.lau, eddyz87,
yonghong.song, mason, ihor.solodrai
[-- Attachment #1: Type: text/plain, Size: 8523 bytes --]
> diff --git a/include/linux/bpf.h b/include/linux/bpf.h
> index 1574fe2d8cc0..f620920ea575 100644
> --- a/include/linux/bpf.h
> +++ b/include/linux/bpf.h
> @@ -908,6 +908,20 @@ enum bpf_arg_type {
> ARG_PTR_TO_TIMER, /* pointer to bpf_timer */
> ARG_KPTR_XCHG_DEST, /* pointer to destination that kptrs are bpf_kptr_xchg'd into */
> ARG_PTR_TO_DYNPTR, /* pointer to bpf_dynptr. See bpf_type_flag for dynptr type */
> +
> + ARG_CONST_SCALAR, /* scalar known at verification time */
> + ARG_CONST_MEM_SIZE, /* ARG_MEM_SIZE that must be constant */
> + ARG_PTR_TO_ALLOC_BTF_ID, /* pointer to an allocated object */
> + ARG_PTR_TO_REFCOUNTED_KPTR, /* pointer to a refcounted local kptr */
> + ARG_PTR_TO_ITER, /* pointer to an iterator */
> + ARG_PTR_TO_LIST_HEAD, /* pointer to bpf_list_head */
> + ARG_PTR_TO_LIST_NODE, /* pointer to bpf_list_node */
> + ARG_PTR_TO_RB_ROOT, /* pointer to bpf_rb_root */
> + ARG_PTR_TO_RB_NODE, /* pointer to bpf_rb_node */
> + ARG_PTR_TO_WORKQUEUE, /* pointer to bpf_wq */
> + ARG_PTR_TO_TASK_WORK, /* pointer to bpf_task_work */
> + ARG_PTR_TO_IRQ_FLAG, /* pointer to saved IRQ flags on the stack */
> + ARG_PTR_TO_RES_SPIN_LOCK, /* pointer to bpf_res_spin_lock */
> __BPF_ARG_TYPE_MAX,
>
> /* Extended arg_types. */
[ ... ]
> diff --git a/kernel/bpf/verifier.c b/kernel/bpf/verifier.c
> index d444e72fdd97..a0a74a5e23be 100644
> --- a/kernel/bpf/verifier.c
> +++ b/kernel/bpf/verifier.c
> @@ -8533,10 +8533,9 @@ 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,
> - bool btf_id_fixed_off_ok)
> +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)
> {
> u32 type = reg->type;
>
> @@ -8592,12 +8591,15 @@ 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 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.
> + * its fixed offset must be 0. bpf_refcount_acquire() returns the
> + * pointer it was given while incrementing the refcount at the
> + * refcount field offset, so it needs a zero offset too. 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, btf_id_fixed_off_ok);
> + return __check_ptr_off_reg(env, reg, argno,
> + base_type(arg_type) != ARG_PTR_TO_REFCOUNTED_KPTR);
> case PTR_TO_CTX:
> /*
> * Allow fixed and variable offsets for syscall context, but
> @@ -8613,13 +8615,6 @@ 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);
> -}
> -
[ ... ]
> @@ -11793,34 +11788,6 @@ static void btf_member_path_str(const struct btf *btf, const struct btf_member_p
> }
> }
>
> -enum kfunc_ptr_arg_type {
> - KF_ARG_CONST_MEM_SIZE,
> - KF_ARG_MEM_SIZE,
> - KF_ARG_CONST,
> - KF_ARG_CONST_ALLOC_SIZE_OR_ZERO,
> - KF_ARG_ANYTHING,
> - KF_ARG_PTR_TO_CTX,
> - KF_ARG_PTR_TO_ALLOC_BTF_ID, /* Allocated object */
> - KF_ARG_PTR_TO_REFCOUNTED_KPTR, /* Refcounted local kptr */
> - KF_ARG_PTR_TO_DYNPTR,
> - KF_ARG_PTR_TO_ITER,
> - KF_ARG_PTR_TO_LIST_HEAD,
> - KF_ARG_PTR_TO_LIST_NODE,
> - KF_ARG_PTR_TO_BTF_ID, /* Also covers reg2btf_ids conversions */
> - KF_ARG_PTR_TO_MEM,
> - KF_ARG_PTR_TO_CALLBACK,
> - KF_ARG_PTR_TO_RB_ROOT,
> - KF_ARG_PTR_TO_RB_NODE,
> - KF_ARG_PTR_TO_CONST_STR,
> - KF_ARG_CONST_MAP_PTR,
> - KF_ARG_PTR_TO_TIMER,
> - KF_ARG_PTR_TO_WORKQUEUE,
> - KF_ARG_PTR_TO_IRQ_FLAG,
> - KF_ARG_PTR_TO_RES_SPIN_LOCK,
> - KF_ARG_PTR_TO_TASK_WORK,
> - KF_ARG_PTR_TO_ARENA,
> -};
> -
This deletion removes KF_ARG_PTR_TO_REFCOUNTED_KPTR from the tree. Does
the kerneldoc comment in include/linux/bpf_verifier.h for struct
bpf_call_arg_meta need updating? It still refers to the deleted name:
/* arg_{btf,btf_id,owning_ref} are used by kfunc-specific handling,
* ...
* bpf_refcount_acquire (via KF_ARG_PTR_TO_REFCOUNTED_KPTR arg type)
*/
The replacement is ARG_PTR_TO_REFCOUNTED_KPTR.
[ ... ]
> @@ -12826,13 +12793,11 @@ static int check_kfunc_args(struct bpf_verifier_env *env, struct bpf_call_arg_me
> */
> for (i = 0; i < nargs; i++) {
> struct bpf_reg_state *reg = get_func_arg_reg(caller, regs, i);
> + enum bpf_arg_type arg_type = meta->fn->arg_type[i];
> const struct btf_type *t, *ref_t, *resolve_ret;
> - enum bpf_arg_type arg_type = ARG_UNUSED;
> argno_t argno = argno_from_arg(i + 1);
> int regno = reg_from_argno(argno);
> - bool btf_id_fixed_off_ok = true;
> u32 ref_id = args[i].type, type_size;
> - int kf_arg_type = meta->fn->arg_type[i];
[ ... ]
> @@ -12901,54 +12866,14 @@ static int check_kfunc_args(struct bpf_verifier_env *env, struct bpf_call_arg_me
> ref_tname = btf_name_by_offset(btf, ref_t->name_off);
> }
>
> - switch (base_type(kf_arg_type)) {
> - case KF_ARG_CONST:
> - case KF_ARG_CONST_MEM_SIZE:
> - case KF_ARG_MEM_SIZE:
> - case KF_ARG_ANYTHING:
> - case KF_ARG_CONST_ALLOC_SIZE_OR_ZERO:
> - case KF_ARG_PTR_TO_ALLOC_BTF_ID:
> - case KF_ARG_PTR_TO_BTF_ID:
> - case KF_ARG_CONST_MAP_PTR:
> - case KF_ARG_PTR_TO_ITER:
> - case KF_ARG_PTR_TO_LIST_HEAD:
> - case KF_ARG_PTR_TO_LIST_NODE:
> - case KF_ARG_PTR_TO_RB_ROOT:
> - case KF_ARG_PTR_TO_RB_NODE:
> - case KF_ARG_PTR_TO_MEM:
> - case KF_ARG_PTR_TO_CALLBACK:
> - case KF_ARG_PTR_TO_CONST_STR:
> - case KF_ARG_PTR_TO_WORKQUEUE:
> - case KF_ARG_PTR_TO_TIMER:
> - case KF_ARG_PTR_TO_TASK_WORK:
> - case KF_ARG_PTR_TO_IRQ_FLAG:
> - case KF_ARG_PTR_TO_RES_SPIN_LOCK:
> - case KF_ARG_PTR_TO_ARENA:
> - break;
> - case KF_ARG_PTR_TO_DYNPTR:
> - arg_type = ARG_PTR_TO_DYNPTR;
> - break;
> - 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;
> - }
> -
> if (regno == meta->release_regno)
> arg_type |= OBJ_RELEASE;
> - ret = __check_func_arg_reg_off(env, reg, argno, arg_type,
> - btf_id_fixed_off_ok);
> + ret = check_func_arg_reg_off(env, reg, argno, arg_type);
> if (ret < 0)
> return ret;
Dropping the translation switch means check_func_arg_reg_off() now
receives the flag-carrying arg_type from get_kfunc_arg_type() instead of
a bare base type. Does this affect the PTR_TO_CTX case in that function?
Looking at check_func_arg_reg_off():
case PTR_TO_CTX:
if (arg_type != ARG_PTR_TO_CTX && is_var_ctx_off_allowed(env->prog))
return 0;
The test compares the full value rather than base_type(arg_type).
Before this commit, the translation switch assigned 'arg_type =
ARG_PTR_TO_CTX;' with no flags. After it, get_kfunc_arg_type() can
return ARG_PTR_TO_CTX | PTR_MAYBE_NULL for a context-typed kfunc
parameter carrying a '__nullable' suffix. In BPF_PROG_TYPE_SYSCALL
programs (the only program type where is_var_ctx_off_allowed() is true),
would a nullable ctx arg skip the __check_ptr_off_reg() call, taking the
'return 0' path instead of rejecting modified ctx pointers?
While this appears unreachable today (no in-tree kfunc has both a
context-typed parameter and a __nullable suffix), is the "No functional
change" claim literally exact, or would converting the test to
'base_type(arg_type) != ARG_PTR_TO_CTX' make the unification flag-safe?
---
AI reviewed your patch. Please fix the bug or email reply why it's not a bug.
See: https://github.com/kernel-patches/vmtest/blob/master/ci/claude/README.md
CI run summary: https://github.com/kernel-patches/bpf/actions/runs/33995442755
^ permalink raw reply [flat|nested] 54+ messages in thread
* Re: [PATCH bpf-next v1 05/22] bpf: Rename ambiguous function argument types
2026-09-05 22:01 ` [PATCH bpf-next v1 05/22] bpf: Rename ambiguous function argument types Amery Hung
@ 2026-09-05 23:08 ` bot+bpf-ci
2026-09-09 17:54 ` Amery Hung
0 siblings, 1 reply; 54+ messages in thread
From: bot+bpf-ci @ 2026-09-05 23:08 UTC (permalink / raw)
To: ameryhung, bpf
Cc: alexei.starovoitov, andrii, daniel, eddyz87, memxor, ameryhung,
kernel-team, ast, andrii, daniel, martin.lau, eddyz87,
yonghong.song, mason, ihor.solodrai
[-- Attachment #1: Type: text/plain, Size: 2145 bytes --]
> ARG_ANYTHING does not accept just anything: check_func_arg() accepts
> an initialized scalar and rejects pointers. Rename it to ARG_SCALAR.
>
> ARG_DONTCARE is the zero-valued terminator for the fixed-size argument
> array rather than an argument whose value is ignored. Rename it to
> ARG_UNUSED and stop helper argument iteration explicitly when it
> is encountered.
>
> Also pass ARG_PTR_TO_MEM when checking a global subprogram memory
> argument instead of using the prototype terminator as a placeholder.
>
> No functional change.
>
> Signed-off-by: Amery Hung <ameryhung@gmail.com>
The commit message says check_func_arg() "rejects pointers" for
ARG_SCALAR (formerly ARG_ANYTHING). Is this claim complete?
Looking at kernel/bpf/verifier.c, the rejection is conditional on
allow_ptr_leaks:
if (arg_type == ARG_SCALAR) {
if (__is_pointer_value(env->allow_ptr_leaks, reg)) {
verbose(env, "%s leaks addr into helper function\n",
reg_arg_name(env, argno));
return -EACCES;
}
return 0;
}
and __is_pointer_value() short-circuits when allow_ptr_leaks is true:
static bool __is_pointer_value(bool allow_ptr_leaks,
const struct bpf_reg_state *reg)
{
if (allow_ptr_leaks)
return false;
return is_pointer_regtype(reg->type);
}
So a privileged program (one with allow_ptr_leaks set) can pass
PTR_TO_MAP_VALUE, PTR_TO_STACK, or other pointer types to an ARG_SCALAR
helper argument. The pointer check only applies to unprivileged programs.
The later commit in this series, a490f49b0c4e, acknowledges this
explicitly: "helpers retain their privileged ARG_SCALAR pointer-leak
behavior."
Could the commit message be more precise? Perhaps: "rejects pointers
unless the program is allowed to leak pointers" or "rejects pointers for
unprivileged programs"?
---
AI reviewed your patch. Please fix the bug or email reply why it's not a bug.
See: https://github.com/kernel-patches/vmtest/blob/master/ci/claude/README.md
CI run summary: https://github.com/kernel-patches/bpf/actions/runs/33995442755
^ permalink raw reply [flat|nested] 54+ messages in thread
* Re: [PATCH bpf-next v1 12/22] bpf: Resolve BTF ID of ARG_PTR_TO_BTF_ID in kfunc bpf_func_proto
2026-09-05 22:01 ` [PATCH bpf-next v1 12/22] bpf: Resolve BTF ID of ARG_PTR_TO_BTF_ID in kfunc bpf_func_proto Amery Hung
@ 2026-09-05 23:08 ` bot+bpf-ci
2026-09-09 20:42 ` Amery Hung
0 siblings, 1 reply; 54+ messages in thread
From: bot+bpf-ci @ 2026-09-05 23:08 UTC (permalink / raw)
To: ameryhung, bpf
Cc: alexei.starovoitov, andrii, daniel, eddyz87, memxor, ameryhung,
kernel-team, ast, andrii, daniel, martin.lau, eddyz87,
yonghong.song, mason, ihor.solodrai
[-- Attachment #1: Type: text/plain, Size: 3995 bytes --]
> diff --git a/include/linux/bpf.h b/include/linux/bpf.h
> index 1f78746e0601..fe6ab92ceeff 100644
> --- a/include/linux/bpf.h
> +++ b/include/linux/bpf.h
> @@ -1020,13 +1020,13 @@ struct bpf_func_proto {
> };
> union {
> struct {
> - u32 *arg1_btf_id;
> - u32 *arg2_btf_id;
> - u32 *arg3_btf_id;
> - u32 *arg4_btf_id;
> - u32 *arg5_btf_id;
> + const u32 *arg1_btf_id;
> + const u32 *arg2_btf_id;
> + const u32 *arg3_btf_id;
> + const u32 *arg4_btf_id;
> + const u32 *arg5_btf_id;
> };
> - u32 *arg_btf_id[MAX_BPF_FUNC_ARGS];
> + const u32 *arg_btf_id[MAX_BPF_FUNC_ARGS];
> struct {
> size_t arg1_size;
> size_t arg2_size;
[ ... ]
> diff --git a/kernel/bpf/verifier.c b/kernel/bpf/verifier.c
> index c5b394e847e6..b6e36df72d13 100644
> --- a/kernel/bpf/verifier.c
> +++ b/kernel/bpf/verifier.c
[ ... ]
> @@ -12082,13 +12082,18 @@ bool bpf_is_kfunc_pkt_changing(struct bpf_call_arg_meta *meta)
>
> static int
> get_kfunc_arg_type(struct bpf_verifier_env *env, struct bpf_call_arg_meta *meta,
> - const struct btf_param *args, int arg, int nargs)
> + const struct btf_param *args, int arg, int nargs,
> + struct bpf_func_proto *proto)
> {
> const struct btf_type *t, *ref_t = NULL;
> + const u32 *ref_id_ptr = NULL;
> argno_t argno = argno_from_arg(arg + 1);
> const char *ref_tname = NULL;
> + u32 ref_id;
> int arg_type;
>
> + proto->arg_btf_id[arg] = NULL;
> +
> if (is_kfunc_arg_prog_aux(meta->btf, &args[arg]))
> return ARG_PTR_TO_PROG_AUX;
>
> @@ -12116,7 +12121,11 @@ get_kfunc_arg_type(struct bpf_verifier_env *env, struct bpf_call_arg_meta *meta,
> reg_arg_name(env, argno), btf_type_str(t));
> return -EINVAL;
> }
> - ref_t = btf_type_skip_modifiers(meta->btf, t->type, NULL);
> + /* Keep a pointer to the BTF field containing the resolved referent ID. */
> + ref_id_ptr = &t->type;
> + ref_t = btf_type_skip_modifiers(meta->btf, *ref_id_ptr, &ref_id);
> + while (*ref_id_ptr != ref_id)
> + ref_id_ptr = &btf_type_by_id(meta->btf, *ref_id_ptr)->type;
> ref_tname = btf_name_by_offset(meta->btf, ref_t->name_off);
>
> /* In this function, we verify the kfunc's BTF as per the argument type,
[ ... ]
> @@ -12239,13 +12248,21 @@ get_kfunc_arg_type(struct bpf_verifier_env *env, struct bpf_call_arg_meta *meta,
> if (is_kfunc_release(meta) && arg == 0)
> arg_type |= OBJ_RELEASE;
>
> - /*
> - * A KF_RCU kfunc accepts an RCU-protected pointer where it would
> - * otherwise demand a referenced or trusted one. Only ARG_PTR_TO_BTF_ID
> - * looks at where its register came from, so leave the other kinds alone.
> - */
> - if (base_type(arg_type) == ARG_PTR_TO_BTF_ID && is_kfunc_rcu(meta))
> - arg_type |= MEM_RCU;
> + if (base_type(arg_type) == ARG_PTR_TO_BTF_ID) {
> + /* A __map argument names a vmlinux type rather than one from
> + * the kfunc's own BTF.
> + */
^^^^
This isn't a bug, but both multi-line comments added in this block start
their text on the same line as the opening '/*'. The BPF subsystem's
comment style puts the opening '/*' on its own line. The second comment
below rewrites one that was previously in the preferred style (with '/*'
on its own line), and another comment added by this patch in
check_kfunc_args() does follow the standard. Could these two be
reformatted to match?
> + if (is_kfunc_arg_map(meta->btf, &args[arg]))
> + proto->arg_btf_id[arg] = reg2btf_ids[CONST_PTR_TO_MAP];
> + else
> + proto->arg_btf_id[arg] = ref_id_ptr;
> +
> + /* A KF_RCU kfunc accepts an RCU-protected pointer where it would
> + * otherwise demand a referenced or trusted one.
> + */
^^^^
> + if (is_kfunc_rcu(meta))
> + arg_type |= MEM_RCU;
> + }
>
> return arg_type;
> }
---
AI reviewed your patch. Please fix the bug or email reply why it's not a bug.
See: https://github.com/kernel-patches/vmtest/blob/master/ci/claude/README.md
CI run summary: https://github.com/kernel-patches/bpf/actions/runs/33995442755
^ permalink raw reply [flat|nested] 54+ messages in thread
* Re: [PATCH bpf-next v1 21/22] bpf: Consolidate release argument validation
2026-09-05 22:01 ` [PATCH bpf-next v1 21/22] bpf: Consolidate release argument validation Amery Hung
@ 2026-09-05 23:08 ` bot+bpf-ci
2026-09-11 20:47 ` Amery Hung
0 siblings, 1 reply; 54+ messages in thread
From: bot+bpf-ci @ 2026-09-05 23:08 UTC (permalink / raw)
To: ameryhung, bpf
Cc: alexei.starovoitov, andrii, daniel, eddyz87, memxor, ameryhung,
kernel-team, ast, andrii, daniel, martin.lau, eddyz87,
yonghong.song, mason, ihor.solodrai
[-- Attachment #1: Type: text/plain, Size: 7238 bytes --]
> diff --git a/kernel/bpf/verifier.c b/kernel/bpf/verifier.c
> index 487ad1ee4179..6c5d7e544f84 100644
> --- a/kernel/bpf/verifier.c
> +++ b/kernel/bpf/verifier.c
[ ... ]
> @@ -8825,29 +8862,9 @@ static int check_func_arg(struct bpf_verifier_env *env, u32 arg,
> return err;
>
> skip_type_check:
> - if (arg_type_is_release(arg_type)) {
> - if (type_may_be_null(reg->type)) {
> - verbose(env, "Possibly NULL pointer passed to trusted %s\n",
> - reg_arg_name(env, argno));
> - bpf_diag_call_arg(
> - env, insn_idx, argno, meta->func_name,
> - "the pointer may be NULL, but this call requires a non-NULL pointer",
> - "Add a NULL check and make the call only on the non-NULL path.");
> - return -EACCES;
> - }
> -
> - if (!arg_type_is_dynptr(arg_type) &&
> - !reg_is_referenced(env, reg) && !bpf_register_is_null(reg)) {
> - verbose(env,
> - "release helper %s expects referenced PTR_TO_BTF_ID passed to %s\n",
> - meta->func_name, reg_arg_name(env, argno));
> - bpf_diag_call_arg(
> - env, insn_idx, argno, meta->func_name,
> - "release helpers require a value that owns a live resource returned by a matching acquire helper",
> - "Pass the resource-owning pointer returned by the matching acquire helper, and avoid calling the release helper after ownership has already been transferred or released.");
> - return -EINVAL;
> - }
> - }
> + err = check_func_arg_release(env, reg, argno, arg_type, meta, insn_idx);
> + if (err)
> + return err;
Does removing the type_may_be_null() check here break the percpu kptr
RCU conversion path?
The deleted block rejected maybe-null registers passed to OBJ_RELEASE
helper arguments. Only bpf_kptr_xchg() arg2 has ARG_PTR_TO_BTR_ID_OR_NULL
| OBJ_RELEASE, so it is the only argument whose admission changes.
check_func_arg_release() only keeps the ownership half of the test:
kernel/bpf/verifier.c:check_func_arg_release() {
if (arg_type_is_dynptr(arg_type) || reg_is_referenced(env, reg) ||
bpf_register_is_null(reg))
return 0;
}
but for the percpu-kptr RCU conversion path, release_reg() assumes the
released register is known non-NULL. In check_helper_call():
bool convert_rcu = (func_id == BPF_FUNC_kptr_xchg) && in_rcu_cs(env) &&
(reg->type & MEM_ALLOC) && (reg->type & MEM_PERCPU);
err = release_reg(env, reg, convert_rcu, !!meta.dynptr.id);
release_reg() then calls ref_convert_alloc_rcu_protected(), which for
every register carrying the id does:
reg->id = 0;
reg->type &= ~MEM_ALLOC;
reg->type |= MEM_RCU;
PTR_MAYBE_NULL is deliberately not touched, and reg->id is zeroed. Before
this patch that combination was unreachable because a maybe-null register
could not be passed to bpf_kptr_xchg() at all. Now the verifier can
produce PTR_TO_BTF_ID | MEM_PERCPU | MEM_RCU | PTR_MAYBE_NULL with
reg->id == 0.
That state breaks NULL-check identity matching. check_cond_jmp_op() calls
mark_ptr_or_null_regs() for any JEQ/JNE-against-0 on a maybe-null
register, and mark_ptr_or_null_regs() uses u32 id = regs[regno].id, i.e.
0. mark_ptr_or_null_reg() then matches on:
if (type_may_be_null(reg->type) && reg->id == id &&
(is_rcu_reg(reg) || !WARN_ON_ONCE(!reg->id))) {
is_rcu_reg() is just "reg->type & MEM_RCU", so the id==0 sanity WARN is
bypassed and every maybe-null MEM_RCU register with id 0 in the state is
marked non-NULL by a NULL check on any one of them.
Concrete accepted-but-unsafe program with percpu kptr fields pc1/pc2 in a
map value:
p1 = bpf_percpu_obj_new(struct val_t);
/* MEM_ALLOC|MEM_PERCPU|PTR_MAYBE_NULL, id=N1 */
p2 = bpf_percpu_obj_new(struct val_t);
/* MEM_ALLOC|MEM_PERCPU|PTR_MAYBE_NULL, id=N2 */
bpf_rcu_read_lock();
old = bpf_kptr_xchg(&v->pc1, p1);
/* p1 -> MEM_RCU|MEM_PERCPU|PTR_MAYBE_NULL, id=0 */
old = bpf_kptr_xchg(&v->pc2, p2);
/* p2 -> MEM_RCU|MEM_PERCPU|PTR_MAYBE_NULL, id=0 */
if (p1) {
/* clears PTR_MAYBE_NULL on p1 AND on p2 (both id 0, both MEM_RCU) */
q = bpf_this_cpu_ptr(p2);
/* R1 now looks non-NULL, so ARG_PTR_TO_PERCPU_BTF_ID is accepted */
q->field = 1;
/* q is PTR_TO_BTF_ID|MEM_ALLOC|MEM_RCU, non-NULL, writable */
}
bpf_rcu_read_unlock();
If bpf_percpu_obj_new() for p2 returned NULL at runtime, the program
stores NULL into the percpu kptr field and then executes
this_cpu_ptr(NULL), which writes at NULL + __per_cpu_offset[cpu].
This would be an unchecked write into the per-CPU area from a
verifier-accepted program.
Note that progs/map_kptr_fail.c:kptr_xchg_possibly_null was deliberately
flipped from __failure to __success, so the relaxation is intentional,
but the changelog does not address the percpu/RCU conversion path, and no
test covers a nullable percpu source xchg'd inside an RCU critical
section.
Should the verifier keep rejecting type_may_be_null(reg->type) for
OBJ_RELEASE helper arguments when the destination kptr field is
BPF_KPTR_PERCPU, or should ref_convert_alloc_rcu_protected() leave a
fresh non-zero reg->id so that maybe-null identity tracking stays intact?
> diff --git a/tools/testing/selftests/bpf/prog_tests/cb_refs.c b/tools/testing/selftests/bpf/prog_tests/cb_refs.c
> index 50ea3d72d446..8743f52e755d 100644
> --- a/tools/testing/selftests/bpf/prog_tests/cb_refs.c
> +++ b/tools/testing/selftests/bpf/prog_tests/cb_refs.c
> @@ -11,8 +11,8 @@ struct {
> const char *prog_name;
> const char *err_msg;
> } cb_refs_tests[] = {
> - { "underflow_prog", "release kfunc bpf_kfunc_call_test_release expects referenced PTR_TO_BTF_ID passed to R1" },
> - { "leak_prog", "Possibly NULL pointer passed to trusted R2" },
> + { "underflow_prog", "release function bpf_kfunc_call_test_release expects referenced PTR_TO_BTF_ID passed to R1" },
> + { "leak_prog", "Unreleased reference id=4 alloc_insn=33" },
This isn't a bug, but could the leak_prog expectation be more robust?
The new expectation hard-codes a full instruction index, "Unreleased
reference id=4 alloc_insn=33", which pins the test to one compiler's
instruction layout for the callback subprog cb2().
Every neighbouring expectation in the same table deliberately truncates
the digits and documents why:
{ "nested_cb", "Unreleased reference id=4 alloc_insn=2" },
/* alloc_insn=2{4,5} */
{ "non_cb_transfer_ref", "Unreleased reference id=4 alloc_insn=1" },
/* alloc_insn=1{1,2} */
and the equivalent expectation added for the same verifier message in
map_kptr_fail.c drops the number entirely:
__failure __msg("Unreleased reference id=4 alloc_insn=")
Matching in test_cb_refs() is strstr(log_buf, cb_refs_tests[i].err_msg),
so a truncated prefix such as "Unreleased reference id=4 alloc_insn=3"
matches equally well while tolerating a one- or two-instruction shift from
a different clang version.
As written, the entry breaks with any layout change in cb2(), and unlike
the two neighbours it carries no comment recording the observed range.
---
AI reviewed your patch. Please fix the bug or email reply why it's not a bug.
See: https://github.com/kernel-patches/vmtest/blob/master/ci/claude/README.md
CI run summary: https://github.com/kernel-patches/bpf/actions/runs/33995442755
^ permalink raw reply [flat|nested] 54+ messages in thread
* Re: [PATCH bpf-next v1 07/22] bpf: Align helper and kfunc ARG_PTR_TO_PROG_AUX handling
2026-09-05 22:01 ` [PATCH bpf-next v1 07/22] bpf: Align helper and kfunc ARG_PTR_TO_PROG_AUX handling Amery Hung
@ 2026-09-05 23:08 ` bot+bpf-ci
2026-09-09 18:23 ` Amery Hung
0 siblings, 1 reply; 54+ messages in thread
From: bot+bpf-ci @ 2026-09-05 23:08 UTC (permalink / raw)
To: ameryhung, bpf
Cc: alexei.starovoitov, andrii, daniel, eddyz87, memxor, ameryhung,
kernel-team, ast, andrii, daniel, martin.lau, eddyz87,
yonghong.song, mason, ihor.solodrai
[-- Attachment #1: Type: text/plain, Size: 8672 bytes --]
> diff --git a/include/linux/bpf.h b/include/linux/bpf.h
> index f620920ea575..547703f54a89 100644
> --- a/include/linux/bpf.h
> +++ b/include/linux/bpf.h
> @@ -922,6 +922,7 @@ enum bpf_arg_type {
> ARG_PTR_TO_TASK_WORK, /* pointer to bpf_task_work */
> ARG_PTR_TO_IRQ_FLAG, /* pointer to saved IRQ flags on the stack */
> ARG_PTR_TO_RES_SPIN_LOCK, /* pointer to bpf_res_spin_lock */
> + ARG_PTR_TO_PROG_AUX, /* pointer to the caller's bpf_prog_aux */
> __BPF_ARG_TYPE_MAX,
>
> /* Extended arg_types. */
[ ... ]
> diff --git a/kernel/bpf/helpers.c b/kernel/bpf/helpers.c
> index 72bfd8f93ae4..1b731aad54da 100644
> --- a/kernel/bpf/helpers.c
> +++ b/kernel/bpf/helpers.c
> @@ -1510,6 +1510,7 @@ static const struct bpf_func_proto bpf_timer_set_callback_proto = {
> .ret_type = RET_INTEGER,
> .arg1_type = ARG_PTR_TO_TIMER,
> .arg2_type = ARG_PTR_TO_FUNC,
> + .arg3_type = ARG_PTR_TO_PROG_AUX,
> };
[ ... ]
> diff --git a/kernel/bpf/verifier.c b/kernel/bpf/verifier.c
> index a0a74a5e23be..22f5aff76b40 100644
> --- a/kernel/bpf/verifier.c
> +++ b/kernel/bpf/verifier.c
[ ... ]
> @@ -9400,9 +9406,42 @@ static bool check_proto_release_reg(const struct bpf_func_proto *fn, struct bpf_
> return true;
> }
>
> -static int check_func_proto(const struct bpf_func_proto *fn, struct bpf_call_arg_meta *meta)
> +static bool check_arg_prog_aux(struct bpf_verifier_env *env,
> + const struct bpf_func_proto *proto)
> {
> - return check_raw_mode_ok(fn, meta) &&
> + bool seen = false;
> + argno_t argno;
> + u32 i;
> +
> + for (i = 0; i < ARRAY_SIZE(proto->arg_type); i++) {
> + if (proto->arg_type[i] == ARG_UNUSED)
> + break;
Does this early break work correctly for kfunc protos?
gen_kfunc_arg_proto() in verifier.c skips __ign and implicit arguments
without assigning a type, leaving ARG_UNUSED holes in the middle of
proto->arg_type. For example:
bpf_arena_alloc_pages(void *p__map, void *addr__ign, u32 page_cnt,
int node_id, u64 flags)
leaves arg_type[1] == ARG_UNUSED while arg_type[2], [3], [4] have real
types. Similarly:
bpf_list_push_front_impl(head, node, meta__ign, off)
leaves arg_type[2] == ARG_UNUSED while arg_type[3] is a real type.
The loop above terminates at the first hole and never inspects remaining
arguments. If a kfunc has a prog_aux argument after a hole, or has >5
arguments with a prog_aux argument in position 6+, these checks are
unreachable.
The consequence is a net loss of validation. check_kfunc_args()
previously performed both checks unconditionally, per argument, from
BTF. Both were deleted from that function (which now does an unguarded
cur_aux(env)->arg_prog = regno) and moved here, where the early break
makes them unreachable for affected signatures.
The stack-argument check is the more critical one. argno_from_arg(i + 1)
/ reg_from_argno() return -1 for arguments 6 and above, and
check_kfunc_args() now assigns that value into the 4-bit arg_prog
bitfield (include/linux/bpf_verifier.h), yielding arg_prog == 0xf.
bpf_fixup_kfunc_call() would then emit BPF_LD_IMM64(15, (long)prog->aux),
storing to a non-existent register number.
No in-tree kfunc currently combines >5 arguments, a non-implicit
bpf_prog_aux argument and a preceding hole, so the escalation is latent;
the validation loss itself is not.
> + if (proto->arg_type[i] != ARG_PTR_TO_PROG_AUX)
> + continue;
> +
> + if (seen) {
> + verifier_bug(env, "Only 1 prog->aux argument supported");
> + return false;
> + }
> +
> + argno = argno_from_arg(i + 1);
> + if (reg_from_argno(argno) < 0) {
> + verbose(env, "%s prog->aux cannot be a stack argument\n",
> + reg_arg_name(env, argno));
> + return false;
> + }
> +
> + seen = true;
> + }
> +
> + return true;
> +}
> +
> +static int check_func_proto(struct bpf_verifier_env *env, const struct bpf_func_proto *fn,
> + struct bpf_call_arg_meta *meta)
> +{
> + return check_arg_prog_aux(env, fn) &&
> + check_raw_mode_ok(fn, meta) &&
> check_arg_pair_ok(fn) &&
> check_mem_arg_rw_flag_ok(fn) &&
> check_proto_release_reg(fn, meta) &&
[ ... ]
> @@ -12044,6 +12083,9 @@ get_kfunc_arg_type(struct bpf_verifier_env *env, struct bpf_call_arg_meta *meta,
> const char *ref_tname = NULL;
> int arg_type;
>
> + if (is_kfunc_arg_prog_aux(meta->btf, &args[arg]))
> + return ARG_PTR_TO_PROG_AUX;
> +
> t = btf_type_skip_modifiers(meta->btf, args[arg].type, NULL);
>
> /* Scalar arguments are classified from their BTF suffix/name alone. */
> @@ -12192,9 +12234,7 @@ static int gen_kfunc_arg_proto(struct bpf_verifier_env *env, struct bpf_call_arg
> }
>
> for (i = 0; i < nargs; i++) {
> - if (is_kfunc_arg_prog_aux(btf, &args[i]) ||
> - is_kfunc_arg_ignore(btf, &args[i]) ||
> - is_kfunc_arg_implicit(meta, i))
> + if (is_kfunc_arg_ignore(btf, &args[i]) || is_kfunc_arg_implicit(meta, i))
> continue;
Does ARG_PTR_TO_PROG_AUX get recorded for KF_IMPLICIT_ARGS kfuncs with
a bpf_prog_aux argument?
The loop above skips implicit arguments before calling
get_kfunc_arg_type(), so those arguments never get a type and
proto->arg_type[i] stays ARG_UNUSED.
resolve_btfids' is_kf_implicit_arg() restricts implicit arguments to
exactly two types: bpf_prog_aux and btf_struct_meta.
process_kfunc_with_implicit_args() rewrites the BTF at build time: the
visible kfunc FUNC gets a truncated func_proto without the implicit
args, while a <name>_impl FUNC keeps the full one. fetch_kfunc_meta()
sets meta->func_proto to the _impl proto, so is_kfunc_arg_implicit()
returns true for the trailing bpf_prog_aux argument in KF_IMPLICIT_ARGS
kfuncs.
Concrete trace for bpf_wq_set_callback (kernel/bpf/helpers.c, registered
KF_IMPLICIT_ARGS):
1. nargs = 4 from the _impl proto (wq, callback_fn, flags, aux); the
visible proto has vlen 3.
2. i == 3: is_kfunc_arg_implicit(meta, 3) is true -> continue,
get_kfunc_arg_type() never runs, proto->arg_type[3] == ARG_UNUSED.
3. check_kfunc_args() i == 3: arg_type == ARG_UNUSED, so the
ARG_PTR_TO_PROG_AUX branch is skipped and the next line
(is_kfunc_arg_implicit) continues. cur_aux(env)->arg_prog is never
set.
4. bpf_fixup_kfunc_call() tests env->insn_aux_data[insn_idx].arg_prog,
which is 0, so no BPF_LD_IMM64(R4, prog->aux) is emitted.
5. At run time bpf_wq_set_callback() is entered with whatever the
program left in R4 and immediately dereferences it:
return __bpf_async_set_callback(async, callback_fn, aux->prog);
This affects every KF_IMPLICIT_ARGS kfunc with a bpf_prog_aux argument:
bpf_wq_set_callback (helpers.c, aux->prog), bpf_task_work_schedule_signal
(helpers.c), bpf_task_work_schedule_resume (helpers.c), bpf_stream_vprintk
(stream.c, bpf_stream_get(stream_id, aux)), bpf_stream_print_stack (stream.c),
scx_bpf_cid_override (kernel/sched/ext/cid.c), plus the testmod kfuncs
bpf_kfunc_implicit_arg and bpf_kfunc_multi_st_ops_test_1_assoc. The
register is program-controlled, so this is an arbitrary kernel pointer
dereference reachable from a loadable BPF program.
The immediately following commit in the series, ef7ad41fb8d0, repairs
exactly this by adding ARG_IGNORE, moving the ignore/implicit test after
is_kfunc_arg_prog_aux() inside get_kfunc_arg_type(), and deleting the
pre-classification skip from gen_kfunc_arg_proto(). The ordering fix
from ef7ad41fb8d0 belongs in this commit to avoid a bisect hole.
>
> arg_type = get_kfunc_arg_type(env, meta, args, i, nargs);
> @@ -12204,7 +12244,7 @@ static int gen_kfunc_arg_proto(struct bpf_verifier_env *env, struct bpf_call_arg
> proto->arg_type[i] = arg_type;
> }
>
> - return 0;
> + return check_arg_prog_aux(env, proto) ? 0 : -EINVAL;
> }
[ ... ]
> @@ -12799,18 +12839,7 @@ static int check_kfunc_args(struct bpf_verifier_env *env, struct bpf_call_arg_me
> int regno = reg_from_argno(argno);
> u32 ref_id = args[i].type, type_size;
>
> - if (is_kfunc_arg_prog_aux(btf, &args[i])) {
> - /* Reject repeated use bpf_prog_aux */
> - if (meta->arg_prog) {
> - verifier_bug(env, "Only 1 prog->aux argument supported per-kfunc");
> - return -EFAULT;
> - }
> - if (regno < 0) {
> - verbose(env, "%s prog->aux cannot be a stack argument\n",
> - reg_arg_name(env, argno));
> - return -EINVAL;
> - }
> - meta->arg_prog = true;
> + if (arg_type == ARG_PTR_TO_PROG_AUX) {
> cur_aux(env)->arg_prog = regno;
> continue;
> }
---
AI reviewed your patch. Please fix the bug or email reply why it's not a bug.
See: https://github.com/kernel-patches/vmtest/blob/master/ci/claude/README.md
CI run summary: https://github.com/kernel-patches/bpf/actions/runs/33995442755
^ permalink raw reply [flat|nested] 54+ messages in thread
* Re: [PATCH bpf-next v1 18/22] bpf: Admit kfunc argument registers through check_reg_type()
2026-09-05 22:01 ` [PATCH bpf-next v1 18/22] bpf: Admit kfunc argument registers through check_reg_type() Amery Hung
@ 2026-09-05 23:23 ` bot+bpf-ci
2026-09-10 16:18 ` Amery Hung
0 siblings, 1 reply; 54+ messages in thread
From: bot+bpf-ci @ 2026-09-05 23:23 UTC (permalink / raw)
To: ameryhung, bpf
Cc: alexei.starovoitov, andrii, daniel, eddyz87, memxor, ameryhung,
kernel-team, ast, andrii, daniel, martin.lau, eddyz87,
yonghong.song, mason, ihor.solodrai
[-- Attachment #1: Type: text/plain, Size: 13838 bytes --]
> diff --git a/kernel/bpf/verifier.c b/kernel/bpf/verifier.c
> index bd3fea62aa02..cad49ea5fdfa 100644
> --- a/kernel/bpf/verifier.c
> +++ b/kernel/bpf/verifier.c
[ ... ]
> @@ -8251,7 +8239,7 @@ static const struct bpf_reg_types mem_types = {
> },
> };
>
> -static const struct bpf_reg_types spin_lock_types = {
> +static const struct bpf_reg_types map_value_or_alloc_obj_types = {
> .types = {
> PTR_TO_MAP_VALUE,
> PTR_TO_BTF_ID | MEM_ALLOC,
[ ... ]
> @@ -8280,7 +8268,29 @@ static const struct bpf_reg_types percpu_btf_ptr_types = {
> static const struct bpf_reg_types func_ptr_types = { .types = { PTR_TO_FUNC } };
> static const struct bpf_reg_types stack_ptr_types = { .types = { PTR_TO_STACK } };
> static const struct bpf_reg_types const_str_ptr_types = { .types = { PTR_TO_MAP_VALUE } };
> -static const struct bpf_reg_types timer_types = { .types = { PTR_TO_MAP_VALUE } };
> +static const struct bpf_reg_types map_value_types = { .types = { PTR_TO_MAP_VALUE } };
> +static const struct bpf_reg_types arena_types = {
> + .types = {
> + PTR_TO_ARENA,
> + SCALAR_VALUE,
> + }
> +};
> +
> +static const struct bpf_reg_types alloc_obj_types = {
> + .types = {
> + PTR_TO_BTF_ID | MEM_ALLOC,
> + PTR_TO_BTF_ID | MEM_ALLOC | MEM_PERCPU,
> + }
> +};
> +
> +static const struct bpf_reg_types alloc_obj_or_non_own_ref_types = {
> + .types = {
> + PTR_TO_BTF_ID | MEM_ALLOC,
> + PTR_TO_BTF_ID | MEM_ALLOC | NON_OWN_REF,
> + PTR_TO_BTF_ID | MEM_ALLOC | NON_OWN_REF | MEM_RCU,
> + }
> +};
Does alloc_obj_or_non_own_ref_types need a diagnostic improvement?
reg_type_str() has no textual prefix for MEM_ALLOC or NON_OWN_REF and
renders PTR_TO_BTF_ID as the bare stem "ptr_". The first two entries both
render as "ptr_" and the third as "rcu_ptr_", giving users the
non-actionable list "ptr_, ptr_, rcu_ptr_". The selftest updates confirm
this is what users see:
rbtree_fail.c: __msg("R2 type=scalar expected=ptr_, ptr_, rcu_ptr_")
refcounted_kptr_fail.c: __msg("R1 type=ctx expected=ptr_, ptr_, rcu_ptr_")
These replace specific messages like "R1 is neither owning or non-owning
ref". The changelog claims "The reason continues to report the actual
register type and all accepted register types", which is literally true but
not useful here: three distinct ownership states collapse to two
indistinguishable strings, and "ptr_" is a dangling stem. Either
reg_type_str() needs prefixes for MEM_ALLOC/NON_OWN_REF, or
ARG_PTR_TO_REFCOUNTED_KPTR / ARG_PTR_TO_LIST_NODE / ARG_PTR_TO_RB_NODE
should keep an ownership-specific message.
> @@ -8310,16 +8320,32 @@ static const struct bpf_reg_types *compatible_reg_types[__BPF_ARG_TYPE_MAX] = {
> #endif
> [ARG_PTR_TO_SOCKET] = &fullsock_types,
> [ARG_PTR_TO_BTF_ID] = &btf_ptr_types,
> - [ARG_PTR_TO_SPIN_LOCK] = &spin_lock_types,
> + [ARG_PTR_TO_SPIN_LOCK] = &map_value_or_alloc_obj_types,
> [ARG_PTR_TO_MEM] = &mem_types,
> [ARG_PTR_TO_RINGBUF_MEM] = &ringbuf_mem_types,
> [ARG_PTR_TO_PERCPU_BTF_ID] = &percpu_btf_ptr_types,
> [ARG_PTR_TO_FUNC] = &func_ptr_types,
> [ARG_PTR_TO_STACK] = &stack_ptr_types,
> [ARG_PTR_TO_CONST_STR] = &const_str_ptr_types,
> - [ARG_PTR_TO_TIMER] = &timer_types,
> + [ARG_PTR_TO_TIMER] = &map_value_types,
> [ARG_KPTR_XCHG_DEST] = &kptr_xchg_dest_types,
> [ARG_PTR_TO_DYNPTR] = &dynptr_types,
> + /* Argument kinds only a kfunc can have. */
> + [ARG_SCALAR] = &scalar_types,
> + [ARG_CONST_SCALAR] = &scalar_types,
> + [ARG_CONST_MEM_SIZE] = &scalar_types,
> + [ARG_PTR_TO_ALLOC_BTF_ID] = &alloc_obj_types,
> + [ARG_PTR_TO_REFCOUNTED_KPTR] = &alloc_obj_or_non_own_ref_types,
> + [ARG_PTR_TO_ITER] = &stack_ptr_types,
> + [ARG_PTR_TO_LIST_HEAD] = &map_value_or_alloc_obj_types,
> + [ARG_PTR_TO_LIST_NODE] = &alloc_obj_or_non_own_ref_types,
> + [ARG_PTR_TO_RB_ROOT] = &map_value_or_alloc_obj_types,
> + [ARG_PTR_TO_RB_NODE] = &alloc_obj_or_non_own_ref_types,
> + [ARG_PTR_TO_RES_SPIN_LOCK] = &map_value_or_alloc_obj_types,
> + [ARG_PTR_TO_WORKQUEUE] = &map_value_types,
> + [ARG_PTR_TO_TASK_WORK] = &map_value_types,
> + [ARG_PTR_TO_IRQ_FLAG] = &stack_ptr_types,
> + [ARG_PTR_TO_ARENA] = &arena_types,
> };
Does ARG_PTR_TO_MEM need to admit allocated objects for kfuncs? Running
check_reg_type() on kfunc arguments makes mem_types the admission gate for
ARG_PTR_TO_MEM, but mem_types lists only PTR_TO_BTF_ID | PTR_TRUSTED among
the BTF-backed register types. Before this commit check_kfunc_args() had no
reg-type test in its ARG_PTR_TO_MEM / ARG_MEM_SIZE cases (the commit
removes tests from ten other cases but adds none here), so a kfunc memory
argument was validated only by check_mem_reg()/check_mem_size_reg() ->
check_helper_mem_access(), which accepts any BTF-ID register:
case PTR_TO_BTF_ID:
return check_ptr_to_btf_access(env, regs, reg, argno, 0,
access_size, access_type, -1);
/* kernel/bpf/verifier.c:7097-7099, switch is on base_type(reg->type) */
Kfunc memory arguments are checked with BPF_READ | BPF_WRITE
(verifier.c:13243 and 13289), and check_ptr_to_btf_access() has an explicit
carve-out that permits exactly those writes for BPF-allocated objects:
/* Writes are permitted with default btf_struct_access for
* program allocated objects (which always have id > 0).
*/
if (atype != BPF_READ && !type_is_ptr_alloc_obj(reg->type)) {
verbose(env, "only read is supported\n");
return -EACCES;
}
/* kernel/bpf/verifier.c:6093-6099 */
check_func_arg_reg_off() likewise enumerates PTR_TO_BTF_ID | MEM_ALLOC and
PTR_TO_BTF_ID | MEM_ALLOC | NON_OWN_REF as register types that legitimately
reach argument checking with a fixed offset (verifier.c:8567-8578). So a
program that does
struct foo *f = bpf_obj_new(typeof(*f));
...
some_kfunc(&f->buf, sizeof(f->buf)); /* void *p, u32 p__sz */
or passes such a pointer to a fixed-size scalar-pointer kfunc argument
(get_kfunc_arg_type() verifier.c:12321, ARG_PTR_TO_MEM | MEM_FIXED_SIZE)
loaded before this commit and is now rejected at check_reg_type() with
-EACCES: "R1 type=ptr_ expected=fp, pkt, pkt_meta, map_key, map_value, mem,
ringbuf_mem, buf, trusted_ptr_, ctx".
Since mem_types already excluded PTR_TO_BTF_ID | MEM_ALLOC for helpers, the
write-to-allocated-object carve-out in check_ptr_to_btf_access() was
reachable from the argument path only through kfuncs; after this commit it
is unreachable from any argument path.
This is a third, undocumented behaviour change. The changelog states "Two
behavior changes fall out of running admission first" and lists only the
ARG_CONST_MEM_SIZE precision fix and the ARG_CONST_MAP_PTR
type_may_be_null() removal. No selftest covers passing an allocated object
as a kfunc memory argument, so the capability is removed silently. Forward
search over the supplied range 8f65640265e8..a490f49b0c4e shows no change
to mem_types. If the narrowing is intended it should be stated in the
changelog (and the now-dead carve-out comment in check_ptr_to_btf_access()
updated); otherwise mem_types needs PTR_TO_BTF_ID | MEM_ALLOC (and the
NON_OWN_REF form).
[ ... ]
> @@ -13002,25 +13015,20 @@ static int check_kfunc_args(struct bpf_verifier_env *env, struct bpf_call_arg_me
> if (base_type(arg_type) == ARG_PTR_TO_BTF_ID)
> ref_id = *meta->fn->arg_btf_id[i];
>
> - ret = resolve_func_arg_type(env, reg, i, meta, insn_idx, &arg_type, &arg_size);
> + ret = resolve_func_arg_type(env, reg, i, meta, &arg_type, &arg_size);
> if (ret < 0)
> return ret;
> +
> + ret = check_reg_type(env, reg, argno, arg_type, meta);
> + if (ret < 0)
> + return ret;
> +
> ret = check_func_arg_reg_off(env, reg, argno, arg_type);
> if (ret < 0)
> return ret;
>
> switch (base_type(arg_type)) {
> case ARG_CONST_SCALAR:
> - if (reg->type != SCALAR_VALUE) {
> - verbose(env, "%s is not a scalar\n", reg_arg_name(env, argno));
> - bpf_diag_call_arg_fmt(env, insn_idx, argno, func_name,
> - "Pass an integer scalar value for this argument, not a pointer or resource object.",
> - "the kfunc expects an integer scalar, but %s is %s",
> - reg_arg_name(env, argno),
> - bpf_diag_reg_type_plain(env, reg->type));
> - return -EINVAL;
> - }
> -
> ret = process_const_arg(env, reg, argno, meta);
> if (ret < 0) {
> if (ret == -EINVAL)
[ ... ]
> @@ -11845,24 +11874,8 @@ static int resolve_func_arg_type(struct bpf_verifier_env *env,
> ref_t = btf_type_skip_modifiers(btf, ref_id, &ref_id);
> ref_tname = btf_name_by_offset(btf, ref_t->name_off);
>
> - if (!btf_type_is_scalar_struct(env, btf, ref_t)) {
> - enum bpf_reg_type reg2btf_type = lookup_reg2btf_ids(ref_id);
> - const char *expected_type;
> -
> - verbose(env, "%s is %s expected %s %s",
> - reg_arg_name(env, argno), reg_type_str(env, reg->type),
> - btf_type_str(ref_t), ref_tname);
> - if (reg2btf_type != NOT_INIT)
> - verbose(env, " or %s", reg_type_str(env, reg2btf_type));
> - verbose(env, "\n");
> - expected_type = bpf_diag_fmt_btf_type(env, btf, ref_id);
> - bpf_diag_call_arg_fmt(env, insn_idx, argno, meta->func_name,
> - "Pass a verifier-tracked pointer to the expected kernel object type, not a pointer to stack storage or another memory buffer.",
> - "the function expects a pointer to %s, but this argument is %s and cannot be used as that kernel object pointer",
> - expected_type,
> - bpf_diag_reg_type_plain(env, reg->type));
> - return -EINVAL;
> - }
> + if (!btf_type_is_scalar_struct(env, btf, ref_t))
> + return 0;
Does this break existing selftests? The patch deletes the only producer of
the "R1 is fp expected STRUCT prog_test_fail1" diagnostic from
resolve_func_arg_type(), replacing it with a bare `return 0;`. It updates
verifier/calls.c line 79 but leaves three other cases in the same file
still asserting the removed message:
calls.c:34 .errstr = "R1 is fp expected STRUCT prog_test_fail1"
calls.c:49 .errstr = "max struct nesting depth exceeded\nR1 is fp
expected STRUCT prog_test_fail2"
calls.c:64 .errstr = "R1 is fp expected STRUCT prog_test_fail3"
All three are reached: bpf_kfunc_call_test_fail1/2/3 take pointers to
struct prog_test_fail1 (has pointer member), prog_test_fail2 (nesting depth
> 4) and prog_test_fail3 (flexible array), so btf_type_is_scalar_struct()
returns false for each and the removed verbose() used to fire. After the
patch, resolve_func_arg_type() returns 0 with arg_type still
ARG_PTR_TO_BTF_ID; check_reg_type() then rejects R1 (PTR_TO_STACK) against
btf_ptr_types and prints "R1 type=fp expected=ptr_, trusted_ptr_, rcu_ptr_"
instead. The asserted substrings never appear, so these three test_verifier
cases fail.
These three sites are not fixed anywhere later in the series either: `git
show a490f49b0c4e:tools/testing/selftests/bpf/verifier/calls.c` still
contains all three stale strings, and no commit in 8f65640265e8..a490f49b0c4e
touches verifier/calls.c.
Do the test expectations in other files also need updating? Three more BPF
selftests assert the "is fp expected STRUCT ..." message that this commit
removes from resolve_func_arg_type():
tools/testing/selftests/bpf/progs/cgrp_kfunc_failure.c:67
__msg("R1 is fp expected STRUCT cgroup")
tools/testing/selftests/bpf/progs/task_kfunc_failure.c:53
__msg("R1 is fp expected STRUCT task_struct")
tools/testing/selftests/bpf/progs/verifier_vfs_reject.c:31
__msg("R1 is fp expected STRUCT task_struct")
struct cgroup and struct task_struct are not scalar structs, so each of
these used to hit the deleted verbose(); after the patch check_reg_type()
emits "R1 type=fp expected=ptr_, trusted_ptr_, rcu_ptr_" and the assertions
fail.
This is confirmed by the series itself: commit a490f49b0c4e ("bpf: Check
helper and kfunc arguments in one path"), four commits later, updates
exactly these three lines to __msg("R1 type=fp expected=ptr_, trusted_ptr_,
rcu_ptr_"). Those test-expectation updates belong in this commit, which is
the one that changes the message; as it stands the tree is bisect-broken
for 4 commits (test_progs cgrp_kfunc/task_kfunc/verifier_vfs_reject).
Does this lose test coverage for the ownership check?
rbtree_fail.c:182 and rbtree_fail.c:206 were the only two selftests
asserting the verifier check at kernel/bpf/verifier.c:13183:
verbose(env, "%s can only take non-owning or refcounted bpf_rb_node
pointer\n", func_name);
Both tests invalidate a non-owning ref across bpf_spin_unlock() (the
register becomes SCALAR_VALUE) and then call bpf_rbtree_remove(). With
check_reg_type() now running before the switch, SCALAR_VALUE is rejected by
ARG_PTR_TO_RB_NODE's alloc_obj_or_non_own_ref_types table first, so the
ownership check is no longer reached and the new expectations only assert
the generic register-class error. `grep -r "can only take non-owning or
refcounted" tools/testing/selftests/bpf/` now returns nothing: the check
has zero test coverage after this patch.
Worth double-checking whether that check is still reachable at all. The
only register types that can now reach it are PTR_TO_BTF_ID|MEM_ALLOC,
|NON_OWN_REF and |NON_OWN_REF|MEM_RCU; the NON_OWN_REF forms satisfy
type_is_non_owning_ref(), and a plain owning MEM_ALLOC register whose
reference has been released is invalidated by
release_reference()/mark_reg_invalid(). If no reachable register type
remains, the check is dead and should be dropped rather than left
uncovered; if it is reachable, the two repurposed tests should be kept (or
a replacement added) so the ownership rule stays guarded.
---
AI reviewed your patch. Please fix the bug or email reply why it's not a bug.
See: https://github.com/kernel-patches/vmtest/blob/master/ci/claude/README.md
CI run summary: https://github.com/kernel-patches/bpf/actions/runs/33995442755
^ permalink raw reply [flat|nested] 54+ messages in thread
* Re: [PATCH bpf-next v1 02/22] bpf: Address check_func_arg() arguments by argno
2026-09-05 22:44 ` bot+bpf-ci
@ 2026-09-09 17:37 ` Amery Hung
0 siblings, 0 replies; 54+ messages in thread
From: Amery Hung @ 2026-09-09 17:37 UTC (permalink / raw)
To: bot+bpf-ci
Cc: bpf, alexei.starovoitov, andrii, daniel, eddyz87, memxor,
kernel-team, ast, martin.lau, yonghong.song, mason, ihor.solodrai
On Sat, Sep 5, 2026 at 3:44 PM <bot+bpf-ci@kernel.org> wrote:
>
> > check_func_arg() derives every argument from BPF_REG_1 + arg. That works
> > for helpers, which have at most five arguments, but kfunc arguments
> > beyond the fifth are passed on the stack.
> >
> > Switch to the argno_t addressing used by check_kfunc_args(): obtain
> > argument state with get_func_arg_reg(), use argno for diagnostics, and
> > call check_reg_arg() only when the argument is held in a register. This
> > prepares check_func_arg() to be shared with kfunc verification.
> >
> > There is no functional change for helpers. reg_arg_name() renders their
> > register arguments as R%d, so the updated diagnostics remain unchanged.
>
> The changelog lists three edits this patch makes, but only two of them
> appear in the diff. The first two changes are present: "obtain argument
> state with get_func_arg_reg()" and "use argno for diagnostics" are both
> implemented. However, the third change, "call check_reg_arg() only when
> the argument is held in a register", is not in this commit.
>
> > diff --git a/kernel/bpf/verifier.c b/kernel/bpf/verifier.c
> > index f49c90beefc5..30e1f7a7553e 100644
> > --- a/kernel/bpf/verifier.c
> > +++ b/kernel/bpf/verifier.c
> > @@ -8769,10 +8769,12 @@ static int check_func_arg(struct bpf_verifier_env *env, u32 arg,
> > int insn_idx)
> > {
> > const struct bpf_func_proto *fn = meta->fn;
> > - u32 regno = BPF_REG_1 + arg;
> > - struct bpf_reg_state *reg = reg_state(env, regno);
> > + struct bpf_func_state *caller = cur_func(env);
> > + struct bpf_reg_state *regs = cur_regs(env);
> > + argno_t argno = argno_from_arg(arg + 1);
> > + struct bpf_reg_state *reg = get_func_arg_reg(caller, regs, arg);
> > enum bpf_arg_type arg_type = fn->arg_type[arg];
> > - argno_t argno = argno_from_reg(regno);
> > + int regno = reg_from_argno(argno);
> > enum bpf_reg_type type = reg->type;
>
> [ ... ]
>
> > @@ -8786,9 +8788,9 @@ static int check_func_arg(struct bpf_verifier_env *env, u32 arg,
> > return err;
> >
> > if (arg_type == ARG_ANYTHING) {
>
> check_func_arg() still calls check_reg_arg() unconditionally:
>
> check_func_arg() {
> argno_t argno = argno_from_arg(arg + 1);
> struct bpf_reg_state *reg = get_func_arg_reg(caller, regs, arg);
> enum bpf_arg_type arg_type = fn->arg_type[arg];
> int regno = reg_from_argno(argno);
> ...
> err = check_reg_arg(env, regno, SRC_OP); /* no guard checking regno >= 0 */
> if (err)
> return err;
>
> The guard that would implement the third changelog item appears 20 commits
> later in the same series. Commit a490f49b0c4e ("bpf: Check helper and kfunc
> arguments in one path") adds the conditional guard:
>
> - err = check_reg_arg(env, regno, SRC_OP);
> + if (regno >= 0) {
> + err = check_reg_arg(env, regno, SRC_OP);
>
> with its own changelog explaining it will "skip check_reg_arg() for
> stack-passed arguments that check_outgoing_stack_args() already validated."
>
> This matters for the stated goal of preparing check_func_arg() to be shared
> with kfunc verification, because the new regno can now be negative.
> reg_from_argno() returns -1 for an argno below -MAX_BPF_FUNC_REG_ARGS (for
> a stack-passed kfunc argument), and __check_reg_arg() takes a u32 regno and
> indexes regs[regno] with no bounds check, so a -1 would become an
> out-of-bounds index.
>
> There is no runtime regression at this commit: check_func_arg()'s only
> caller is check_helper_call(), whose loop is "for (i = 0; i <
> MAX_BPF_FUNC_REG_ARGS; i++)", so arg is always in [0,4] and regno is
> always 1..5.
>
> Would it be more accurate to drop the "call check_reg_arg() only when the
> argument is held in a register" clause from this commit message, or move it
> to the patch that actually implements it?
I will move the change early to this commit to reduce churn.
>
>
> ---
> AI reviewed your patch. Please fix the bug or email reply why it's not a bug.
> See: https://github.com/kernel-patches/vmtest/blob/master/ci/claude/README.md
>
> CI run summary: https://github.com/kernel-patches/bpf/actions/runs/33995442755
^ permalink raw reply [flat|nested] 54+ messages in thread
* Re: [PATCH bpf-next v1 04/22] bpf: Only compare func_id against kfunc BTF IDs for kfunc calls
2026-09-05 22:44 ` bot+bpf-ci
@ 2026-09-09 17:47 ` Amery Hung
0 siblings, 0 replies; 54+ messages in thread
From: Amery Hung @ 2026-09-09 17:47 UTC (permalink / raw)
To: bot+bpf-ci
Cc: bpf, alexei.starovoitov, andrii, daniel, eddyz87, memxor,
kernel-team, ast, martin.lau, yonghong.song, mason, ihor.solodrai
On Sat, Sep 5, 2026 at 3:44 PM <bot+bpf-ci@kernel.org> wrote:
>
> > bpf: Only compare func_id against kfunc BTF IDs for kfunc calls
> >
> > Helpers are identified by enum bpf_func_id, while kfuncs are identified
> > by a BTF ID. Both values are stored in bpf_call_arg_meta::func_id, and
> > a helper ID can have the same numeric value as a kfunc BTF ID.
> >
> > Introduce is_kfunc_call(), which first confirms that the metadata is for
> > a kfunc through meta->btf, and use it for comparisons against the special
> > kfunc BTF ID list. This complements is_helper_call() before later patches
> > move these checks into paths shared by helpers and kfuncs.
> >
> > Signed-off-by: Amery Hung <ameryhung@gmail.com>
>
> This looks like a bug fix for the helper/kfunc metadata unification.
> Should this include:
>
> Fixes: bf9c1b911f4d ("bpf: Unify helper and kfunc call argument meta")
>
This is not a bug fix. All converted comparisons are currently reached
only from kfunc verification, where meta->btf is non-NULL. It is only
needed later when we move these checks into shared paths.
>
> ---
> AI reviewed your patch. Please fix the bug or email reply why it's not a bug.
> See: https://github.com/kernel-patches/vmtest/blob/master/ci/claude/README.md
>
> CI run summary: https://github.com/kernel-patches/bpf/actions/runs/33995442755
^ permalink raw reply [flat|nested] 54+ messages in thread
* Re: [PATCH bpf-next v1 05/22] bpf: Rename ambiguous function argument types
2026-09-05 23:08 ` bot+bpf-ci
@ 2026-09-09 17:54 ` Amery Hung
0 siblings, 0 replies; 54+ messages in thread
From: Amery Hung @ 2026-09-09 17:54 UTC (permalink / raw)
To: bot+bpf-ci
Cc: bpf, alexei.starovoitov, andrii, daniel, eddyz87, memxor,
kernel-team, ast, martin.lau, yonghong.song, mason, ihor.solodrai
On Sat, Sep 5, 2026 at 4:08 PM <bot+bpf-ci@kernel.org> wrote:
>
> > ARG_ANYTHING does not accept just anything: check_func_arg() accepts
> > an initialized scalar and rejects pointers. Rename it to ARG_SCALAR.
> >
> > ARG_DONTCARE is the zero-valued terminator for the fixed-size argument
> > array rather than an argument whose value is ignored. Rename it to
> > ARG_UNUSED and stop helper argument iteration explicitly when it
> > is encountered.
> >
> > Also pass ARG_PTR_TO_MEM when checking a global subprogram memory
> > argument instead of using the prototype terminator as a placeholder.
> >
> > No functional change.
> >
> > Signed-off-by: Amery Hung <ameryhung@gmail.com>
>
> The commit message says check_func_arg() "rejects pointers" for
> ARG_SCALAR (formerly ARG_ANYTHING). Is this claim complete?
>
> Looking at kernel/bpf/verifier.c, the rejection is conditional on
> allow_ptr_leaks:
>
> if (arg_type == ARG_SCALAR) {
> if (__is_pointer_value(env->allow_ptr_leaks, reg)) {
> verbose(env, "%s leaks addr into helper function\n",
> reg_arg_name(env, argno));
> return -EACCES;
> }
> return 0;
> }
>
> and __is_pointer_value() short-circuits when allow_ptr_leaks is true:
>
> static bool __is_pointer_value(bool allow_ptr_leaks,
> const struct bpf_reg_state *reg)
> {
> if (allow_ptr_leaks)
> return false;
>
> return is_pointer_regtype(reg->type);
> }
>
> So a privileged program (one with allow_ptr_leaks set) can pass
> PTR_TO_MAP_VALUE, PTR_TO_STACK, or other pointer types to an ARG_SCALAR
> helper argument. The pointer check only applies to unprivileged programs.
>
> The later commit in this series, a490f49b0c4e, acknowledges this
> explicitly: "helpers retain their privileged ARG_SCALAR pointer-leak
> behavior."
>
> Could the commit message be more precise? Perhaps: "rejects pointers
> unless the program is allowed to leak pointers" or "rejects pointers for
> unprivileged programs"?
Since now ARG_SCALAR is introduced outside of this patchset. This
patch needs to drop most of the changes.
I will only rename ARG_DONTCARE to ARG_UNUSED and change ARG_ANYTHING
to ARG_SCALAR for global subprogram.
>
>
> ---
> AI reviewed your patch. Please fix the bug or email reply why it's not a bug.
> See: https://github.com/kernel-patches/vmtest/blob/master/ci/claude/README.md
>
> CI run summary: https://github.com/kernel-patches/bpf/actions/runs/33995442755
^ permalink raw reply [flat|nested] 54+ messages in thread
* Re: [PATCH bpf-next v1 06/22] bpf: Unify kfunc argument kinds with enum bpf_arg_type
2026-09-05 23:08 ` bot+bpf-ci
@ 2026-09-09 18:04 ` Amery Hung
0 siblings, 0 replies; 54+ messages in thread
From: Amery Hung @ 2026-09-09 18:04 UTC (permalink / raw)
To: bot+bpf-ci
Cc: bpf, alexei.starovoitov, andrii, daniel, eddyz87, memxor,
kernel-team, ast, martin.lau, yonghong.song, mason, ihor.solodrai
On Sat, Sep 5, 2026 at 4:08 PM <bot+bpf-ci@kernel.org> wrote:
>
> > diff --git a/include/linux/bpf.h b/include/linux/bpf.h
> > index 1574fe2d8cc0..f620920ea575 100644
> > --- a/include/linux/bpf.h
> > +++ b/include/linux/bpf.h
> > @@ -908,6 +908,20 @@ enum bpf_arg_type {
> > ARG_PTR_TO_TIMER, /* pointer to bpf_timer */
> > ARG_KPTR_XCHG_DEST, /* pointer to destination that kptrs are bpf_kptr_xchg'd into */
> > ARG_PTR_TO_DYNPTR, /* pointer to bpf_dynptr. See bpf_type_flag for dynptr type */
> > +
> > + ARG_CONST_SCALAR, /* scalar known at verification time */
> > + ARG_CONST_MEM_SIZE, /* ARG_MEM_SIZE that must be constant */
> > + ARG_PTR_TO_ALLOC_BTF_ID, /* pointer to an allocated object */
> > + ARG_PTR_TO_REFCOUNTED_KPTR, /* pointer to a refcounted local kptr */
> > + ARG_PTR_TO_ITER, /* pointer to an iterator */
> > + ARG_PTR_TO_LIST_HEAD, /* pointer to bpf_list_head */
> > + ARG_PTR_TO_LIST_NODE, /* pointer to bpf_list_node */
> > + ARG_PTR_TO_RB_ROOT, /* pointer to bpf_rb_root */
> > + ARG_PTR_TO_RB_NODE, /* pointer to bpf_rb_node */
> > + ARG_PTR_TO_WORKQUEUE, /* pointer to bpf_wq */
> > + ARG_PTR_TO_TASK_WORK, /* pointer to bpf_task_work */
> > + ARG_PTR_TO_IRQ_FLAG, /* pointer to saved IRQ flags on the stack */
> > + ARG_PTR_TO_RES_SPIN_LOCK, /* pointer to bpf_res_spin_lock */
> > __BPF_ARG_TYPE_MAX,
> >
> > /* Extended arg_types. */
>
> [ ... ]
>
> > diff --git a/kernel/bpf/verifier.c b/kernel/bpf/verifier.c
> > index d444e72fdd97..a0a74a5e23be 100644
> > --- a/kernel/bpf/verifier.c
> > +++ b/kernel/bpf/verifier.c
> > @@ -8533,10 +8533,9 @@ 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,
> > - bool btf_id_fixed_off_ok)
> > +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)
> > {
> > u32 type = reg->type;
> >
> > @@ -8592,12 +8591,15 @@ 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 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.
> > + * its fixed offset must be 0. bpf_refcount_acquire() returns the
> > + * pointer it was given while incrementing the refcount at the
> > + * refcount field offset, so it needs a zero offset too. 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, btf_id_fixed_off_ok);
> > + return __check_ptr_off_reg(env, reg, argno,
> > + base_type(arg_type) != ARG_PTR_TO_REFCOUNTED_KPTR);
> > case PTR_TO_CTX:
> > /*
> > * Allow fixed and variable offsets for syscall context, but
> > @@ -8613,13 +8615,6 @@ 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);
> > -}
> > -
>
> [ ... ]
>
> > @@ -11793,34 +11788,6 @@ static void btf_member_path_str(const struct btf *btf, const struct btf_member_p
> > }
> > }
> >
> > -enum kfunc_ptr_arg_type {
> > - KF_ARG_CONST_MEM_SIZE,
> > - KF_ARG_MEM_SIZE,
> > - KF_ARG_CONST,
> > - KF_ARG_CONST_ALLOC_SIZE_OR_ZERO,
> > - KF_ARG_ANYTHING,
> > - KF_ARG_PTR_TO_CTX,
> > - KF_ARG_PTR_TO_ALLOC_BTF_ID, /* Allocated object */
> > - KF_ARG_PTR_TO_REFCOUNTED_KPTR, /* Refcounted local kptr */
> > - KF_ARG_PTR_TO_DYNPTR,
> > - KF_ARG_PTR_TO_ITER,
> > - KF_ARG_PTR_TO_LIST_HEAD,
> > - KF_ARG_PTR_TO_LIST_NODE,
> > - KF_ARG_PTR_TO_BTF_ID, /* Also covers reg2btf_ids conversions */
> > - KF_ARG_PTR_TO_MEM,
> > - KF_ARG_PTR_TO_CALLBACK,
> > - KF_ARG_PTR_TO_RB_ROOT,
> > - KF_ARG_PTR_TO_RB_NODE,
> > - KF_ARG_PTR_TO_CONST_STR,
> > - KF_ARG_CONST_MAP_PTR,
> > - KF_ARG_PTR_TO_TIMER,
> > - KF_ARG_PTR_TO_WORKQUEUE,
> > - KF_ARG_PTR_TO_IRQ_FLAG,
> > - KF_ARG_PTR_TO_RES_SPIN_LOCK,
> > - KF_ARG_PTR_TO_TASK_WORK,
> > - KF_ARG_PTR_TO_ARENA,
> > -};
> > -
>
> This deletion removes KF_ARG_PTR_TO_REFCOUNTED_KPTR from the tree. Does
> the kerneldoc comment in include/linux/bpf_verifier.h for struct
> bpf_call_arg_meta need updating? It still refers to the deleted name:
>
> /* arg_{btf,btf_id,owning_ref} are used by kfunc-specific handling,
> * ...
> * bpf_refcount_acquire (via KF_ARG_PTR_TO_REFCOUNTED_KPTR arg type)
> */
>
> The replacement is ARG_PTR_TO_REFCOUNTED_KPTR.
>
> [ ... ]
>
> > @@ -12826,13 +12793,11 @@ static int check_kfunc_args(struct bpf_verifier_env *env, struct bpf_call_arg_me
> > */
> > for (i = 0; i < nargs; i++) {
> > struct bpf_reg_state *reg = get_func_arg_reg(caller, regs, i);
> > + enum bpf_arg_type arg_type = meta->fn->arg_type[i];
> > const struct btf_type *t, *ref_t, *resolve_ret;
> > - enum bpf_arg_type arg_type = ARG_UNUSED;
> > argno_t argno = argno_from_arg(i + 1);
> > int regno = reg_from_argno(argno);
> > - bool btf_id_fixed_off_ok = true;
> > u32 ref_id = args[i].type, type_size;
> > - int kf_arg_type = meta->fn->arg_type[i];
>
> [ ... ]
>
> > @@ -12901,54 +12866,14 @@ static int check_kfunc_args(struct bpf_verifier_env *env, struct bpf_call_arg_me
> > ref_tname = btf_name_by_offset(btf, ref_t->name_off);
> > }
> >
> > - switch (base_type(kf_arg_type)) {
> > - case KF_ARG_CONST:
> > - case KF_ARG_CONST_MEM_SIZE:
> > - case KF_ARG_MEM_SIZE:
> > - case KF_ARG_ANYTHING:
> > - case KF_ARG_CONST_ALLOC_SIZE_OR_ZERO:
> > - case KF_ARG_PTR_TO_ALLOC_BTF_ID:
> > - case KF_ARG_PTR_TO_BTF_ID:
> > - case KF_ARG_CONST_MAP_PTR:
> > - case KF_ARG_PTR_TO_ITER:
> > - case KF_ARG_PTR_TO_LIST_HEAD:
> > - case KF_ARG_PTR_TO_LIST_NODE:
> > - case KF_ARG_PTR_TO_RB_ROOT:
> > - case KF_ARG_PTR_TO_RB_NODE:
> > - case KF_ARG_PTR_TO_MEM:
> > - case KF_ARG_PTR_TO_CALLBACK:
> > - case KF_ARG_PTR_TO_CONST_STR:
> > - case KF_ARG_PTR_TO_WORKQUEUE:
> > - case KF_ARG_PTR_TO_TIMER:
> > - case KF_ARG_PTR_TO_TASK_WORK:
> > - case KF_ARG_PTR_TO_IRQ_FLAG:
> > - case KF_ARG_PTR_TO_RES_SPIN_LOCK:
> > - case KF_ARG_PTR_TO_ARENA:
> > - break;
> > - case KF_ARG_PTR_TO_DYNPTR:
> > - arg_type = ARG_PTR_TO_DYNPTR;
> > - break;
> > - 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;
> > - }
> > -
> > if (regno == meta->release_regno)
> > arg_type |= OBJ_RELEASE;
> > - ret = __check_func_arg_reg_off(env, reg, argno, arg_type,
> > - btf_id_fixed_off_ok);
> > + ret = check_func_arg_reg_off(env, reg, argno, arg_type);
> > if (ret < 0)
> > return ret;
>
> Dropping the translation switch means check_func_arg_reg_off() now
> receives the flag-carrying arg_type from get_kfunc_arg_type() instead of
> a bare base type. Does this affect the PTR_TO_CTX case in that function?
>
> Looking at check_func_arg_reg_off():
>
> case PTR_TO_CTX:
> if (arg_type != ARG_PTR_TO_CTX && is_var_ctx_off_allowed(env->prog))
> return 0;
>
> The test compares the full value rather than base_type(arg_type).
> Before this commit, the translation switch assigned 'arg_type =
> ARG_PTR_TO_CTX;' with no flags. After it, get_kfunc_arg_type() can
> return ARG_PTR_TO_CTX | PTR_MAYBE_NULL for a context-typed kfunc
> parameter carrying a '__nullable' suffix. In BPF_PROG_TYPE_SYSCALL
> programs (the only program type where is_var_ctx_off_allowed() is true),
> would a nullable ctx arg skip the __check_ptr_off_reg() call, taking the
> 'return 0' path instead of rejecting modified ctx pointers?
>
> While this appears unreachable today (no in-tree kfunc has both a
> context-typed parameter and a __nullable suffix), is the "No functional
> change" claim literally exact, or would converting the test to
> 'base_type(arg_type) != ARG_PTR_TO_CTX' make the unification flag-safe?
Make sense to compare against base type. Will change.
>
>
> ---
> AI reviewed your patch. Please fix the bug or email reply why it's not a bug.
> See: https://github.com/kernel-patches/vmtest/blob/master/ci/claude/README.md
>
> CI run summary: https://github.com/kernel-patches/bpf/actions/runs/33995442755
^ permalink raw reply [flat|nested] 54+ messages in thread
* Re: [PATCH bpf-next v1 07/22] bpf: Align helper and kfunc ARG_PTR_TO_PROG_AUX handling
2026-09-05 23:08 ` bot+bpf-ci
@ 2026-09-09 18:23 ` Amery Hung
0 siblings, 0 replies; 54+ messages in thread
From: Amery Hung @ 2026-09-09 18:23 UTC (permalink / raw)
To: bot+bpf-ci
Cc: bpf, alexei.starovoitov, andrii, daniel, eddyz87, memxor,
kernel-team, ast, martin.lau, yonghong.song, mason, ihor.solodrai
On Sat, Sep 5, 2026 at 4:08 PM <bot+bpf-ci@kernel.org> wrote:
>
> > diff --git a/include/linux/bpf.h b/include/linux/bpf.h
> > index f620920ea575..547703f54a89 100644
> > --- a/include/linux/bpf.h
> > +++ b/include/linux/bpf.h
> > @@ -922,6 +922,7 @@ enum bpf_arg_type {
> > ARG_PTR_TO_TASK_WORK, /* pointer to bpf_task_work */
> > ARG_PTR_TO_IRQ_FLAG, /* pointer to saved IRQ flags on the stack */
> > ARG_PTR_TO_RES_SPIN_LOCK, /* pointer to bpf_res_spin_lock */
> > + ARG_PTR_TO_PROG_AUX, /* pointer to the caller's bpf_prog_aux */
> > __BPF_ARG_TYPE_MAX,
> >
> > /* Extended arg_types. */
>
> [ ... ]
>
> > diff --git a/kernel/bpf/helpers.c b/kernel/bpf/helpers.c
> > index 72bfd8f93ae4..1b731aad54da 100644
> > --- a/kernel/bpf/helpers.c
> > +++ b/kernel/bpf/helpers.c
> > @@ -1510,6 +1510,7 @@ static const struct bpf_func_proto bpf_timer_set_callback_proto = {
> > .ret_type = RET_INTEGER,
> > .arg1_type = ARG_PTR_TO_TIMER,
> > .arg2_type = ARG_PTR_TO_FUNC,
> > + .arg3_type = ARG_PTR_TO_PROG_AUX,
> > };
>
> [ ... ]
>
> > diff --git a/kernel/bpf/verifier.c b/kernel/bpf/verifier.c
> > index a0a74a5e23be..22f5aff76b40 100644
> > --- a/kernel/bpf/verifier.c
> > +++ b/kernel/bpf/verifier.c
>
> [ ... ]
>
> > @@ -9400,9 +9406,42 @@ static bool check_proto_release_reg(const struct bpf_func_proto *fn, struct bpf_
> > return true;
> > }
> >
> > -static int check_func_proto(const struct bpf_func_proto *fn, struct bpf_call_arg_meta *meta)
> > +static bool check_arg_prog_aux(struct bpf_verifier_env *env,
> > + const struct bpf_func_proto *proto)
> > {
> > - return check_raw_mode_ok(fn, meta) &&
> > + bool seen = false;
> > + argno_t argno;
> > + u32 i;
> > +
> > + for (i = 0; i < ARRAY_SIZE(proto->arg_type); i++) {
> > + if (proto->arg_type[i] == ARG_UNUSED)
> > + break;
>
> Does this early break work correctly for kfunc protos?
>
> gen_kfunc_arg_proto() in verifier.c skips __ign and implicit arguments
> without assigning a type, leaving ARG_UNUSED holes in the middle of
> proto->arg_type. For example:
>
> bpf_arena_alloc_pages(void *p__map, void *addr__ign, u32 page_cnt,
> int node_id, u64 flags)
>
> leaves arg_type[1] == ARG_UNUSED while arg_type[2], [3], [4] have real
> types. Similarly:
>
> bpf_list_push_front_impl(head, node, meta__ign, off)
>
> leaves arg_type[2] == ARG_UNUSED while arg_type[3] is a real type.
>
> The loop above terminates at the first hole and never inspects remaining
> arguments. If a kfunc has a prog_aux argument after a hole, or has >5
> arguments with a prog_aux argument in position 6+, these checks are
> unreachable.
>
> The consequence is a net loss of validation. check_kfunc_args()
> previously performed both checks unconditionally, per argument, from
> BTF. Both were deleted from that function (which now does an unguarded
> cur_aux(env)->arg_prog = regno) and moved here, where the early break
> makes them unreachable for affected signatures.
>
> The stack-argument check is the more critical one. argno_from_arg(i + 1)
> / reg_from_argno() return -1 for arguments 6 and above, and
> check_kfunc_args() now assigns that value into the 4-bit arg_prog
> bitfield (include/linux/bpf_verifier.h), yielding arg_prog == 0xf.
> bpf_fixup_kfunc_call() would then emit BPF_LD_IMM64(15, (long)prog->aux),
> storing to a non-existent register number.
>
> No in-tree kfunc currently combines >5 arguments, a non-implicit
> bpf_prog_aux argument and a preceding hole, so the escalation is latent;
> the validation loss itself is not.
>
> > + if (proto->arg_type[i] != ARG_PTR_TO_PROG_AUX)
> > + continue;
> > +
> > + if (seen) {
> > + verifier_bug(env, "Only 1 prog->aux argument supported");
> > + return false;
> > + }
> > +
> > + argno = argno_from_arg(i + 1);
> > + if (reg_from_argno(argno) < 0) {
> > + verbose(env, "%s prog->aux cannot be a stack argument\n",
> > + reg_arg_name(env, argno));
> > + return false;
> > + }
> > +
> > + seen = true;
> > + }
> > +
> > + return true;
> > +}
> > +
> > +static int check_func_proto(struct bpf_verifier_env *env, const struct bpf_func_proto *fn,
> > + struct bpf_call_arg_meta *meta)
> > +{
> > + return check_arg_prog_aux(env, fn) &&
> > + check_raw_mode_ok(fn, meta) &&
> > check_arg_pair_ok(fn) &&
> > check_mem_arg_rw_flag_ok(fn) &&
> > check_proto_release_reg(fn, meta) &&
>
> [ ... ]
>
> > @@ -12044,6 +12083,9 @@ get_kfunc_arg_type(struct bpf_verifier_env *env, struct bpf_call_arg_meta *meta,
> > const char *ref_tname = NULL;
> > int arg_type;
> >
> > + if (is_kfunc_arg_prog_aux(meta->btf, &args[arg]))
> > + return ARG_PTR_TO_PROG_AUX;
> > +
> > t = btf_type_skip_modifiers(meta->btf, args[arg].type, NULL);
> >
> > /* Scalar arguments are classified from their BTF suffix/name alone. */
> > @@ -12192,9 +12234,7 @@ static int gen_kfunc_arg_proto(struct bpf_verifier_env *env, struct bpf_call_arg
> > }
> >
> > for (i = 0; i < nargs; i++) {
> > - if (is_kfunc_arg_prog_aux(btf, &args[i]) ||
> > - is_kfunc_arg_ignore(btf, &args[i]) ||
> > - is_kfunc_arg_implicit(meta, i))
> > + if (is_kfunc_arg_ignore(btf, &args[i]) || is_kfunc_arg_implicit(meta, i))
> > continue;
>
> Does ARG_PTR_TO_PROG_AUX get recorded for KF_IMPLICIT_ARGS kfuncs with
> a bpf_prog_aux argument?
>
> The loop above skips implicit arguments before calling
> get_kfunc_arg_type(), so those arguments never get a type and
> proto->arg_type[i] stays ARG_UNUSED.
>
> resolve_btfids' is_kf_implicit_arg() restricts implicit arguments to
> exactly two types: bpf_prog_aux and btf_struct_meta.
> process_kfunc_with_implicit_args() rewrites the BTF at build time: the
> visible kfunc FUNC gets a truncated func_proto without the implicit
> args, while a <name>_impl FUNC keeps the full one. fetch_kfunc_meta()
> sets meta->func_proto to the _impl proto, so is_kfunc_arg_implicit()
> returns true for the trailing bpf_prog_aux argument in KF_IMPLICIT_ARGS
> kfuncs.
>
> Concrete trace for bpf_wq_set_callback (kernel/bpf/helpers.c, registered
> KF_IMPLICIT_ARGS):
>
> 1. nargs = 4 from the _impl proto (wq, callback_fn, flags, aux); the
> visible proto has vlen 3.
>
> 2. i == 3: is_kfunc_arg_implicit(meta, 3) is true -> continue,
> get_kfunc_arg_type() never runs, proto->arg_type[3] == ARG_UNUSED.
>
> 3. check_kfunc_args() i == 3: arg_type == ARG_UNUSED, so the
> ARG_PTR_TO_PROG_AUX branch is skipped and the next line
> (is_kfunc_arg_implicit) continues. cur_aux(env)->arg_prog is never
> set.
>
> 4. bpf_fixup_kfunc_call() tests env->insn_aux_data[insn_idx].arg_prog,
> which is 0, so no BPF_LD_IMM64(R4, prog->aux) is emitted.
>
> 5. At run time bpf_wq_set_callback() is entered with whatever the
> program left in R4 and immediately dereferences it:
>
> return __bpf_async_set_callback(async, callback_fn, aux->prog);
>
> This affects every KF_IMPLICIT_ARGS kfunc with a bpf_prog_aux argument:
> bpf_wq_set_callback (helpers.c, aux->prog), bpf_task_work_schedule_signal
> (helpers.c), bpf_task_work_schedule_resume (helpers.c), bpf_stream_vprintk
> (stream.c, bpf_stream_get(stream_id, aux)), bpf_stream_print_stack (stream.c),
> scx_bpf_cid_override (kernel/sched/ext/cid.c), plus the testmod kfuncs
> bpf_kfunc_implicit_arg and bpf_kfunc_multi_st_ops_test_1_assoc. The
> register is program-controlled, so this is an arbitrary kernel pointer
> dereference reachable from a loadable BPF program.
>
> The immediately following commit in the series, ef7ad41fb8d0, repairs
> exactly this by adding ARG_IGNORE, moving the ignore/implicit test after
> is_kfunc_arg_prog_aux() inside get_kfunc_arg_type(), and deleting the
> pre-classification skip from gen_kfunc_arg_proto(). The ordering fix
> from ef7ad41fb8d0 belongs in this commit to avoid a bisect hole.
>
> >
> > arg_type = get_kfunc_arg_type(env, meta, args, i, nargs);
> > @@ -12204,7 +12244,7 @@ static int gen_kfunc_arg_proto(struct bpf_verifier_env *env, struct bpf_call_arg
> > proto->arg_type[i] = arg_type;
> > }
> >
> > - return 0;
> > + return check_arg_prog_aux(env, proto) ? 0 : -EINVAL;
> > }
>
> [ ... ]
>
> > @@ -12799,18 +12839,7 @@ static int check_kfunc_args(struct bpf_verifier_env *env, struct bpf_call_arg_me
> > int regno = reg_from_argno(argno);
> > u32 ref_id = args[i].type, type_size;
> >
> > - if (is_kfunc_arg_prog_aux(btf, &args[i])) {
> > - /* Reject repeated use bpf_prog_aux */
> > - if (meta->arg_prog) {
> > - verifier_bug(env, "Only 1 prog->aux argument supported per-kfunc");
> > - return -EFAULT;
> > - }
> > - if (regno < 0) {
> > - verbose(env, "%s prog->aux cannot be a stack argument\n",
> > - reg_arg_name(env, argno));
> > - return -EINVAL;
> > - }
> > - meta->arg_prog = true;
> > + if (arg_type == ARG_PTR_TO_PROG_AUX) {
> > cur_aux(env)->arg_prog = regno;
> > continue;
> > }
>
Neither issue affects the final series state because the next patch
introduces ARG_IGNORE and classifies ignored and implicit arguments.
However, the intermediate state is not bisect-safe. I will switch the
order of the two patches.
>
> ---
> AI reviewed your patch. Please fix the bug or email reply why it's not a bug.
> See: https://github.com/kernel-patches/vmtest/blob/master/ci/claude/README.md
>
> CI run summary: https://github.com/kernel-patches/bpf/actions/runs/33995442755
^ permalink raw reply [flat|nested] 54+ messages in thread
* Re: [PATCH bpf-next v1 08/22] bpf: Classify kfunc arguments the verifier ignores
2026-09-05 22:44 ` bot+bpf-ci
@ 2026-09-09 18:27 ` Amery Hung
0 siblings, 0 replies; 54+ messages in thread
From: Amery Hung @ 2026-09-09 18:27 UTC (permalink / raw)
To: bot+bpf-ci
Cc: bpf, alexei.starovoitov, andrii, daniel, eddyz87, memxor,
kernel-team, ast, martin.lau, yonghong.song, mason, ihor.solodrai
On Sat, Sep 5, 2026 at 3:44 PM <bot+bpf-ci@kernel.org> wrote:
>
> > bpf: Classify kfunc arguments the verifier ignores
> >
> > The verifier does not inspect __ign arguments or the implicit
> > arguments of a KF_IMPLICIT_ARGS kfunc. get_kfunc_arg_type() leaves
> > them unclassified, so gen_kfunc_arg_proto() skips them and
> > check_kfunc_args() repeats the same BTF predicates for every call.
> >
> > Add ARG_IGNORE and classify both cases in get_kfunc_arg_type(). The
> > generated prototype can then record every argument, and the call site
> > can consume the recorded type without deriving the classification
> > again.
> >
> > No functional change.
> >
> > Signed-off-by: Amery Hung <ameryhung@gmail.com>
>
> Does the "No functional change." line accurately describe this patch?
>
> Looking at the parent commit 654bdd813138, the gen_kfunc_arg_proto() loop
> began with:
>
> kernel/bpf/verifier.c (at 654bdd813138):
> for (i = 0; i < nargs; i++) {
> if (is_kfunc_arg_ignore(btf, &args[i]) || is_kfunc_arg_implicit(meta, i))
> continue;
>
> arg_type = get_kfunc_arg_type(env, meta, args, i, nargs);
> ...
> }
>
> So get_kfunc_arg_type() was never called for any prog_aux argument, since
> prog_aux arguments are always implicit. The proto->arg_type[aux_idx] stayed
> ARG_UNUSED (0) and could never become ARG_PTR_TO_PROG_AUX.
>
> Then check_kfunc_args() reads this proto:
>
> kernel/bpf/verifier.c (at 654bdd813138):
> enum bpf_arg_type arg_type = meta->fn->arg_type[i];
> ...
> if (arg_type == ARG_PTR_TO_PROG_AUX) {
> cur_aux(env)->arg_prog = regno;
> continue;
> }
>
> With ARG_UNUSED recorded, arg_prog stayed 0.
>
> The fixup in kernel/bpf/fixups.c is gated on aux->arg_prog:
>
> kernel/bpf/fixups.c:
> if (aux->arg_prog) {
> insn_buf[0] = BPF_LD_IMM64(aux->arg_prog, (long)prog->aux);
> ...
> }
>
> So no BPF_LD_IMM64 was emitted and the kfunc was called with an uninitialized
> register in place of prog->aux.
>
> This patch fixes that by testing is_kfunc_arg_prog_aux() before the new
> ARG_IGNORE test in get_kfunc_arg_type():
>
> > diff --git a/kernel/bpf/verifier.c b/kernel/bpf/verifier.c
> > index 22f5aff76b40..7416f1e16aa9 100644
> > --- a/kernel/bpf/verifier.c
> > +++ b/kernel/bpf/verifier.c
> > @@ -12086,6 +12088,9 @@ get_kfunc_arg_type(struct bpf_verifier_env *env, struct bpf_call_arg_meta *meta,
> > if (is_kfunc_arg_prog_aux(meta->btf, &args[arg]))
> > return ARG_PTR_TO_PROG_AUX;
> >
> > + if (is_kfunc_arg_ignore(meta->btf, &args[arg]) || is_kfunc_arg_implicit(meta, arg))
> > + return ARG_IGNORE;
> > +
> > t = btf_type_skip_modifiers(meta->btf, args[arg].type, NULL);
>
> and dropping the continue from gen_kfunc_arg_proto():
>
> > @@ -12215,7 +12220,6 @@ get_kfunc_arg_type(struct bpf_verifier_env *env, struct bpf_call_arg_meta *meta,
> > static int gen_kfunc_arg_proto(struct bpf_verifier_env *env, struct bpf_call_arg_meta *meta,
> > struct bpf_func_proto *proto)
> > {
> > - const struct btf *btf = meta->btf;
> > const struct btf_param *args;
> > u32 i, nargs;
> > int arg_type;
> > @@ -12234,9 +12238,6 @@ static int gen_kfunc_arg_proto(struct bpf_verifier_env *env, struct bpf_call_arg
> > }
> >
> > for (i = 0; i < nargs; i++) {
> > - if (is_kfunc_arg_ignore(btf, &args[i]) || is_kfunc_arg_implicit(meta, i))
> > - continue;
> > -
> > arg_type = get_kfunc_arg_type(env, meta, args, i, nargs);
> > if (arg_type < 0)
> > return arg_type;
>
> so ARG_PTR_TO_PROG_AUX is again recorded in the generated prototype and
> arg_prog/BPF_LD_IMM64 are emitted.
>
> This affects bpf_wq_set_callback(), bpf_task_work_schedule_signal()/_resume(),
> bpf_stream_vprintk(), bpf_stream_print_stack() and all scx_bpf_* sched_ext
> kfuncs.
>
> Should this patch either be squashed with 654bdd813138 or have the "No
> functional change." line qualified, since it repairs a bug where kfuncs with a
> prog_aux argument were called with an uninitialized register?
>
> Also, the ordering is load-bearing: swapping the two tests in
> get_kfunc_arg_type() would silently reintroduce the broken code generation,
> since prog_aux arguments are always implicit. Should a comment note that the
> prog_aux test must stay ahead of the ARG_IGNORE test?
Will reorder patch 7 and 8.
>
>
> ---
> AI reviewed your patch. Please fix the bug or email reply why it's not a bug.
> See: https://github.com/kernel-patches/vmtest/blob/master/ci/claude/README.md
>
> CI run summary: https://github.com/kernel-patches/bpf/actions/runs/33995442755
^ permalink raw reply [flat|nested] 54+ messages in thread
* Re: [PATCH bpf-next v1 11/22] bpf: Set MEM_RCU when generating kfunc argument types
2026-09-05 22:44 ` bot+bpf-ci
@ 2026-09-09 18:41 ` Amery Hung
0 siblings, 0 replies; 54+ messages in thread
From: Amery Hung @ 2026-09-09 18:41 UTC (permalink / raw)
To: bot+bpf-ci
Cc: bpf, alexei.starovoitov, andrii, daniel, eddyz87, memxor,
kernel-team, ast, martin.lau, yonghong.song, mason, ihor.solodrai
On Sat, Sep 5, 2026 at 3:44 PM <bot+bpf-ci@kernel.org> wrote:
>
> > diff --git a/kernel/bpf/verifier.c b/kernel/bpf/verifier.c
> > index f43462a43ee9..c5b394e847e6 100644
> > --- a/kernel/bpf/verifier.c
> > +++ b/kernel/bpf/verifier.c
> > @@ -12239,6 +12239,14 @@ get_kfunc_arg_type(struct bpf_verifier_env *env, struct bpf_call_arg_meta *meta,
> > if (is_kfunc_release(meta) && arg == 0)
> > arg_type |= OBJ_RELEASE;
> >
> > + /*
> > + * A KF_RCU kfunc accepts an RCU-protected pointer where it would
> > + * otherwise demand a referenced or trusted one. Only ARG_PTR_TO_BTF_ID
> > + * looks at where its register came from, so leave the other kinds alone.
> > + */
> > + if (base_type(arg_type) == ARG_PTR_TO_BTF_ID && is_kfunc_rcu(meta))
> > + arg_type |= MEM_RCU;
> > +
>
> This isn't a bug, but would it be clearer to state the actual reason
> for the ARG_PTR_TO_BTF_ID guard here? The comment says only
> ARG_PTR_TO_BTF_ID looks at register provenance, but several other
> kinds handled in this same function do inspect provenance:
> ARG_PTR_TO_ALLOC_BTF_ID and ARG_PTR_TO_REFCOUNTED_KPTR check
> reg_is_referenced()/type_is_ptr_alloc_obj(), and
> ARG_PTR_TO_LIST_NODE/ARG_PTR_TO_RB_NODE/ARG_PTR_TO_LIST_HEAD/ARG_PTR_TO_RB_ROOT
> check type_is_non_owning_ref().
>
> The guard is actually needed for the reason the changelog gives: to
> prevent MEM_RCU from leaking into arguments whose handling compares
> the whole arg_type, like the ARG_PTR_TO_CTX test in
> check_func_arg_reg_off().
I will adjust the comment. Besides, I will also update the commit msg
to drop the "ARG_PTR_TO_CTX test in check_func_arg_reg_off()" example
since it now compares base type.
>
> > return arg_type;
> > }
> >
> > @@ -13174,7 +13182,7 @@ static int check_kfunc_args(struct bpf_verifier_env *env, struct bpf_call_arg_me
> > reg2btf_ids[base_type(reg->type)]) {
> > if (!is_trusted_reg(env, reg) ||
> > bpf_type_has_unsafe_modifiers(reg->type)) {
> > - if (!is_kfunc_rcu(meta)) {
> > + if (!(arg_type & MEM_RCU)) {
> > const char *expected_type;
> >
> > expected_type = bpf_diag_fmt_btf_type(env, btf, ref_id);
>
>
> ---
> AI reviewed your patch. Please fix the bug or email reply why it's not a bug.
> See: https://github.com/kernel-patches/vmtest/blob/master/ci/claude/README.md
>
> CI run summary: https://github.com/kernel-patches/bpf/actions/runs/33995442755
^ permalink raw reply [flat|nested] 54+ messages in thread
* Re: [PATCH bpf-next v1 12/22] bpf: Resolve BTF ID of ARG_PTR_TO_BTF_ID in kfunc bpf_func_proto
2026-09-05 23:08 ` bot+bpf-ci
@ 2026-09-09 20:42 ` Amery Hung
0 siblings, 0 replies; 54+ messages in thread
From: Amery Hung @ 2026-09-09 20:42 UTC (permalink / raw)
To: bot+bpf-ci
Cc: bpf, alexei.starovoitov, andrii, daniel, eddyz87, memxor,
kernel-team, ast, martin.lau, yonghong.song, mason, ihor.solodrai
On Sat, Sep 5, 2026 at 4:08 PM <bot+bpf-ci@kernel.org> wrote:
>
> > diff --git a/include/linux/bpf.h b/include/linux/bpf.h
> > index 1f78746e0601..fe6ab92ceeff 100644
> > --- a/include/linux/bpf.h
> > +++ b/include/linux/bpf.h
> > @@ -1020,13 +1020,13 @@ struct bpf_func_proto {
> > };
> > union {
> > struct {
> > - u32 *arg1_btf_id;
> > - u32 *arg2_btf_id;
> > - u32 *arg3_btf_id;
> > - u32 *arg4_btf_id;
> > - u32 *arg5_btf_id;
> > + const u32 *arg1_btf_id;
> > + const u32 *arg2_btf_id;
> > + const u32 *arg3_btf_id;
> > + const u32 *arg4_btf_id;
> > + const u32 *arg5_btf_id;
> > };
> > - u32 *arg_btf_id[MAX_BPF_FUNC_ARGS];
> > + const u32 *arg_btf_id[MAX_BPF_FUNC_ARGS];
> > struct {
> > size_t arg1_size;
> > size_t arg2_size;
>
> [ ... ]
>
> > diff --git a/kernel/bpf/verifier.c b/kernel/bpf/verifier.c
> > index c5b394e847e6..b6e36df72d13 100644
> > --- a/kernel/bpf/verifier.c
> > +++ b/kernel/bpf/verifier.c
>
> [ ... ]
>
> > @@ -12082,13 +12082,18 @@ bool bpf_is_kfunc_pkt_changing(struct bpf_call_arg_meta *meta)
> >
> > static int
> > get_kfunc_arg_type(struct bpf_verifier_env *env, struct bpf_call_arg_meta *meta,
> > - const struct btf_param *args, int arg, int nargs)
> > + const struct btf_param *args, int arg, int nargs,
> > + struct bpf_func_proto *proto)
> > {
> > const struct btf_type *t, *ref_t = NULL;
> > + const u32 *ref_id_ptr = NULL;
> > argno_t argno = argno_from_arg(arg + 1);
> > const char *ref_tname = NULL;
> > + u32 ref_id;
> > int arg_type;
> >
> > + proto->arg_btf_id[arg] = NULL;
> > +
> > if (is_kfunc_arg_prog_aux(meta->btf, &args[arg]))
> > return ARG_PTR_TO_PROG_AUX;
> >
> > @@ -12116,7 +12121,11 @@ get_kfunc_arg_type(struct bpf_verifier_env *env, struct bpf_call_arg_meta *meta,
> > reg_arg_name(env, argno), btf_type_str(t));
> > return -EINVAL;
> > }
> > - ref_t = btf_type_skip_modifiers(meta->btf, t->type, NULL);
> > + /* Keep a pointer to the BTF field containing the resolved referent ID. */
> > + ref_id_ptr = &t->type;
> > + ref_t = btf_type_skip_modifiers(meta->btf, *ref_id_ptr, &ref_id);
> > + while (*ref_id_ptr != ref_id)
> > + ref_id_ptr = &btf_type_by_id(meta->btf, *ref_id_ptr)->type;
> > ref_tname = btf_name_by_offset(meta->btf, ref_t->name_off);
> >
> > /* In this function, we verify the kfunc's BTF as per the argument type,
>
> [ ... ]
>
> > @@ -12239,13 +12248,21 @@ get_kfunc_arg_type(struct bpf_verifier_env *env, struct bpf_call_arg_meta *meta,
> > if (is_kfunc_release(meta) && arg == 0)
> > arg_type |= OBJ_RELEASE;
> >
> > - /*
> > - * A KF_RCU kfunc accepts an RCU-protected pointer where it would
> > - * otherwise demand a referenced or trusted one. Only ARG_PTR_TO_BTF_ID
> > - * looks at where its register came from, so leave the other kinds alone.
> > - */
> > - if (base_type(arg_type) == ARG_PTR_TO_BTF_ID && is_kfunc_rcu(meta))
> > - arg_type |= MEM_RCU;
> > + if (base_type(arg_type) == ARG_PTR_TO_BTF_ID) {
> > + /* A __map argument names a vmlinux type rather than one from
> > + * the kfunc's own BTF.
> > + */
> ^^^^
>
> This isn't a bug, but both multi-line comments added in this block start
> their text on the same line as the opening '/*'. The BPF subsystem's
> comment style puts the opening '/*' on its own line. The second comment
> below rewrites one that was previously in the preferred style (with '/*'
> on its own line), and another comment added by this patch in
> check_kfunc_args() does follow the standard. Could these two be
> reformatted to match?
Will fix the comment style.
>
> > + if (is_kfunc_arg_map(meta->btf, &args[arg]))
> > + proto->arg_btf_id[arg] = reg2btf_ids[CONST_PTR_TO_MAP];
> > + else
> > + proto->arg_btf_id[arg] = ref_id_ptr;
> > +
> > + /* A KF_RCU kfunc accepts an RCU-protected pointer where it would
> > + * otherwise demand a referenced or trusted one.
> > + */
> ^^^^
>
> > + if (is_kfunc_rcu(meta))
> > + arg_type |= MEM_RCU;
> > + }
> >
> > return arg_type;
> > }
>
>
> ---
> AI reviewed your patch. Please fix the bug or email reply why it's not a bug.
> See: https://github.com/kernel-patches/vmtest/blob/master/ci/claude/README.md
>
> CI run summary: https://github.com/kernel-patches/bpf/actions/runs/33995442755
^ permalink raw reply [flat|nested] 54+ messages in thread
* Re: [PATCH bpf-next v1 16/22] bpf: Drop redundant BTF pointer helper write rejection
2026-09-05 23:08 ` bot+bpf-ci
@ 2026-09-09 20:48 ` Amery Hung
0 siblings, 0 replies; 54+ messages in thread
From: Amery Hung @ 2026-09-09 20:48 UTC (permalink / raw)
To: bot+bpf-ci
Cc: bpf, alexei.starovoitov, andrii, daniel, eddyz87, memxor,
kernel-team, ast, martin.lau, yonghong.song, mason, ihor.solodrai
On Sat, Sep 5, 2026 at 4:08 PM <bot+bpf-ci@kernel.org> wrote:
>
> > commit 5188979f5e68c2684632ba13facdb46febabed5a
> > Author: Amery Hung <ameryhung@gmail.com>
> > Date: Sat Sep 5 15:01:11 2026 -0700
> >
> > bpf: Drop redundant BTF pointer helper write rejection
> >
> > check_reg_type() rejects a trusted PTR_TO_BTF_ID admitted by
> > mem_types unless the argument carries MEM_RDONLY. This duplicates
> > argument-specific memory access validation and is too conservative for
> > map keys and input map values, whose read direction is not represented
> > by MEM_RDONLY.
> >
> > Since commit fa75b7c85b0d ("bpf: Enforce write checks for BTF pointer
> > helper access"), check_helper_mem_access() forwards the requested access
> > type to check_ptr_to_btf_access(). Map keys always request read access.
> > Map values request write access only when tagged with MEM_WRITE.
> > ARG_PTR_TO_MEM prototypes must carry either MEM_RDONLY or MEM_WRITE,
> > and size arguments use the direction of their associated memory
> > argument. btf_struct_access() therefore checks the effective access
> ^^^^
> > direction in every case.
>
> Is btf_struct_access() the correct function name here?
>
> btf_struct_access() in kernel/bpf/btf.c explicitly ignores the access
> direction; the parameter is declared unused:
>
> int btf_struct_access(struct bpf_verifier_log *log,
> const struct bpf_reg_state *reg,
> int off, int size, enum bpf_access_type atype __maybe_unused,
> u32 *next_btf_id, enum bpf_type_flag *flag,
> const char **field_name)
>
> The direction is enforced one level up, in check_ptr_to_btf_access()
> in kernel/bpf/verifier.c, which:
> - rejects any non-read of a faultable pointer,
> - for atype == BPF_WRITE dispatches to the program-type callback
> env->ops->btf_struct_access() when one is installed, and
> - otherwise rejects with "only read is supported" unless the register
> is a program-allocated object.
>
> Reading btf_struct_access() as the op pointer env->ops->btf_struct_access
> (same name, so the sentence is ambiguous) does not make the claim correct
> either: that op is NULL for most program types, and in that case the
> direction check is check_ptr_to_btf_access()'s own "only read is
> supported" rejection rather than any btf_struct_access().
>
> Should the commit message say check_ptr_to_btf_access() instead?
Right. It should be check_ptr_to_btf_access.
>
>
> >
> > Drop the early rejection and keep check_reg_type() limited to register
> > admission.
> >
> > Signed-off-by: Amery Hung <ameryhung@gmail.com>
>
> This looks like a bug fix for commit fa75b7c85b0d. The commit message
> explicitly references commit fa75b7c85b0d which introduced the validation
> flow change that made the check_reg_type() logic become redundant and
> overly conservative. After fa75b7c85b0d changed check_helper_mem_access()
> to forward the requested access_type to check_ptr_to_btf_access(), the
> early rejection in check_reg_type() for mem_types without MEM_RDONLY
> became incorrect, blocking valid use cases for map keys and input map
> values.
>
> Should this include:
>
> Fixes: fa75b7c85b0d ("bpf: Enforce write checks for BTF pointer helper access")
>
It's not a bug and does not need a fix tag.
>
> ---
> AI reviewed your patch. Please fix the bug or email reply why it's not a bug.
> See: https://github.com/kernel-patches/vmtest/blob/master/ci/claude/README.md
>
> CI run summary: https://github.com/kernel-patches/bpf/actions/runs/33995442755
^ permalink raw reply [flat|nested] 54+ messages in thread
* Re: [PATCH bpf-next v1 18/22] bpf: Admit kfunc argument registers through check_reg_type()
2026-09-05 23:23 ` bot+bpf-ci
@ 2026-09-10 16:18 ` Amery Hung
0 siblings, 0 replies; 54+ messages in thread
From: Amery Hung @ 2026-09-10 16:18 UTC (permalink / raw)
To: bot+bpf-ci
Cc: bpf, alexei.starovoitov, andrii, daniel, eddyz87, memxor,
kernel-team, ast, martin.lau, yonghong.song, mason, ihor.solodrai
On Sat, Sep 5, 2026 at 4:23 PM <bot+bpf-ci@kernel.org> wrote:
>
> > diff --git a/kernel/bpf/verifier.c b/kernel/bpf/verifier.c
> > index bd3fea62aa02..cad49ea5fdfa 100644
> > --- a/kernel/bpf/verifier.c
> > +++ b/kernel/bpf/verifier.c
>
> [ ... ]
>
> > @@ -8251,7 +8239,7 @@ static const struct bpf_reg_types mem_types = {
> > },
> > };
> >
> > -static const struct bpf_reg_types spin_lock_types = {
> > +static const struct bpf_reg_types map_value_or_alloc_obj_types = {
> > .types = {
> > PTR_TO_MAP_VALUE,
> > PTR_TO_BTF_ID | MEM_ALLOC,
>
> [ ... ]
>
> > @@ -8280,7 +8268,29 @@ static const struct bpf_reg_types percpu_btf_ptr_types = {
> > static const struct bpf_reg_types func_ptr_types = { .types = { PTR_TO_FUNC } };
> > static const struct bpf_reg_types stack_ptr_types = { .types = { PTR_TO_STACK } };
> > static const struct bpf_reg_types const_str_ptr_types = { .types = { PTR_TO_MAP_VALUE } };
> > -static const struct bpf_reg_types timer_types = { .types = { PTR_TO_MAP_VALUE } };
> > +static const struct bpf_reg_types map_value_types = { .types = { PTR_TO_MAP_VALUE } };
> > +static const struct bpf_reg_types arena_types = {
> > + .types = {
> > + PTR_TO_ARENA,
> > + SCALAR_VALUE,
> > + }
> > +};
> > +
> > +static const struct bpf_reg_types alloc_obj_types = {
> > + .types = {
> > + PTR_TO_BTF_ID | MEM_ALLOC,
> > + PTR_TO_BTF_ID | MEM_ALLOC | MEM_PERCPU,
> > + }
> > +};
> > +
> > +static const struct bpf_reg_types alloc_obj_or_non_own_ref_types = {
> > + .types = {
> > + PTR_TO_BTF_ID | MEM_ALLOC,
> > + PTR_TO_BTF_ID | MEM_ALLOC | NON_OWN_REF,
> > + PTR_TO_BTF_ID | MEM_ALLOC | NON_OWN_REF | MEM_RCU,
> > + }
> > +};
>
> Does alloc_obj_or_non_own_ref_types need a diagnostic improvement?
> reg_type_str() has no textual prefix for MEM_ALLOC or NON_OWN_REF and
> renders PTR_TO_BTF_ID as the bare stem "ptr_". The first two entries both
> render as "ptr_" and the third as "rcu_ptr_", giving users the
> non-actionable list "ptr_, ptr_, rcu_ptr_". The selftest updates confirm
> this is what users see:
>
> rbtree_fail.c: __msg("R2 type=scalar expected=ptr_, ptr_, rcu_ptr_")
> refcounted_kptr_fail.c: __msg("R1 type=ctx expected=ptr_, ptr_, rcu_ptr_")
>
> These replace specific messages like "R1 is neither owning or non-owning
> ref". The changelog claims "The reason continues to report the actual
> register type and all accepted register types", which is literally true but
> not useful here: three distinct ownership states collapse to two
> indistinguishable strings, and "ptr_" is a dangling stem. Either
> reg_type_str() needs prefixes for MEM_ALLOC/NON_OWN_REF, or
> ARG_PTR_TO_REFCOUNTED_KPTR / ARG_PTR_TO_LIST_NODE / ARG_PTR_TO_RB_NODE
> should keep an ownership-specific message.
>
> > @@ -8310,16 +8320,32 @@ static const struct bpf_reg_types *compatible_reg_types[__BPF_ARG_TYPE_MAX] = {
> > #endif
> > [ARG_PTR_TO_SOCKET] = &fullsock_types,
> > [ARG_PTR_TO_BTF_ID] = &btf_ptr_types,
> > - [ARG_PTR_TO_SPIN_LOCK] = &spin_lock_types,
> > + [ARG_PTR_TO_SPIN_LOCK] = &map_value_or_alloc_obj_types,
> > [ARG_PTR_TO_MEM] = &mem_types,
> > [ARG_PTR_TO_RINGBUF_MEM] = &ringbuf_mem_types,
> > [ARG_PTR_TO_PERCPU_BTF_ID] = &percpu_btf_ptr_types,
> > [ARG_PTR_TO_FUNC] = &func_ptr_types,
> > [ARG_PTR_TO_STACK] = &stack_ptr_types,
> > [ARG_PTR_TO_CONST_STR] = &const_str_ptr_types,
> > - [ARG_PTR_TO_TIMER] = &timer_types,
> > + [ARG_PTR_TO_TIMER] = &map_value_types,
> > [ARG_KPTR_XCHG_DEST] = &kptr_xchg_dest_types,
> > [ARG_PTR_TO_DYNPTR] = &dynptr_types,
> > + /* Argument kinds only a kfunc can have. */
> > + [ARG_SCALAR] = &scalar_types,
> > + [ARG_CONST_SCALAR] = &scalar_types,
> > + [ARG_CONST_MEM_SIZE] = &scalar_types,
> > + [ARG_PTR_TO_ALLOC_BTF_ID] = &alloc_obj_types,
> > + [ARG_PTR_TO_REFCOUNTED_KPTR] = &alloc_obj_or_non_own_ref_types,
> > + [ARG_PTR_TO_ITER] = &stack_ptr_types,
> > + [ARG_PTR_TO_LIST_HEAD] = &map_value_or_alloc_obj_types,
> > + [ARG_PTR_TO_LIST_NODE] = &alloc_obj_or_non_own_ref_types,
> > + [ARG_PTR_TO_RB_ROOT] = &map_value_or_alloc_obj_types,
> > + [ARG_PTR_TO_RB_NODE] = &alloc_obj_or_non_own_ref_types,
> > + [ARG_PTR_TO_RES_SPIN_LOCK] = &map_value_or_alloc_obj_types,
> > + [ARG_PTR_TO_WORKQUEUE] = &map_value_types,
> > + [ARG_PTR_TO_TASK_WORK] = &map_value_types,
> > + [ARG_PTR_TO_IRQ_FLAG] = &stack_ptr_types,
> > + [ARG_PTR_TO_ARENA] = &arena_types,
> > };
>
> Does ARG_PTR_TO_MEM need to admit allocated objects for kfuncs? Running
This seems to be a real regression. Since helper currently does not
allow it (wether relaxing it or not can be defer), I will add a
shortcut in check_reg_type to allow this instead of adding
PTR_TO_BTF_ID | MEM_ALLOC to mem_types.
> check_reg_type() on kfunc arguments makes mem_types the admission gate for
> ARG_PTR_TO_MEM, but mem_types lists only PTR_TO_BTF_ID | PTR_TRUSTED among
> the BTF-backed register types. Before this commit check_kfunc_args() had no
> reg-type test in its ARG_PTR_TO_MEM / ARG_MEM_SIZE cases (the commit
> removes tests from ten other cases but adds none here), so a kfunc memory
> argument was validated only by check_mem_reg()/check_mem_size_reg() ->
> check_helper_mem_access(), which accepts any BTF-ID register:
>
> case PTR_TO_BTF_ID:
> return check_ptr_to_btf_access(env, regs, reg, argno, 0,
> access_size, access_type, -1);
> /* kernel/bpf/verifier.c:7097-7099, switch is on base_type(reg->type) */
>
> Kfunc memory arguments are checked with BPF_READ | BPF_WRITE
> (verifier.c:13243 and 13289), and check_ptr_to_btf_access() has an explicit
> carve-out that permits exactly those writes for BPF-allocated objects:
>
> /* Writes are permitted with default btf_struct_access for
> * program allocated objects (which always have id > 0).
> */
> if (atype != BPF_READ && !type_is_ptr_alloc_obj(reg->type)) {
> verbose(env, "only read is supported\n");
> return -EACCES;
> }
> /* kernel/bpf/verifier.c:6093-6099 */
>
> check_func_arg_reg_off() likewise enumerates PTR_TO_BTF_ID | MEM_ALLOC and
> PTR_TO_BTF_ID | MEM_ALLOC | NON_OWN_REF as register types that legitimately
> reach argument checking with a fixed offset (verifier.c:8567-8578). So a
> program that does
>
> struct foo *f = bpf_obj_new(typeof(*f));
> ...
> some_kfunc(&f->buf, sizeof(f->buf)); /* void *p, u32 p__sz */
>
> or passes such a pointer to a fixed-size scalar-pointer kfunc argument
> (get_kfunc_arg_type() verifier.c:12321, ARG_PTR_TO_MEM | MEM_FIXED_SIZE)
> loaded before this commit and is now rejected at check_reg_type() with
> -EACCES: "R1 type=ptr_ expected=fp, pkt, pkt_meta, map_key, map_value, mem,
> ringbuf_mem, buf, trusted_ptr_, ctx".
>
> Since mem_types already excluded PTR_TO_BTF_ID | MEM_ALLOC for helpers, the
> write-to-allocated-object carve-out in check_ptr_to_btf_access() was
> reachable from the argument path only through kfuncs; after this commit it
> is unreachable from any argument path.
>
> This is a third, undocumented behaviour change. The changelog states "Two
> behavior changes fall out of running admission first" and lists only the
> ARG_CONST_MEM_SIZE precision fix and the ARG_CONST_MAP_PTR
> type_may_be_null() removal. No selftest covers passing an allocated object
> as a kfunc memory argument, so the capability is removed silently. Forward
> search over the supplied range 8f65640265e8..a490f49b0c4e shows no change
> to mem_types. If the narrowing is intended it should be stated in the
> changelog (and the now-dead carve-out comment in check_ptr_to_btf_access()
> updated); otherwise mem_types needs PTR_TO_BTF_ID | MEM_ALLOC (and the
> NON_OWN_REF form).
>
> [ ... ]
>
> > @@ -13002,25 +13015,20 @@ static int check_kfunc_args(struct bpf_verifier_env *env, struct bpf_call_arg_me
> > if (base_type(arg_type) == ARG_PTR_TO_BTF_ID)
> > ref_id = *meta->fn->arg_btf_id[i];
> >
> > - ret = resolve_func_arg_type(env, reg, i, meta, insn_idx, &arg_type, &arg_size);
> > + ret = resolve_func_arg_type(env, reg, i, meta, &arg_type, &arg_size);
> > if (ret < 0)
> > return ret;
> > +
> > + ret = check_reg_type(env, reg, argno, arg_type, meta);
> > + if (ret < 0)
> > + return ret;
> > +
> > ret = check_func_arg_reg_off(env, reg, argno, arg_type);
> > if (ret < 0)
> > return ret;
> >
> > switch (base_type(arg_type)) {
> > case ARG_CONST_SCALAR:
> > - if (reg->type != SCALAR_VALUE) {
> > - verbose(env, "%s is not a scalar\n", reg_arg_name(env, argno));
> > - bpf_diag_call_arg_fmt(env, insn_idx, argno, func_name,
> > - "Pass an integer scalar value for this argument, not a pointer or resource object.",
> > - "the kfunc expects an integer scalar, but %s is %s",
> > - reg_arg_name(env, argno),
> > - bpf_diag_reg_type_plain(env, reg->type));
> > - return -EINVAL;
> > - }
> > -
> > ret = process_const_arg(env, reg, argno, meta);
> > if (ret < 0) {
> > if (ret == -EINVAL)
>
> [ ... ]
>
> > @@ -11845,24 +11874,8 @@ static int resolve_func_arg_type(struct bpf_verifier_env *env,
> > ref_t = btf_type_skip_modifiers(btf, ref_id, &ref_id);
> > ref_tname = btf_name_by_offset(btf, ref_t->name_off);
> >
> > - if (!btf_type_is_scalar_struct(env, btf, ref_t)) {
> > - enum bpf_reg_type reg2btf_type = lookup_reg2btf_ids(ref_id);
> > - const char *expected_type;
> > -
> > - verbose(env, "%s is %s expected %s %s",
> > - reg_arg_name(env, argno), reg_type_str(env, reg->type),
> > - btf_type_str(ref_t), ref_tname);
> > - if (reg2btf_type != NOT_INIT)
> > - verbose(env, " or %s", reg_type_str(env, reg2btf_type));
> > - verbose(env, "\n");
> > - expected_type = bpf_diag_fmt_btf_type(env, btf, ref_id);
> > - bpf_diag_call_arg_fmt(env, insn_idx, argno, meta->func_name,
> > - "Pass a verifier-tracked pointer to the expected kernel object type, not a pointer to stack storage or another memory buffer.",
> > - "the function expects a pointer to %s, but this argument is %s and cannot be used as that kernel object pointer",
> > - expected_type,
> > - bpf_diag_reg_type_plain(env, reg->type));
> > - return -EINVAL;
> > - }
> > + if (!btf_type_is_scalar_struct(env, btf, ref_t))
> > + return 0;
>
> Does this break existing selftests? The patch deletes the only producer of
> the "R1 is fp expected STRUCT prog_test_fail1" diagnostic from
> resolve_func_arg_type(), replacing it with a bare `return 0;`. It updates
> verifier/calls.c line 79 but leaves three other cases in the same file
> still asserting the removed message:
>
> calls.c:34 .errstr = "R1 is fp expected STRUCT prog_test_fail1"
> calls.c:49 .errstr = "max struct nesting depth exceeded\nR1 is fp
> expected STRUCT prog_test_fail2"
> calls.c:64 .errstr = "R1 is fp expected STRUCT prog_test_fail3"
>
> All three are reached: bpf_kfunc_call_test_fail1/2/3 take pointers to
> struct prog_test_fail1 (has pointer member), prog_test_fail2 (nesting depth
> > 4) and prog_test_fail3 (flexible array), so btf_type_is_scalar_struct()
> returns false for each and the removed verbose() used to fire. After the
> patch, resolve_func_arg_type() returns 0 with arg_type still
> ARG_PTR_TO_BTF_ID; check_reg_type() then rejects R1 (PTR_TO_STACK) against
> btf_ptr_types and prints "R1 type=fp expected=ptr_, trusted_ptr_, rcu_ptr_"
> instead. The asserted substrings never appear, so these three test_verifier
> cases fail.
>
> These three sites are not fixed anywhere later in the series either: `git
> show a490f49b0c4e:tools/testing/selftests/bpf/verifier/calls.c` still
> contains all three stale strings, and no commit in 8f65640265e8..a490f49b0c4e
> touches verifier/calls.c.
>
> Do the test expectations in other files also need updating? Three more BPF
> selftests assert the "is fp expected STRUCT ..." message that this commit
> removes from resolve_func_arg_type():
>
> tools/testing/selftests/bpf/progs/cgrp_kfunc_failure.c:67
> __msg("R1 is fp expected STRUCT cgroup")
> tools/testing/selftests/bpf/progs/task_kfunc_failure.c:53
> __msg("R1 is fp expected STRUCT task_struct")
> tools/testing/selftests/bpf/progs/verifier_vfs_reject.c:31
> __msg("R1 is fp expected STRUCT task_struct")
>
> struct cgroup and struct task_struct are not scalar structs, so each of
> these used to hit the deleted verbose(); after the patch check_reg_type()
> emits "R1 type=fp expected=ptr_, trusted_ptr_, rcu_ptr_" and the assertions
> fail.
>
> This is confirmed by the series itself: commit a490f49b0c4e ("bpf: Check
> helper and kfunc arguments in one path"), four commits later, updates
> exactly these three lines to __msg("R1 type=fp expected=ptr_, trusted_ptr_,
> rcu_ptr_"). Those test-expectation updates belong in this commit, which is
> the one that changes the message; as it stands the tree is bisect-broken
> for 4 commits (test_progs cgrp_kfunc/task_kfunc/verifier_vfs_reject).
Will fix the test error message expectations.
>
> Does this lose test coverage for the ownership check?
> rbtree_fail.c:182 and rbtree_fail.c:206 were the only two selftests
> asserting the verifier check at kernel/bpf/verifier.c:13183:
>
> verbose(env, "%s can only take non-owning or refcounted bpf_rb_node
> pointer\n", func_name);
>
> Both tests invalidate a non-owning ref across bpf_spin_unlock() (the
> register becomes SCALAR_VALUE) and then call bpf_rbtree_remove(). With
> check_reg_type() now running before the switch, SCALAR_VALUE is rejected by
> ARG_PTR_TO_RB_NODE's alloc_obj_or_non_own_ref_types table first, so the
> ownership check is no longer reached and the new expectations only assert
> the generic register-class error. `grep -r "can only take non-owning or
> refcounted" tools/testing/selftests/bpf/` now returns nothing: the check
> has zero test coverage after this patch.
>
> Worth double-checking whether that check is still reachable at all. The
> only register types that can now reach it are PTR_TO_BTF_ID|MEM_ALLOC,
> |NON_OWN_REF and |NON_OWN_REF|MEM_RCU; the NON_OWN_REF forms satisfy
> type_is_non_owning_ref(), and a plain owning MEM_ALLOC register whose
> reference has been released is invalidated by
> release_reference()/mark_reg_invalid(). If no reachable register type
> remains, the check is dead and should be dropped rather than left
> uncovered; if it is reachable, the two repurposed tests should be kept (or
> a replacement added) so the ownership rule stays guarded.
The check is indeed unreachable.
I prefer keep it to be safe. The listed compatible types happen to
match what !type_is_non_owning_ref() && reg_is_referenced() gates. It
requires that a plain MEM_ALLOC pointer must carry a live reference.
>
>
> ---
> AI reviewed your patch. Please fix the bug or email reply why it's not a bug.
> See: https://github.com/kernel-patches/vmtest/blob/master/ci/claude/README.md
>
> CI run summary: https://github.com/kernel-patches/bpf/actions/runs/33995442755
^ permalink raw reply [flat|nested] 54+ messages in thread
* Re: [PATCH bpf-next v1 14/22] bpf: Consolidate runtime argument type resolution
2026-09-05 22:01 ` [PATCH bpf-next v1 14/22] bpf: Consolidate runtime argument type resolution Amery Hung
@ 2026-09-10 21:52 ` Alexei Starovoitov
2026-09-11 21:01 ` Amery Hung
0 siblings, 1 reply; 54+ messages in thread
From: Alexei Starovoitov @ 2026-09-10 21:52 UTC (permalink / raw)
To: Amery Hung, bpf; +Cc: andrii, daniel, eddyz87, memxor, kernel-team
On Sat Sep 5, 2026 at 3:01 PM PDT, Amery Hung wrote:
> A function argument type can require call-site refinement before
> register admission. Helpers refine map-value arguments according to the
> recorded map type, while kfuncs reinterpret a pointer to a scalar-only
> BTF struct as fixed-size memory when the register does not carry a
> BTF ID.
>
> Add resolve_func_arg_type() as the common entry point. It delegates
> helper map-value refinement to the existing resolve_map_arg_type(),
> then handles the kfunc BTF-to-memory fallback. It updates the effective
> argument type and size consumed by the subsequent checks.
>
> Preserve PTR_MAYBE_NULL when changing the base type so later
> nullability validation observes the BTF contract. Keep OBJ_RELEASE
> arguments in their original pointer class. The latter is not a bug fix:
> existing kfunc checking already validates ownership before this
> resolver. It preserves release metadata for the later shared checking
> order, where ownership validation follows register admission.
>
> Moving the kfunc fallback before admission lets the resulting
> fixed-size memory argument enter the compatibility check directly.
> This is another step toward routing both call types through
> check_func_arg().
>
> Signed-off-by: Amery Hung <ameryhung@gmail.com>
> ---
> kernel/bpf/verifier.c | 199 +++++++++++++++++++++++-------------------
> 1 file changed, 110 insertions(+), 89 deletions(-)
>
> diff --git a/kernel/bpf/verifier.c b/kernel/bpf/verifier.c
> index 2e068c095436..15ed47b90316 100644
> --- a/kernel/bpf/verifier.c
> +++ b/kernel/bpf/verifier.c
> @@ -8151,8 +8151,8 @@ static bool is_kfunc_call(const struct bpf_call_arg_meta *meta, u32 btf_id)
> }
>
> static int resolve_map_arg_type(struct bpf_verifier_env *env,
> - const struct bpf_call_arg_meta *meta,
> - enum bpf_arg_type *arg_type)
> + const struct bpf_call_arg_meta *meta,
> + enum bpf_arg_type *arg_type)
> {
> if (!meta->map.ptr) {
> /* kernel subsystem misconfigured verifier */
> @@ -8180,6 +8180,11 @@ static int resolve_map_arg_type(struct bpf_verifier_env *env,
> return 0;
> }
>
> +static int resolve_func_arg_type(struct bpf_verifier_env *env,
> + struct bpf_reg_state *reg, u32 arg,
> + struct bpf_call_arg_meta *meta, int insn_idx,
> + enum bpf_arg_type *arg_type, u32 *arg_size);
> +
> struct bpf_reg_types {
> const enum bpf_reg_type types[10];
> u32 *btf_id;
> @@ -8794,6 +8799,7 @@ static int check_func_arg(struct bpf_verifier_env *env, u32 arg,
> int regno = reg_from_argno(argno);
> enum bpf_reg_type type = reg->type;
> const u32 *arg_btf_id = NULL;
> + u32 arg_size = arg_type & MEM_FIXED_SIZE ? fn->arg_size[arg] : 0;
> u32 key_size;
> int err = 0;
>
> @@ -8823,11 +8829,9 @@ static int check_func_arg(struct bpf_verifier_env *env, u32 arg,
> return -EACCES;
> }
>
> - if (base_type(arg_type) == ARG_PTR_TO_MAP_VALUE) {
> - err = resolve_map_arg_type(env, meta, &arg_type);
> - if (err)
> - return err;
> - }
> + err = resolve_func_arg_type(env, reg, arg, meta, insn_idx, &arg_type, &arg_size);
> + if (err)
> + return err;
>
> if (bpf_register_is_null(reg) && type_may_be_null(arg_type))
> /* A NULL register has a SCALAR_VALUE type, so skip
> @@ -8965,12 +8969,12 @@ static int check_func_arg(struct bpf_verifier_env *env, u32 arg,
> * next is_mem_size argument below.
> */
> if (arg_type & MEM_FIXED_SIZE) {
> - err = check_mem_reg(env, reg, argno, fn->arg_size[arg],
> + err = check_mem_reg(env, reg, argno, arg_size,
> arg_type & MEM_WRITE ? BPF_WRITE : BPF_READ, meta, NULL);
> if (err)
> return err;
> if (arg_type & MEM_ALIGNED)
> - err = check_ptr_alignment(env, reg, 0, fn->arg_size[arg], true);
> + err = check_ptr_alignment(env, reg, 0, arg_size, true);
> }
> break;
> case ARG_MEM_SIZE:
> @@ -11814,6 +11818,65 @@ static bool btf_type_is_scalar_struct(struct bpf_verifier_env *env,
> return btf_struct_is_composed_of(env, btf, t, BTF_MEMBER_SCALAR);
> }
>
> +static int resolve_func_arg_type(struct bpf_verifier_env *env,
> + struct bpf_reg_state *reg, u32 arg,
> + struct bpf_call_arg_meta *meta, int insn_idx,
> + enum bpf_arg_type *arg_type, u32 *arg_size)
> +{
> + argno_t argno = argno_from_arg(arg + 1);
> + const struct btf_param *args;
> + const struct btf_type *ref_t, *resolve_ret;
> + const struct btf *btf;
> + const char *ref_tname;
> + u32 ref_id;
> +
> + if (base_type(*arg_type) == ARG_PTR_TO_MAP_VALUE)
> + return resolve_map_arg_type(env, meta, arg_type);
> +
> + if (base_type(*arg_type) == ARG_PTR_TO_BTF_ID) {
Overall, it all looks great to me.
Here can reduce indent by:
if (base_type(*arg_type) != ARG_PTR_TO_BTF_ID)
return 0;
Pls address AI nits and respin, so we can land asap.
^ permalink raw reply [flat|nested] 54+ messages in thread
* Re: [PATCH bpf-next v1 22/22] bpf: Check helper and kfunc arguments in one path
2026-09-05 22:01 ` [PATCH bpf-next v1 22/22] bpf: Check helper and kfunc arguments in one path Amery Hung
2026-09-05 22:33 ` sashiko-bot
@ 2026-09-10 21:53 ` Alexei Starovoitov
2026-09-11 20:55 ` Amery Hung
1 sibling, 1 reply; 54+ messages in thread
From: Alexei Starovoitov @ 2026-09-10 21:53 UTC (permalink / raw)
To: Amery Hung, bpf; +Cc: andrii, daniel, eddyz87, memxor, kernel-team
On Sat Sep 5, 2026 at 3:01 PM PDT, Amery Hung wrote:
> + err = check_mem_size_reg(env, buff_reg, reg, buff_argno, argno,
> + access_type, zero_size_allowed, meta, &failure);
> + if (err < 0) {
can reduce indent here as well by
if (!err)
break;
> + const char *buff_arg, *size_arg;
> +
> + buff_arg = bpf_diag_arg_name(env, buff_argno);
> + size_arg = bpf_diag_arg_name(env, argno);
> + verbose(env, "%s and ", reg_arg_name(env, buff_argno));
> + verbose(env, "%s memory, len pair leads to invalid memory access\n",
^ permalink raw reply [flat|nested] 54+ messages in thread
* Re: [PATCH bpf-next v1 19/22] selftests/bpf: Test kfunc packet memory direct writes
2026-09-05 22:44 ` bot+bpf-ci
@ 2026-09-11 20:46 ` Amery Hung
0 siblings, 0 replies; 54+ messages in thread
From: Amery Hung @ 2026-09-11 20:46 UTC (permalink / raw)
To: bot+bpf-ci
Cc: bpf, alexei.starovoitov, andrii, daniel, eddyz87, memxor,
kernel-team, ast, martin.lau, yonghong.song, mason, ihor.solodrai
On Sat, Sep 5, 2026 at 3:44 PM <bot+bpf-ci@kernel.org> wrote:
>
> > diff --git a/tools/testing/selftests/bpf/progs/verifier_kfunc_packet_access.c b/tools/testing/selftests/bpf/progs/verifier_kfunc_packet_access.c
> > new file mode 100644
> > index 000000000000..9ae63c9eab02
> > --- /dev/null
> > +++ b/tools/testing/selftests/bpf/progs/verifier_kfunc_packet_access.c
> > @@ -0,0 +1,47 @@
> > +// SPDX-License-Identifier: GPL-2.0
> > +
> > +#include <vmlinux.h>
> > +#include <bpf/bpf_helpers.h>
> > +#include "bpf_misc.h"
> > +
> > +struct nf_conn *bpf_skb_ct_lookup(struct __sk_buff *skb_ctx,
> > + struct bpf_sock_tuple *bpf_tuple,
> > + u32 tuple__sz, struct bpf_ct_opts *opts,
> > + u32 opts__sz) __ksym;
> > +void bpf_ct_release(struct nf_conn *nfct) __ksym;
> > +
> > +char _license[] SEC("license") = "GPL";
> > +
> > +SEC("tc")
> > +__description("kfunc packet write requests writable skb")
> > +__success
> > +/* bpf_unclone_prologue() */
> > +__xlated("r6 = *(u8 *)(r1 +{{[0-9]+}})")
> > +__xlated("...")
> > +__xlated("w6 &= {{1|128}}")
>
> Is this test expected to pass at this commit? Looking at the verifier
> code at this commit, the kfunc memory-argument path goes through
> check_helper_mem_access() for PTR_TO_PACKET, which calls
> check_packet_access() directly:
>
> kernel/bpf/verifier.c:check_helper_mem_access():
> case PTR_TO_PACKET:
> case PTR_TO_PACKET_META:
> return check_packet_access(env, reg, argno, 0, access_size,
> zero_size_allowed);
>
> This path never calls may_access_direct_pkt_data(), so
> env->seen_direct_write remains false. Without seen_direct_write set,
> net/core/filter.c:tc_cls_act_prologue() -> bpf_unclone_prologue()
> returns 0:
>
> net/core/filter.c:bpf_unclone_prologue():
> if (!direct_write)
> return 0;
>
> The kfunc_packet_write() program performs no direct packet store, so
> it won't trigger the may_access_direct_pkt_data() call that would set
> env->seen_direct_write. This means no prologue instructions are emitted
> and the __xlated() patterns from here to the end would fail to match.
>
> This appears to be fixed by a later commit in the series that moves the
> check into check_helper_mem_access(). Should this test be reordered to
> appear after that commit to avoid breaking bisection?
Yes. They should be reordered. I forgot to swap them back after testing.
>
> > +__xlated("...")
> > +__xlated("if r6 == 0x0 goto")
> > +__xlated("r6 = r1")
> > +__xlated("r2 ^= r2")
> > +__xlated("call")
> > +__xlated("if r0 == 0x0 goto")
> > +__xlated("w0 = 2")
> > +__xlated("...")
> > +__xlated("exit")
> > +__xlated("r1 = r6")
> > +int kfunc_packet_write(struct __sk_buff *skb)
> > +{
> > + void *data_end = (void *)(long)skb->data_end;
> > + void *data = (void *)(long)skb->data;
> > + struct bpf_sock_tuple tuple = {};
> > + struct nf_conn *nfct;
> > +
> > + if (data + sizeof(struct bpf_ct_opts) > data_end)
> > + return 0;
> > +
> > + /* An invalid tuple size makes bpf_skb_ct_lookup() write opts->error. */
> > + nfct = bpf_skb_ct_lookup(skb, &tuple, 1, data, sizeof(struct bpf_ct_opts));
> > + if (nfct)
> > + bpf_ct_release(nfct);
> > + return 0;
> > +}
>
> Going back to the alternation pattern above:
>
> > +__xlated("w6 &= {{1|128}}")
>
> This isn't a bug, but does the regex match what the commit message
> describes? The pattern text "w6 &= {{1|128}}" compiles to the POSIX ERE
> "w6 &= 1|128" since tools/testing/selftests/bpf/test_loader.c only
> escapes metacharacters outside {{ }}. Without grouping, this is
> (w6 &= 1)|(128) due to '|' having lowest precedence, so the second
> alternative matches a bare "128" anywhere in the xlated dump rather than
> constraining the cloned-bit mask value. Should this be "w6 &= {{(1|128)}}"
> to properly match the endian-specific mask?
This did cause selftest to fail. Will fix.
>
>
> ---
> AI reviewed your patch. Please fix the bug or email reply why it's not a bug.
> See: https://github.com/kernel-patches/vmtest/blob/master/ci/claude/README.md
>
> CI run summary: https://github.com/kernel-patches/bpf/actions/runs/33995442755
^ permalink raw reply [flat|nested] 54+ messages in thread
* Re: [PATCH bpf-next v1 21/22] bpf: Consolidate release argument validation
2026-09-05 23:08 ` bot+bpf-ci
@ 2026-09-11 20:47 ` Amery Hung
0 siblings, 0 replies; 54+ messages in thread
From: Amery Hung @ 2026-09-11 20:47 UTC (permalink / raw)
To: bot+bpf-ci
Cc: bpf, alexei.starovoitov, andrii, daniel, eddyz87, memxor,
kernel-team, ast, martin.lau, yonghong.song, mason, ihor.solodrai
On Sat, Sep 5, 2026 at 4:08 PM <bot+bpf-ci@kernel.org> wrote:
>
> > diff --git a/kernel/bpf/verifier.c b/kernel/bpf/verifier.c
> > index 487ad1ee4179..6c5d7e544f84 100644
> > --- a/kernel/bpf/verifier.c
> > +++ b/kernel/bpf/verifier.c
>
> [ ... ]
>
> > @@ -8825,29 +8862,9 @@ static int check_func_arg(struct bpf_verifier_env *env, u32 arg,
> > return err;
> >
> > skip_type_check:
> > - if (arg_type_is_release(arg_type)) {
> > - if (type_may_be_null(reg->type)) {
> > - verbose(env, "Possibly NULL pointer passed to trusted %s\n",
> > - reg_arg_name(env, argno));
> > - bpf_diag_call_arg(
> > - env, insn_idx, argno, meta->func_name,
> > - "the pointer may be NULL, but this call requires a non-NULL pointer",
> > - "Add a NULL check and make the call only on the non-NULL path.");
> > - return -EACCES;
> > - }
> > -
> > - if (!arg_type_is_dynptr(arg_type) &&
> > - !reg_is_referenced(env, reg) && !bpf_register_is_null(reg)) {
> > - verbose(env,
> > - "release helper %s expects referenced PTR_TO_BTF_ID passed to %s\n",
> > - meta->func_name, reg_arg_name(env, argno));
> > - bpf_diag_call_arg(
> > - env, insn_idx, argno, meta->func_name,
> > - "release helpers require a value that owns a live resource returned by a matching acquire helper",
> > - "Pass the resource-owning pointer returned by the matching acquire helper, and avoid calling the release helper after ownership has already been transferred or released.");
> > - return -EINVAL;
> > - }
> > - }
> > + err = check_func_arg_release(env, reg, argno, arg_type, meta, insn_idx);
> > + if (err)
> > + return err;
>
> Does removing the type_may_be_null() check here break the percpu kptr
> RCU conversion path?
>
> The deleted block rejected maybe-null registers passed to OBJ_RELEASE
> helper arguments. Only bpf_kptr_xchg() arg2 has ARG_PTR_TO_BTR_ID_OR_NULL
> | OBJ_RELEASE, so it is the only argument whose admission changes.
>
> check_func_arg_release() only keeps the ownership half of the test:
>
> kernel/bpf/verifier.c:check_func_arg_release() {
> if (arg_type_is_dynptr(arg_type) || reg_is_referenced(env, reg) ||
> bpf_register_is_null(reg))
> return 0;
> }
>
> but for the percpu-kptr RCU conversion path, release_reg() assumes the
> released register is known non-NULL. In check_helper_call():
>
> bool convert_rcu = (func_id == BPF_FUNC_kptr_xchg) && in_rcu_cs(env) &&
> (reg->type & MEM_ALLOC) && (reg->type & MEM_PERCPU);
> err = release_reg(env, reg, convert_rcu, !!meta.dynptr.id);
>
> release_reg() then calls ref_convert_alloc_rcu_protected(), which for
> every register carrying the id does:
>
> reg->id = 0;
> reg->type &= ~MEM_ALLOC;
> reg->type |= MEM_RCU;
>
> PTR_MAYBE_NULL is deliberately not touched, and reg->id is zeroed. Before
> this patch that combination was unreachable because a maybe-null register
> could not be passed to bpf_kptr_xchg() at all. Now the verifier can
> produce PTR_TO_BTF_ID | MEM_PERCPU | MEM_RCU | PTR_MAYBE_NULL with
> reg->id == 0.
>
> That state breaks NULL-check identity matching. check_cond_jmp_op() calls
> mark_ptr_or_null_regs() for any JEQ/JNE-against-0 on a maybe-null
> register, and mark_ptr_or_null_regs() uses u32 id = regs[regno].id, i.e.
> 0. mark_ptr_or_null_reg() then matches on:
>
> if (type_may_be_null(reg->type) && reg->id == id &&
> (is_rcu_reg(reg) || !WARN_ON_ONCE(!reg->id))) {
>
> is_rcu_reg() is just "reg->type & MEM_RCU", so the id==0 sanity WARN is
> bypassed and every maybe-null MEM_RCU register with id 0 in the state is
> marked non-NULL by a NULL check on any one of them.
>
> Concrete accepted-but-unsafe program with percpu kptr fields pc1/pc2 in a
> map value:
>
> p1 = bpf_percpu_obj_new(struct val_t);
> /* MEM_ALLOC|MEM_PERCPU|PTR_MAYBE_NULL, id=N1 */
>
> p2 = bpf_percpu_obj_new(struct val_t);
> /* MEM_ALLOC|MEM_PERCPU|PTR_MAYBE_NULL, id=N2 */
>
> bpf_rcu_read_lock();
>
> old = bpf_kptr_xchg(&v->pc1, p1);
> /* p1 -> MEM_RCU|MEM_PERCPU|PTR_MAYBE_NULL, id=0 */
>
> old = bpf_kptr_xchg(&v->pc2, p2);
> /* p2 -> MEM_RCU|MEM_PERCPU|PTR_MAYBE_NULL, id=0 */
>
> if (p1) {
> /* clears PTR_MAYBE_NULL on p1 AND on p2 (both id 0, both MEM_RCU) */
>
> q = bpf_this_cpu_ptr(p2);
> /* R1 now looks non-NULL, so ARG_PTR_TO_PERCPU_BTF_ID is accepted */
>
> q->field = 1;
> /* q is PTR_TO_BTF_ID|MEM_ALLOC|MEM_RCU, non-NULL, writable */
> }
> bpf_rcu_read_unlock();
>
> If bpf_percpu_obj_new() for p2 returned NULL at runtime, the program
> stores NULL into the percpu kptr field and then executes
> this_cpu_ptr(NULL), which writes at NULL + __per_cpu_offset[cpu].
>
> This would be an unchecked write into the per-CPU area from a
> verifier-accepted program.
>
> Note that progs/map_kptr_fail.c:kptr_xchg_possibly_null was deliberately
> flipped from __failure to __success, so the relaxation is intentional,
> but the changelog does not address the percpu/RCU conversion path, and no
> test covers a nullable percpu source xchg'd inside an RCU critical
> section.
>
> Should the verifier keep rejecting type_may_be_null(reg->type) for
> OBJ_RELEASE helper arguments when the destination kptr field is
> BPF_KPTR_PERCPU, or should ref_convert_alloc_rcu_protected() leave a
> fresh non-zero reg->id so that maybe-null identity tracking stays intact?
>
> > diff --git a/tools/testing/selftests/bpf/prog_tests/cb_refs.c b/tools/testing/selftests/bpf/prog_tests/cb_refs.c
> > index 50ea3d72d446..8743f52e755d 100644
> > --- a/tools/testing/selftests/bpf/prog_tests/cb_refs.c
> > +++ b/tools/testing/selftests/bpf/prog_tests/cb_refs.c
> > @@ -11,8 +11,8 @@ struct {
> > const char *prog_name;
> > const char *err_msg;
> > } cb_refs_tests[] = {
> > - { "underflow_prog", "release kfunc bpf_kfunc_call_test_release expects referenced PTR_TO_BTF_ID passed to R1" },
> > - { "leak_prog", "Possibly NULL pointer passed to trusted R2" },
> > + { "underflow_prog", "release function bpf_kfunc_call_test_release expects referenced PTR_TO_BTF_ID passed to R1" },
> > + { "leak_prog", "Unreleased reference id=4 alloc_insn=33" },
>
> This isn't a bug, but could the leak_prog expectation be more robust?
>
> The new expectation hard-codes a full instruction index, "Unreleased
> reference id=4 alloc_insn=33", which pins the test to one compiler's
> instruction layout for the callback subprog cb2().
>
> Every neighbouring expectation in the same table deliberately truncates
> the digits and documents why:
>
> { "nested_cb", "Unreleased reference id=4 alloc_insn=2" },
> /* alloc_insn=2{4,5} */
>
> { "non_cb_transfer_ref", "Unreleased reference id=4 alloc_insn=1" },
> /* alloc_insn=1{1,2} */
>
> and the equivalent expectation added for the same verifier message in
> map_kptr_fail.c drops the number entirely:
>
> __failure __msg("Unreleased reference id=4 alloc_insn=")
>
> Matching in test_cb_refs() is strstr(log_buf, cb_refs_tests[i].err_msg),
> so a truncated prefix such as "Unreleased reference id=4 alloc_insn=3"
> matches equally well while tolerating a one- or two-instruction shift from
> a different clang version.
>
Test. It should be { "leak_prog", "Unreleased reference id=4
alloc_insn=3" }, /* alloc_insn=3{2,3} */
> As written, the entry breaks with any layout change in cb2(), and unlike
> the two neighbours it carries no comment recording the observed range.
>
>
> ---
> AI reviewed your patch. Please fix the bug or email reply why it's not a bug.
> See: https://github.com/kernel-patches/vmtest/blob/master/ci/claude/README.md
>
> CI run summary: https://github.com/kernel-patches/bpf/actions/runs/33995442755
^ permalink raw reply [flat|nested] 54+ messages in thread
* Re: [PATCH bpf-next v1 22/22] bpf: Check helper and kfunc arguments in one path
2026-09-10 21:53 ` Alexei Starovoitov
@ 2026-09-11 20:55 ` Amery Hung
0 siblings, 0 replies; 54+ messages in thread
From: Amery Hung @ 2026-09-11 20:55 UTC (permalink / raw)
To: Alexei Starovoitov; +Cc: bpf, andrii, daniel, eddyz87, memxor, kernel-team
On Thu, Sep 10, 2026 at 2:53 PM Alexei Starovoitov
<alexei.starovoitov@gmail.com> wrote:
>
> On Sat Sep 5, 2026 at 3:01 PM PDT, Amery Hung wrote:
> > + err = check_mem_size_reg(env, buff_reg, reg, buff_argno, argno,
> > + access_type, zero_size_allowed, meta, &failure);
> > + if (err < 0) {
>
>
> can reduce indent here as well by
> if (!err)
> break;
I will tweak the style to reduce the indent.
>
> > + const char *buff_arg, *size_arg;
> > +
> > + buff_arg = bpf_diag_arg_name(env, buff_argno);
> > + size_arg = bpf_diag_arg_name(env, argno);
> > + verbose(env, "%s and ", reg_arg_name(env, buff_argno));
> > + verbose(env, "%s memory, len pair leads to invalid memory access\n",
^ permalink raw reply [flat|nested] 54+ messages in thread
* Re: [PATCH bpf-next v1 22/22] bpf: Check helper and kfunc arguments in one path
2026-09-05 22:33 ` sashiko-bot
@ 2026-09-11 20:59 ` Amery Hung
0 siblings, 0 replies; 54+ messages in thread
From: Amery Hung @ 2026-09-11 20:59 UTC (permalink / raw)
To: sashiko-reviews; +Cc: bpf
On Sat, Sep 5, 2026 at 3:33 PM <sashiko-bot@kernel.org> wrote:
>
> Thank you for your contribution! Sashiko AI review found 1 potential issue(s) to consider:
> - [Critical] Out-of-bounds reads on `meta->fn->arg_type` and `meta->fn->arg_btf_id` for kfuncs with more than 5 arguments, allowing bypass of eBPF verifier safety checks.
> --
>
> commit a1895fafaa5219b7b972d20a932c550f7862cec8
> Author: Amery Hung <ameryhung@gmail.com>
>
> bpf: Check helper and kfunc arguments in one path
>
> This commit unifies the argument checking logic for BPF helpers and kfuncs
> by introducing check_func_args(). It moves kfunc-specific argument handling
> into the shared check_func_arg() path to avoid duplicating the argument
> iteration and switch logic.
>
> > diff --git a/kernel/bpf/verifier.c b/kernel/bpf/verifier.c
> > index 6c5d7e544f844..54656fb31bbe7 100644
> > --- a/kernel/bpf/verifier.c
> > +++ b/kernel/bpf/verifier.c
> [ ... ]
> > @@ -8808,6 +8840,7 @@ static int check_func_arg(struct bpf_verifier_env *env, u32 arg,
> > struct bpf_call_arg_meta *meta,
> > int insn_idx)
> > {
> > + const struct btf_param *btf_arg = meta->btf ? &btf_params(meta->func_proto)[arg] : NULL;
> > const struct bpf_func_proto *fn = meta->fn;
> > struct bpf_func_state *caller = cur_func(env);
> > struct bpf_reg_state *regs = cur_regs(env);
> > argno_t argno = argno_from_arg(arg + 1);
> > struct bpf_reg_state *reg = get_func_arg_reg(caller, regs, arg);
> > enum bpf_arg_type arg_type = fn->arg_type[arg];
>
> [Severity: Critical]
> Can this read out of bounds when processing kfuncs with more than 5
> arguments? The fn->arg_type array is sized for MAX_BPF_FUNC_ARGS (5), but
> kfuncs can have more arguments. For stack arguments (arg >= 5), this appears
> to read past the end of the array.
The review is a false positive. MAX_BPF_FUNC_ARGS is 12 not
MAX_BPF_FUNC_REG_ARGS, which is 5.
>
> [ ... ]
> > @@ -8935,6 +8980,7 @@ static int check_func_arg(struct bpf_verifier_env *env, u32 arg,
> > case ARG_PTR_TO_BTF_ID_SOCK_COMMON:
> > {
> > const u32 *arg_btf_id = fn->arg_btf_id[arg];
>
> [Severity: Critical]
> Does this also read out of bounds for kfuncs with more than 5 arguments?
> Similar to arg_type, the arg_btf_id array in struct bpf_func_proto is limited
> to MAX_BPF_FUNC_ARGS. If a garbage pointer is dereferenced here, could it
> crash the verifier?
Same false positive as the previous one.
>
> [ ... ]
> > @@ -9056,6 +9389,37 @@ static int check_func_arg(struct bpf_verifier_env *env, u32 arg,
> > return err;
> > }
> >
> > +static int check_func_args(struct bpf_verifier_env *env, struct bpf_call_arg_meta *meta,
> > + int insn_idx)
> > +{
> > + struct bpf_func_state *caller = cur_func(env);
> > + const struct btf_param *args = NULL;
> > + u32 arg, nargs = MAX_BPF_FUNC_REG_ARGS;
> > + int err;
> > +
> > + if (meta->btf) {
> > + args = btf_params(meta->func_proto);
> > + nargs = btf_type_vlen(meta->func_proto);
> > + }
> > +
> > + if (nargs > MAX_BPF_FUNC_REG_ARGS) {
> > + err = check_outgoing_stack_args(env, caller, nargs, meta->func_name,
> > + meta->btf, args);
> > + if (err)
> > + return err;
> > + }
> > +
> > + for (arg = 0; arg < nargs; arg++) {
> > + if (meta->fn->arg_type[arg] == ARG_UNUSED)
> > + break;
>
> [Severity: Critical]
> Could this loop early-exit and bypass verifier checks for stack arguments?
>
> Since nargs can exceed 5 for kfuncs, the out-of-bounds read on
> meta->fn->arg_type[arg] might evaluate to ARG_UNUSED (0). If this happens,
> the loop breaks and skips the safety checks for all remaining stack
> arguments, potentially allowing unverified memory access.
Same false positive here. No out-of-bound read, and there is also no
ARG_UNUSED holes in kfunc arguments.
>
> > + err = check_func_arg(env, arg, meta, insn_idx);
> > + if (err)
> > + return err;
> > + }
> > +
> > + return 0;
> > +}
> > +
>
> --
> Sashiko AI review · https://sashiko.dev/#/patchset/20260905220117.922028-1-ameryhung@gmail.com?part=22
^ permalink raw reply [flat|nested] 54+ messages in thread
* Re: [PATCH bpf-next v1 14/22] bpf: Consolidate runtime argument type resolution
2026-09-10 21:52 ` Alexei Starovoitov
@ 2026-09-11 21:01 ` Amery Hung
0 siblings, 0 replies; 54+ messages in thread
From: Amery Hung @ 2026-09-11 21:01 UTC (permalink / raw)
To: Alexei Starovoitov; +Cc: bpf, andrii, daniel, eddyz87, memxor, kernel-team
On Thu, Sep 10, 2026 at 2:52 PM Alexei Starovoitov
<alexei.starovoitov@gmail.com> wrote:
>
> On Sat Sep 5, 2026 at 3:01 PM PDT, Amery Hung wrote:
> > A function argument type can require call-site refinement before
> > register admission. Helpers refine map-value arguments according to the
> > recorded map type, while kfuncs reinterpret a pointer to a scalar-only
> > BTF struct as fixed-size memory when the register does not carry a
> > BTF ID.
> >
> > Add resolve_func_arg_type() as the common entry point. It delegates
> > helper map-value refinement to the existing resolve_map_arg_type(),
> > then handles the kfunc BTF-to-memory fallback. It updates the effective
> > argument type and size consumed by the subsequent checks.
> >
> > Preserve PTR_MAYBE_NULL when changing the base type so later
> > nullability validation observes the BTF contract. Keep OBJ_RELEASE
> > arguments in their original pointer class. The latter is not a bug fix:
> > existing kfunc checking already validates ownership before this
> > resolver. It preserves release metadata for the later shared checking
> > order, where ownership validation follows register admission.
> >
> > Moving the kfunc fallback before admission lets the resulting
> > fixed-size memory argument enter the compatibility check directly.
> > This is another step toward routing both call types through
> > check_func_arg().
> >
> > Signed-off-by: Amery Hung <ameryhung@gmail.com>
> > ---
> > kernel/bpf/verifier.c | 199 +++++++++++++++++++++++-------------------
> > 1 file changed, 110 insertions(+), 89 deletions(-)
> >
> > diff --git a/kernel/bpf/verifier.c b/kernel/bpf/verifier.c
> > index 2e068c095436..15ed47b90316 100644
> > --- a/kernel/bpf/verifier.c
> > +++ b/kernel/bpf/verifier.c
> > @@ -8151,8 +8151,8 @@ static bool is_kfunc_call(const struct bpf_call_arg_meta *meta, u32 btf_id)
> > }
> >
> > static int resolve_map_arg_type(struct bpf_verifier_env *env,
> > - const struct bpf_call_arg_meta *meta,
> > - enum bpf_arg_type *arg_type)
> > + const struct bpf_call_arg_meta *meta,
> > + enum bpf_arg_type *arg_type)
> > {
> > if (!meta->map.ptr) {
> > /* kernel subsystem misconfigured verifier */
> > @@ -8180,6 +8180,11 @@ static int resolve_map_arg_type(struct bpf_verifier_env *env,
> > return 0;
> > }
> >
> > +static int resolve_func_arg_type(struct bpf_verifier_env *env,
> > + struct bpf_reg_state *reg, u32 arg,
> > + struct bpf_call_arg_meta *meta, int insn_idx,
> > + enum bpf_arg_type *arg_type, u32 *arg_size);
> > +
> > struct bpf_reg_types {
> > const enum bpf_reg_type types[10];
> > u32 *btf_id;
> > @@ -8794,6 +8799,7 @@ static int check_func_arg(struct bpf_verifier_env *env, u32 arg,
> > int regno = reg_from_argno(argno);
> > enum bpf_reg_type type = reg->type;
> > const u32 *arg_btf_id = NULL;
> > + u32 arg_size = arg_type & MEM_FIXED_SIZE ? fn->arg_size[arg] : 0;
> > u32 key_size;
> > int err = 0;
> >
> > @@ -8823,11 +8829,9 @@ static int check_func_arg(struct bpf_verifier_env *env, u32 arg,
> > return -EACCES;
> > }
> >
> > - if (base_type(arg_type) == ARG_PTR_TO_MAP_VALUE) {
> > - err = resolve_map_arg_type(env, meta, &arg_type);
> > - if (err)
> > - return err;
> > - }
> > + err = resolve_func_arg_type(env, reg, arg, meta, insn_idx, &arg_type, &arg_size);
> > + if (err)
> > + return err;
> >
> > if (bpf_register_is_null(reg) && type_may_be_null(arg_type))
> > /* A NULL register has a SCALAR_VALUE type, so skip
> > @@ -8965,12 +8969,12 @@ static int check_func_arg(struct bpf_verifier_env *env, u32 arg,
> > * next is_mem_size argument below.
> > */
> > if (arg_type & MEM_FIXED_SIZE) {
> > - err = check_mem_reg(env, reg, argno, fn->arg_size[arg],
> > + err = check_mem_reg(env, reg, argno, arg_size,
> > arg_type & MEM_WRITE ? BPF_WRITE : BPF_READ, meta, NULL);
> > if (err)
> > return err;
> > if (arg_type & MEM_ALIGNED)
> > - err = check_ptr_alignment(env, reg, 0, fn->arg_size[arg], true);
> > + err = check_ptr_alignment(env, reg, 0, arg_size, true);
> > }
> > break;
> > case ARG_MEM_SIZE:
> > @@ -11814,6 +11818,65 @@ static bool btf_type_is_scalar_struct(struct bpf_verifier_env *env,
> > return btf_struct_is_composed_of(env, btf, t, BTF_MEMBER_SCALAR);
> > }
> >
> > +static int resolve_func_arg_type(struct bpf_verifier_env *env,
> > + struct bpf_reg_state *reg, u32 arg,
> > + struct bpf_call_arg_meta *meta, int insn_idx,
> > + enum bpf_arg_type *arg_type, u32 *arg_size)
> > +{
> > + argno_t argno = argno_from_arg(arg + 1);
> > + const struct btf_param *args;
> > + const struct btf_type *ref_t, *resolve_ret;
> > + const struct btf *btf;
> > + const char *ref_tname;
> > + u32 ref_id;
> > +
> > + if (base_type(*arg_type) == ARG_PTR_TO_MAP_VALUE)
> > + return resolve_map_arg_type(env, meta, arg_type);
> > +
> > + if (base_type(*arg_type) == ARG_PTR_TO_BTF_ID) {
>
> Overall, it all looks great to me.
>
> Here can reduce indent by:
> if (base_type(*arg_type) != ARG_PTR_TO_BTF_ID)
> return 0;
Will change.
>
> Pls address AI nits and respin, so we can land asap.
>
Thanks for reviewing the set. I will respin soon.
^ permalink raw reply [flat|nested] 54+ messages in thread
* Re: [PATCH bpf-next v1 00/22] bpf: Unify helper and kfunc argument checks
2026-09-05 22:00 [PATCH bpf-next v1 00/22] bpf: Unify helper and kfunc argument checks Amery Hung
` (21 preceding siblings ...)
2026-09-05 22:01 ` [PATCH bpf-next v1 22/22] bpf: Check helper and kfunc arguments in one path Amery Hung
@ 2026-09-12 3:20 ` patchwork-bot+netdevbpf
22 siblings, 0 replies; 54+ messages in thread
From: patchwork-bot+netdevbpf @ 2026-09-12 3:20 UTC (permalink / raw)
To: Amery Hung
Cc: bpf, alexei.starovoitov, andrii, daniel, eddyz87, memxor,
kernel-team
Hello:
This series was applied to bpf/bpf-next.git (master)
by Alexei Starovoitov <ast@kernel.org>:
On Sat, 5 Sep 2026 15:00:55 -0700 you wrote:
> Hi,
>
> This is the 3rd and final patchset unifying helepr and kfunc checks.
>
> Helper and kfunc calls currently validate arguments through separate
> loops despite enforcing many of the same contracts. This duplicates
> type admission, nullability, memory, BTF, packet-access, and resource
> ownership handling.
>
> [...]
Here is the summary with links:
- [bpf-next,v1,01/22] bpf: Pass call metadata through shared argument checks
(no matching commit)
- [bpf-next,v1,02/22] bpf: Address check_func_arg() arguments by argno
(no matching commit)
- [bpf-next,v1,03/22] bpf: Only compare func_id against BPF_FUNC_* for helper calls
https://git.kernel.org/bpf/bpf-next/c/d18bcf4537e0
- [bpf-next,v1,04/22] bpf: Only compare func_id against kfunc BTF IDs for kfunc calls
https://git.kernel.org/bpf/bpf-next/c/0bddc59e33a4
- [bpf-next,v1,05/22] bpf: Rename ambiguous function argument types
(no matching commit)
- [bpf-next,v1,06/22] bpf: Unify kfunc argument kinds with enum bpf_arg_type
(no matching commit)
- [bpf-next,v1,07/22] bpf: Align helper and kfunc ARG_PTR_TO_PROG_AUX handling
(no matching commit)
- [bpf-next,v1,08/22] bpf: Classify kfunc arguments the verifier ignores
(no matching commit)
- [bpf-next,v1,09/22] bpf: Set OBJ_RELEASE when generating kfunc argument types
https://git.kernel.org/bpf/bpf-next/c/57e181cde7e6
- [bpf-next,v1,10/22] bpf: Set MEM_UNINIT and dynptr subtypes when generating kfunc arg types
https://git.kernel.org/bpf/bpf-next/c/78811eac7ae6
- [bpf-next,v1,11/22] bpf: Set MEM_RCU when generating kfunc argument types
(no matching commit)
- [bpf-next,v1,12/22] bpf: Resolve BTF ID of ARG_PTR_TO_BTF_ID in kfunc bpf_func_proto
(no matching commit)
- [bpf-next,v1,13/22] bpf: Resolve ARG_PTR_TO_MEM | MEM_FIXED_SIZE size in kfunc bpf_func_proto
https://git.kernel.org/bpf/bpf-next/c/6ca151e7cc32
- [bpf-next,v1,14/22] bpf: Consolidate runtime argument type resolution
(no matching commit)
- [bpf-next,v1,15/22] bpf: Consolidate nullable argument validation
(no matching commit)
- [bpf-next,v1,16/22] bpf: Drop redundant BTF pointer helper write rejection
(no matching commit)
- [bpf-next,v1,17/22] bpf: Consolidate helper and kfunc PTR_TO_BTF_ID argument matching
(no matching commit)
- [bpf-next,v1,18/22] bpf: Admit kfunc argument registers through check_reg_type()
(no matching commit)
- [bpf-next,v1,19/22] selftests/bpf: Test kfunc packet memory direct writes
(no matching commit)
- [bpf-next,v1,20/22] bpf: Consolidate function call pkt_access validation
https://git.kernel.org/bpf/bpf-next/c/8fe994c80af2
- [bpf-next,v1,21/22] bpf: Consolidate release argument validation
(no matching commit)
- [bpf-next,v1,22/22] bpf: Check helper and kfunc arguments in one path
(no matching commit)
You are awesome, thank you!
--
Deet-doot-dot, I am a bot.
https://korg.docs.kernel.org/patchwork/pwbot.html
^ permalink raw reply [flat|nested] 54+ messages in thread
end of thread, other threads:[~2026-09-12 3:21 UTC | newest]
Thread overview: 54+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2026-09-05 22:00 [PATCH bpf-next v1 00/22] bpf: Unify helper and kfunc argument checks Amery Hung
2026-09-05 22:00 ` [PATCH bpf-next v1 01/22] bpf: Pass call metadata through shared " Amery Hung
2026-09-05 22:00 ` [PATCH bpf-next v1 02/22] bpf: Address check_func_arg() arguments by argno Amery Hung
2026-09-05 22:44 ` bot+bpf-ci
2026-09-09 17:37 ` Amery Hung
2026-09-05 22:00 ` [PATCH bpf-next v1 03/22] bpf: Only compare func_id against BPF_FUNC_* for helper calls Amery Hung
2026-09-05 22:00 ` [PATCH bpf-next v1 04/22] bpf: Only compare func_id against kfunc BTF IDs for kfunc calls Amery Hung
2026-09-05 22:44 ` bot+bpf-ci
2026-09-09 17:47 ` Amery Hung
2026-09-05 22:01 ` [PATCH bpf-next v1 05/22] bpf: Rename ambiguous function argument types Amery Hung
2026-09-05 23:08 ` bot+bpf-ci
2026-09-09 17:54 ` Amery Hung
2026-09-05 22:01 ` [PATCH bpf-next v1 06/22] bpf: Unify kfunc argument kinds with enum bpf_arg_type Amery Hung
2026-09-05 23:08 ` bot+bpf-ci
2026-09-09 18:04 ` Amery Hung
2026-09-05 22:01 ` [PATCH bpf-next v1 07/22] bpf: Align helper and kfunc ARG_PTR_TO_PROG_AUX handling Amery Hung
2026-09-05 23:08 ` bot+bpf-ci
2026-09-09 18:23 ` Amery Hung
2026-09-05 22:01 ` [PATCH bpf-next v1 08/22] bpf: Classify kfunc arguments the verifier ignores Amery Hung
2026-09-05 22:44 ` bot+bpf-ci
2026-09-09 18:27 ` Amery Hung
2026-09-05 22:01 ` [PATCH bpf-next v1 09/22] bpf: Set OBJ_RELEASE when generating kfunc argument types Amery Hung
2026-09-05 22:01 ` [PATCH bpf-next v1 10/22] bpf: Set MEM_UNINIT and dynptr subtypes when generating kfunc arg types Amery Hung
2026-09-05 22:01 ` [PATCH bpf-next v1 11/22] bpf: Set MEM_RCU when generating kfunc argument types Amery Hung
2026-09-05 22:44 ` bot+bpf-ci
2026-09-09 18:41 ` Amery Hung
2026-09-05 22:01 ` [PATCH bpf-next v1 12/22] bpf: Resolve BTF ID of ARG_PTR_TO_BTF_ID in kfunc bpf_func_proto Amery Hung
2026-09-05 23:08 ` bot+bpf-ci
2026-09-09 20:42 ` Amery Hung
2026-09-05 22:01 ` [PATCH bpf-next v1 13/22] bpf: Resolve ARG_PTR_TO_MEM | MEM_FIXED_SIZE size " Amery Hung
2026-09-05 22:01 ` [PATCH bpf-next v1 14/22] bpf: Consolidate runtime argument type resolution Amery Hung
2026-09-10 21:52 ` Alexei Starovoitov
2026-09-11 21:01 ` Amery Hung
2026-09-05 22:01 ` [PATCH bpf-next v1 15/22] bpf: Consolidate nullable argument validation Amery Hung
2026-09-05 22:01 ` [PATCH bpf-next v1 16/22] bpf: Drop redundant BTF pointer helper write rejection Amery Hung
2026-09-05 23:08 ` bot+bpf-ci
2026-09-09 20:48 ` Amery Hung
2026-09-05 22:01 ` [PATCH bpf-next v1 17/22] bpf: Consolidate helper and kfunc PTR_TO_BTF_ID argument matching Amery Hung
2026-09-05 22:01 ` [PATCH bpf-next v1 18/22] bpf: Admit kfunc argument registers through check_reg_type() Amery Hung
2026-09-05 23:23 ` bot+bpf-ci
2026-09-10 16:18 ` Amery Hung
2026-09-05 22:01 ` [PATCH bpf-next v1 19/22] selftests/bpf: Test kfunc packet memory direct writes Amery Hung
2026-09-05 22:44 ` bot+bpf-ci
2026-09-11 20:46 ` Amery Hung
2026-09-05 22:01 ` [PATCH bpf-next v1 20/22] bpf: Consolidate function call pkt_access validation Amery Hung
2026-09-05 22:01 ` [PATCH bpf-next v1 21/22] bpf: Consolidate release argument validation Amery Hung
2026-09-05 23:08 ` bot+bpf-ci
2026-09-11 20:47 ` Amery Hung
2026-09-05 22:01 ` [PATCH bpf-next v1 22/22] bpf: Check helper and kfunc arguments in one path Amery Hung
2026-09-05 22:33 ` sashiko-bot
2026-09-11 20:59 ` Amery Hung
2026-09-10 21:53 ` Alexei Starovoitov
2026-09-11 20:55 ` Amery Hung
2026-09-12 3:20 ` [PATCH bpf-next v1 00/22] bpf: Unify helper and kfunc argument checks patchwork-bot+netdevbpf
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox