kernel/bpf/verifier.c | 19 +++++++++++++++-- .../testing/selftests/bpf/test_kmods/bpf_testmod.c | 6 +++++- 2 files changed, 22 insertions(+), 3 deletions(-)
From: Yuan Chen <chenyuan@kylinos.cn>
v4: https://lore.kernel.org/bpf/20260602093836.2632714-1-chenyuan_fl@163.com/
v1 made btf_types_are_same() cross-BTF aware by comparing kind, size
and name. Alan Maguire pointed out that this would be too permissive
since two different structs can share the same name and size.
v2 used a name-based classifier (get_kfunc_arg_inject_type).
v3 splits the combined is_kfunc_arg_ignore || is_kfunc_arg_implicit
check in check_kfunc_args(), so that an implicit argument reaching
is_kfunc_arg_implicit() without any prior handler is rejected with
-EFAULT instead of silently skipped (Eduard Zingerman). This prevents
the silent fallthrough that occurred when module BTF was inconsistent
with vmlinux (e.g. pahole 1.30 breaking distilled base dedup).
v4: fix a build error in the selftest.
v5 changes:
- Patch 1: extend the kfunc allow-list in check_kfunc_args() to cover
all kfuncs with implicit args handled in bpf_fixup_kfunc_call():
obj_new, percpu_obj_new, obj_drop, percpu_obj_drop,
refcount_acquire, list_push, rbtree_add (Eduard Zingerman).
- Patch 2: simplify the selftest by verifying aux->name in
bpf_kfunc_implicit_arg() instead of adding a separate struct_ops
test program with inline assembly (Eduard Zingerman). This is
a single-file change exercised by the existing kfunc_implicit_args
selftest.
Yuan Chen (2):
bpf: Fix kfunc implicit arg inject type detection to prevent invalid
pointer deref
selftests/bpf: strengthen bpf_kfunc_implicit_arg to verify aux injection
kernel/bpf/verifier.c | 19 +++++++++++++++--
.../testing/selftests/bpf/test_kmods/bpf_testmod.c | 6 +++++-
2 files changed, 22 insertions(+), 3 deletions(-)
--
2.54.0
From: Yuan Chen <chenyuan@kylinos.cn>
v5: https://lore.kernel.org/bpf/20260608142618.3064380-1-chenyuan_fl@163.com/
v1 made btf_types_are_same() cross-BTF aware by comparing kind, size
and name. Alan Maguire pointed out that this would be too permissive
since two different structs can share the same name and size.
v2 used a name-based classifier (get_kfunc_arg_inject_type).
v3 splits the combined is_kfunc_arg_ignore || is_kfunc_arg_implicit
check in check_kfunc_args(), so that an implicit argument reaching
is_kfunc_arg_implicit() without any prior handler is rejected with
-EFAULT instead of silently skipped (Eduard Zingerman). This prevents
the silent fallthrough that occurred when module BTF was inconsistent
with vmlinux (e.g. pahole 1.30 breaking distilled base dedup).
v4: fix a build error in the selftest.
v5 changes:
- Patch 1: extend the kfunc allow-list to cover all KF_IMPLICIT_ARGS
kfuncs handled in bpf_fixup_kfunc_call(): obj_new, percpu_obj_new,
obj_drop, percpu_obj_drop, refcount_acquire, list_push, rbtree_add.
- Patch 2: simplify the selftest by verifying aux->name in
bpf_kfunc_implicit_arg() instead of adding a separate struct_ops
test program with inline assembly.
v6 changes:
- Patch 1: fix comment formatting to follow kernel style (Eduard
Zingerman).
Yuan Chen (2):
bpf: Fix kfunc implicit arg inject type detection to prevent invalid
pointer deref
selftests/bpf: strengthen bpf_kfunc_implicit_arg to verify aux injection
kernel/bpf/verifier.c | 20 +++++++++++++++++++-
.../testing/selftests/bpf/test_kmods/bpf_testmod.c | 6 +++++-
2 files changed, 24 insertions(+), 2 deletions(-)
--
2.54.0
From: Yuan Chen <chenyuan@kylinos.cn>
When a module kfunc declares an implicit struct bpf_prog_aux * argument,
the verifier must identify it so the kernel injects env->prog->aux into
the correct register at runtime. The original check used
is_kfunc_arg_prog_aux() which calls btf_types_are_same() to compare the
module BTF type against vmlinux.
Root Cause
This issue was triggered by pahole 1.30 generating module BTF with
incorrect type information, which caused the kernel's distilled base
BTF deduplication for modules to fail. As a result, the module retained
its own copy of struct bpf_prog_aux with a different BTF ID than
vmlinux's definition. While pahole 1.31 fixed the BTF generation issue,
the kernel must be robust against such inconsistencies: a BTF mismatch
should result in a clean rejection, not a kernel crash or information
disclosure.
When the distilled base dedup fails and btf_types_are_same() cannot
match the module's bpf_prog_aux type against vmlinux's,
is_kfunc_arg_prog_aux() returned false and the code fell through
silently without setting arg_prog. The kfunc then received whatever
value was in the argument register and dereferenced it as a
bpf_prog_aux pointer, leading to:
BUG: kernel invalid pointer dereference, address: 00000000000009e2
RIP: bpf_prog_get_assoc_struct_ops+0xa/0xc0
RDI: 0x000000000000046d (stale register value)
In the observed crash the stale value was the process PID, causing a
dereference within the unmapped NULL page. However, an attacker able
to control the register value -- for example by writing a BPF program
that explicitly sets R2 before calling a KF_IMPLICIT_ARGS kfunc --
could redirect the dereference to arbitrary kernel memory, turning
this into an information disclosure. The fix ensures the verifier
either validates and injects the correct bpf_prog_aux pointer, or
rejects the program outright -- no silent fallthrough that could
be exploited.
Crash Stack Trace
PID: 1133 TASK: ffff8881057d3900 CPU: 3 COMMAND: "test_progs"
#0 machine_kexec at ffffffff812f6e26
#1 __crash_kexec at ffffffff8145a788
#2 crash_kexec at ffffffff8145ac24
#3 oops_end at ffffffff812bb67c
#4 page_fault_oops at ffffffff813053a1
#5 exc_page_fault at ffffffff828e60a1
#6 asm_exc_page_fault at ffffffff810012a6
[exception RIP: bpf_prog_get_assoc_struct_ops+10]
RIP: ffffffff815c024a RSP: ffffc90001b57e48 RFLAGS: 00010283
RAX: ffff8881057d3900 RBX: ffffc90001b57e68 RCX: ffff8881057d3900
RDX: 0000607d4d1768b8 RSI: 000000000000046d RDI: 000000000000046d
#7 bpf_kfunc_multi_st_ops_test_1_assoc at ffffffffc0013a85 [bpf_testmod]
#8 bpf_trace_run2 at ffffffff814f8332
#9 __traceiter_sys_enter at ffffffff81415f45
#10 trace_syscall_enter at ffffffff81416735
#11 do_syscall_64 at ffffffff828e06a1
Fix
Split the combined is_kfunc_arg_ignore() || is_kfunc_arg_implicit()
check in check_kfunc_args() so that an implicit argument reaching
is_kfunc_arg_implicit() without being handled by a prior handler is
rejected with -EFAULT, instead of silently skipped. Existing implicit
args in bpf_fixup_kfunc_call() (obj_new, percpu_obj_new, obj_drop,
percpu_obj_drop, refcount_acquire, list_push, rbtree_add) are
explicitly allowed.
Suggested-by: Eduard Zingerman <eddyz87@gmail.com>
Fixes: 64e1360524b9 ("bpf: Verifier support for KF_IMPLICIT_ARGS")
Signed-off-by: Yuan Chen <chenyuan@kylinos.cn>
---
kernel/bpf/verifier.c | 21 ++++++++++++++++++++-
1 file changed, 20 insertions(+), 1 deletion(-)
diff --git a/kernel/bpf/verifier.c b/kernel/bpf/verifier.c
index 8ed484cb1a8a..0d598a419008 100644
--- a/kernel/bpf/verifier.c
+++ b/kernel/bpf/verifier.c
@@ -11885,9 +11885,28 @@ static int check_kfunc_args(struct bpf_verifier_env *env, struct bpf_kfunc_call_
continue;
}
- if (is_kfunc_arg_ignore(btf, &args[i]) || is_kfunc_arg_implicit(meta, i))
+ if (is_kfunc_arg_ignore(btf, &args[i]))
continue;
+ if (is_kfunc_arg_implicit(meta, i)) {
+ /*
+ * This is an exhaustive list of the non-bpf_prog_aux
+ * implicit args kfuncs, which are handled
+ * post-verification by bpf_fixup_kfunc_call().
+ */
+ if (is_bpf_obj_new_kfunc(meta->func_id) ||
+ is_bpf_percpu_obj_new_kfunc(meta->func_id) ||
+ is_bpf_obj_drop_kfunc(meta->func_id) ||
+ is_bpf_percpu_obj_drop_kfunc(meta->func_id) ||
+ is_bpf_refcount_acquire_kfunc(meta->func_id) ||
+ is_bpf_list_push_kfunc(meta->func_id) ||
+ is_bpf_rbtree_add_kfunc(meta->func_id))
+ continue;
+ verbose(env, "%s unrecognized implicit argument, possible BTF mismatch\n",
+ reg_arg_name(env, argno));
+ return -EFAULT;
+ }
+
t = btf_type_skip_modifiers(btf, args[i].type, NULL);
if (btf_type_is_scalar(t)) {
--
2.54.0
On Tue, 2026-06-09 at 20:52 +0800, chenyuan_fl@163.com wrote:
> From: Yuan Chen <chenyuan@kylinos.cn>
>
> When a module kfunc declares an implicit struct bpf_prog_aux * argument,
> the verifier must identify it so the kernel injects env->prog->aux into
> the correct register at runtime. The original check used
> is_kfunc_arg_prog_aux() which calls btf_types_are_same() to compare the
> module BTF type against vmlinux.
>
> Root Cause
>
> This issue was triggered by pahole 1.30 generating module BTF with
> incorrect type information, which caused the kernel's distilled base
> BTF deduplication for modules to fail. As a result, the module retained
> its own copy of struct bpf_prog_aux with a different BTF ID than
> vmlinux's definition. While pahole 1.31 fixed the BTF generation issue,
> the kernel must be robust against such inconsistencies: a BTF mismatch
> should result in a clean rejection, not a kernel crash or information
> disclosure.
>
> When the distilled base dedup fails and btf_types_are_same() cannot
> match the module's bpf_prog_aux type against vmlinux's,
> is_kfunc_arg_prog_aux() returned false and the code fell through
> silently without setting arg_prog. The kfunc then received whatever
> value was in the argument register and dereferenced it as a
> bpf_prog_aux pointer, leading to:
>
> BUG: kernel invalid pointer dereference, address: 00000000000009e2
> RIP: bpf_prog_get_assoc_struct_ops+0xa/0xc0
> RDI: 0x000000000000046d (stale register value)
>
> In the observed crash the stale value was the process PID, causing a
> dereference within the unmapped NULL page. However, an attacker able
> to control the register value -- for example by writing a BPF program
> that explicitly sets R2 before calling a KF_IMPLICIT_ARGS kfunc --
> could redirect the dereference to arbitrary kernel memory, turning
> this into an information disclosure. The fix ensures the verifier
> either validates and injects the correct bpf_prog_aux pointer, or
> rejects the program outright -- no silent fallthrough that could
> be exploited.
>
> Crash Stack Trace
>
> PID: 1133 TASK: ffff8881057d3900 CPU: 3 COMMAND: "test_progs"
> #0 machine_kexec at ffffffff812f6e26
> #1 __crash_kexec at ffffffff8145a788
> #2 crash_kexec at ffffffff8145ac24
> #3 oops_end at ffffffff812bb67c
> #4 page_fault_oops at ffffffff813053a1
> #5 exc_page_fault at ffffffff828e60a1
> #6 asm_exc_page_fault at ffffffff810012a6
> [exception RIP: bpf_prog_get_assoc_struct_ops+10]
> RIP: ffffffff815c024a RSP: ffffc90001b57e48 RFLAGS: 00010283
> RAX: ffff8881057d3900 RBX: ffffc90001b57e68 RCX: ffff8881057d3900
> RDX: 0000607d4d1768b8 RSI: 000000000000046d RDI: 000000000000046d
> #7 bpf_kfunc_multi_st_ops_test_1_assoc at ffffffffc0013a85 [bpf_testmod]
> #8 bpf_trace_run2 at ffffffff814f8332
> #9 __traceiter_sys_enter at ffffffff81415f45
> #10 trace_syscall_enter at ffffffff81416735
> #11 do_syscall_64 at ffffffff828e06a1
>
> Fix
>
> Split the combined is_kfunc_arg_ignore() || is_kfunc_arg_implicit()
> check in check_kfunc_args() so that an implicit argument reaching
> is_kfunc_arg_implicit() without being handled by a prior handler is
> rejected with -EFAULT, instead of silently skipped. Existing implicit
> args in bpf_fixup_kfunc_call() (obj_new, percpu_obj_new, obj_drop,
> percpu_obj_drop, refcount_acquire, list_push, rbtree_add) are
> explicitly allowed.
>
> Suggested-by: Eduard Zingerman <eddyz87@gmail.com>
> Fixes: 64e1360524b9 ("bpf: Verifier support for KF_IMPLICIT_ARGS")
> Signed-off-by: Yuan Chen <chenyuan@kylinos.cn>
> ---
Please do not drop acks.
Also, wdyt about this:
https://lore.kernel.org/bpf/20260602093836.2632714-1-chenyuan_fl@163.com/T/#m736b14f7e8b0a117bf0791ce79fc0896df8ca0f2
?
[...]
> diff --git a/kernel/bpf/verifier.c b/kernel/bpf/verifier.c
> index 7fb88e1cd7c4..806c86388984 100644
> --- a/kernel/bpf/verifier.c
> +++ b/kernel/bpf/verifier.c
> @@ -12082,9 +12082,28 @@ static int check_kfunc_args(struct bpf_verifier_env *env, struct bpf_kfunc_call_
> continue;
> }
>
> - if (is_kfunc_arg_ignore(btf, &args[i]) || is_kfunc_arg_implicit(meta, i))
> + if (is_kfunc_arg_ignore(btf, &args[i]))
> continue;
>
> + if (is_kfunc_arg_implicit(meta, i)) {
> + /*
> + * This is an exhaustive list of the non-bpf_prog_aux
> + * implicit args kfuncs, which are handled
> + * post-verification by bpf_fixup_kfunc_call().
> + */
> + if (is_bpf_obj_new_kfunc(meta->func_id) ||
> + is_bpf_percpu_obj_new_kfunc(meta->func_id) ||
> + is_bpf_obj_drop_kfunc(meta->func_id) ||
> + is_bpf_percpu_obj_drop_kfunc(meta->func_id) ||
> + is_bpf_refcount_acquire_kfunc(meta->func_id) ||
> + is_bpf_list_push_kfunc(meta->func_id) ||
> + is_bpf_rbtree_add_kfunc(meta->func_id))
> + continue;
> + verbose(env, "%s unrecognized implicit argument, possible BTF mismatch\n",
> + reg_arg_name(env, argno));
> + return -EFAULT;
> + }
> +
Does this code compile? The call to reg_arg_name(env, argno) references
two symbols that don't exist at this commit. The function reg_arg_name()
is not defined in the tree: searching kernel/bpf/verifier.c finds the
identifier only once at this call site, and a tree-wide search yields no
definition. The variable argno is also undeclared: check_kfunc_args()
declares 'u32 regno = i + 1, ref_id, type_size;' but has no argno local.
Both trigger build failures (implicit declaration of function and
undeclared variable). The intended register is likely regno, matching the
pattern used elsewhere in this function:
verbose(env, "R%d ...", regno);
Looking at the comment by Ihor Solodrai on Mon, 8 Jun 2026 17:54:36 -0700
(https://lore.kernel.org/bpf/eab4e479-2023-49b8-9915-1c8ef2356837@linux.dev/),
the list of allowed kfuncs is incomplete. It omits bpf_wq_set_callback,
bpf_stream_vprintk, bpf_stream_print_stack, bpf_task_work_schedule_signal,
and bpf_task_work_schedule_resume, all of which have KF_IMPLICIT_ARGS and
would trigger the new error path incorrectly.
Eduard Zingerman's response on Mon, 08 Jun 2026 23:20:36 -0700
(https://lore.kernel.org/bpf/0d63a8d68bf0778d6e931ed27892a5bcb151295d.camel@gmail.com/)
suggested an alternative fix: instead of enumerating kfuncs in the
verifier, check in bpf_fixup_kfunc_call() whether an implicit arg has the
KF_IMPLICIT_ARGS flag but wasn't filled by an earlier handler. This would
catch the BTF mismatch case without needing to maintain a list and without
breaking valid kfuncs.
The comment claims the list is exhaustive, but it's missing at least five
kfuncs. Should this use Eduard's suggested approach in bpf_fixup_kfunc_call()
instead?
---
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/27208267613
From: Yuan Chen <chenyuan@kylinos.cn>
Verify that the KF_IMPLICIT_ARGS injection path correctly passes
the bpf_prog_aux pointer by checking aux->name in
bpf_kfunc_implicit_arg() for the expected program name prefix.
If the verifier incorrectly skipped injection (as could happen
with pahole 1.30's BTF mismatch), the stale register would not
contain a valid aux pointer and the name check would fail.
This is a positive test exercised by the existing kfunc_implicit_args
selftest, which calls bpf_kfunc_implicit_arg(5) and expects a return
value of 5.
Signed-off-by: Yuan Chen <chenyuan@kylinos.cn>
---
tools/testing/selftests/bpf/test_kmods/bpf_testmod.c | 6 +++++-
1 file changed, 5 insertions(+), 1 deletion(-)
diff --git a/tools/testing/selftests/bpf/test_kmods/bpf_testmod.c b/tools/testing/selftests/bpf/test_kmods/bpf_testmod.c
index 30f1cd23093c..624d57a5c79a 100644
--- a/tools/testing/selftests/bpf/test_kmods/bpf_testmod.c
+++ b/tools/testing/selftests/bpf/test_kmods/bpf_testmod.c
@@ -1906,7 +1906,11 @@ int bpf_kfunc_multi_st_ops_test_1_assoc(struct st_ops_args *args, struct bpf_pro
int bpf_kfunc_implicit_arg(int a, struct bpf_prog_aux *aux)
{
- if (aux && a > 0)
+ /* Verify the kernel injected the correct bpf_prog_aux pointer
+ * rather than leaving a stale register value. */
+ if (!aux || strncmp(aux->name, "test_kfunc", sizeof("test_kfunc") - 1))
+ return -EINVAL;
+ if (a > 0)
return a;
return -EINVAL;
}
--
2.54.0
On Mon Jun 8, 2026 at 7:26 AM PDT, chenyuan_fl wrote: > From: Yuan Chen <chenyuan@kylinos.cn> > > v4: https://lore.kernel.org/bpf/20260602093836.2632714-1-chenyuan_fl@163.com/ > > v1 made btf_types_are_same() cross-BTF aware by comparing kind, size > and name. Alan Maguire pointed out that this would be too permissive > since two different structs can share the same name and size. > > v2 used a name-based classifier (get_kfunc_arg_inject_type). > > v3 splits the combined is_kfunc_arg_ignore || is_kfunc_arg_implicit > check in check_kfunc_args(), so that an implicit argument reaching > is_kfunc_arg_implicit() without any prior handler is rejected with > -EFAULT instead of silently skipped (Eduard Zingerman). This prevents > the silent fallthrough that occurred when module BTF was inconsistent > with vmlinux (e.g. pahole 1.30 breaking distilled base dedup). > > v4: fix a build error in the selftest. > > v5 changes: > - Patch 1: extend the kfunc allow-list in check_kfunc_args() to cover > all kfuncs with implicit args handled in bpf_fixup_kfunc_call(): > obj_new, percpu_obj_new, obj_drop, percpu_obj_drop, > refcount_acquire, list_push, rbtree_add (Eduard Zingerman). > - Patch 2: simplify the selftest by verifying aux->name in > bpf_kfunc_implicit_arg() instead of adding a separate struct_ops > test program with inline assembly (Eduard Zingerman). This is > a single-file change exercised by the existing kfunc_implicit_args > selftest. Pls address feedback from Eduard and respin with [PATCH bpf-next v6] subject. pw-bot: cr
© 2016 - 2026 Red Hat, Inc.