* [PATCH bpf v1 1/2] bpf: Fix tracing of kfuncs with implicit args
@ 2026-07-10 0:59 Ihor Solodrai
2026-07-10 0:59 ` [PATCH bpf v1 2/2] selftests/bpf: Cover tracing implicit kfunc args Ihor Solodrai
2026-07-10 1:21 ` [PATCH bpf v1 1/2] bpf: Fix tracing of kfuncs with implicit args sashiko-bot
0 siblings, 2 replies; 8+ messages in thread
From: Ihor Solodrai @ 2026-07-10 0:59 UTC (permalink / raw)
To: Alexei Starovoitov, Andrii Nakryiko, Daniel Borkmann,
Eduard Zingerman, Kumar Kartikeya Dwivedi
Cc: Tejun Heo, bpf, linux-kernel, kernel-team
A kfunc marked with KF_IMPLICIT_ARGS flag takes implicit arguments
(such as bpf_prog_aux) that the verifier injects at load time.
resolve_btfids strips those from the kfunc's BTF-visible prototype and
keeps the real kernel ABI in a counterpart _impl prototype [1].
fentry/fexit/fmod_ret/fsession programs may attach to the kernel
functions, including those with implicit args. However
bpf_check_attach_target() and bpf_check_attach_btf_id_multi() extract
the struct btf_func_model from the wrong BTF prototype of the
kfunc. The btf_func_model is later read to construct the trampoline,
which then causes the injected implicit argument to be clobbered and
the kfunc dereferencing garbage [2].
Add btf_attach_func_proto() to resolve the real ABI prototype of the
kfunc the way the call site does: by looking up the _impl prototype
for a KF_IMPLICIT_ARGS kfunc. Use it at both attach-target model
construction sites.
To enable this, make two supporting changes:
* pass bpf_verifier_log instead of bpf_verifier_env to
find_kfunc_impl_proto(), so it can be reused from the attach path
* introduce btf_kfunc_accumulated_flags() helper to read a kfunc's
flags across all hooks, because a program attaching to a kfunc is
not in the kfunc's call-set
The remaining call sites of btf_distill_func_proto() are safe as
is. The BPF_TRACE_ITER case distills a registered iterator's
prototype, and bpf_struct_ops_desc_init() distills the
function-pointer members of a struct_ops type. Neither is a kfunc, and
so can't have implicit arguments.
[1] https://lore.kernel.org/all/20260120222638.3976562-1-ihor.solodrai@linux.dev/
Fixes: 64e1360524b9 ("bpf: Verifier support for KF_IMPLICIT_ARGS")
Reported-by: Tejun Heo <tj@kernel.org>
Link: https://github.com/sched-ext/scx/issues/3687#issuecomment-4906694106
Signed-off-by: Ihor Solodrai <ihor.solodrai@linux.dev>
---
This should probably be backported to 7.1
---
include/linux/btf.h | 1 +
kernel/bpf/btf.c | 20 +++++++++++++++++
kernel/bpf/verifier.c | 51 ++++++++++++++++++++++++++++++++-----------
3 files changed, 59 insertions(+), 13 deletions(-)
diff --git a/include/linux/btf.h b/include/linux/btf.h
index 240401d9b25b..c52245ae0df5 100644
--- a/include/linux/btf.h
+++ b/include/linux/btf.h
@@ -578,6 +578,7 @@ const char *btf_str_by_offset(const struct btf *btf, u32 offset);
struct btf *btf_parse_vmlinux(void);
struct btf *bpf_prog_get_target_btf(const struct bpf_prog *prog);
u32 *btf_kfunc_flags(const struct btf *btf, u32 kfunc_btf_id, const struct bpf_prog *prog);
+u32 btf_kfunc_accumulated_flags(const struct btf *btf, u32 kfunc_btf_id);
bool btf_kfunc_is_allowed(const struct btf *btf, u32 kfunc_btf_id, const struct bpf_prog *prog);
u32 *btf_kfunc_is_modify_return(const struct btf *btf, u32 kfunc_btf_id,
const struct bpf_prog *prog);
diff --git a/kernel/bpf/btf.c b/kernel/bpf/btf.c
index 64572f85edc8..20aeca6b4f95 100644
--- a/kernel/bpf/btf.c
+++ b/kernel/bpf/btf.c
@@ -9114,6 +9114,26 @@ u32 *btf_kfunc_flags(const struct btf *btf, u32 kfunc_btf_id, const struct bpf_p
return btf_kfunc_id_set_contains(btf, hook, kfunc_btf_id);
}
+/*
+ * Return the union of a kfunc's flags across all hooks.
+ * Unlike btf_kfunc_flags(), not restricted to a calling
+ * program's hook. Used when attaching to a kfunc for tracing.
+ */
+u32 btf_kfunc_accumulated_flags(const struct btf *btf, u32 kfunc_btf_id)
+{
+ enum btf_kfunc_hook hook;
+ u32 *hook_flags;
+ u32 flags = 0;
+
+ for (hook = 0; hook < BTF_KFUNC_HOOK_MAX; hook++) {
+ hook_flags = btf_kfunc_id_set_contains(btf, hook, kfunc_btf_id);
+ if (hook_flags)
+ flags |= *hook_flags;
+ }
+
+ return flags;
+}
+
u32 *btf_kfunc_is_modify_return(const struct btf *btf, u32 kfunc_btf_id,
const struct bpf_prog *prog)
{
diff --git a/kernel/bpf/verifier.c b/kernel/bpf/verifier.c
index 6515d4d3c003..fde11a2f6869 100644
--- a/kernel/bpf/verifier.c
+++ b/kernel/bpf/verifier.c
@@ -2584,24 +2584,24 @@ static struct btf *find_kfunc_desc_btf(struct bpf_verifier_env *env, s16 offset)
#define KF_IMPL_SUFFIX "_impl"
-static const struct btf_type *find_kfunc_impl_proto(struct bpf_verifier_env *env,
- struct btf *btf,
- const char *func_name)
+static const struct btf_type *
+find_kfunc_impl_proto(struct bpf_verifier_log *log, struct btf *btf, const char *func_name)
{
- char *buf = env->tmp_str_buf;
const struct btf_type *func;
+ char buf[KSYM_NAME_LEN];
s32 impl_id;
int len;
- len = snprintf(buf, TMP_STR_BUF_LEN, "%s%s", func_name, KF_IMPL_SUFFIX);
- if (len < 0 || len >= TMP_STR_BUF_LEN) {
- verbose(env, "function name %s%s is too long\n", func_name, KF_IMPL_SUFFIX);
+ len = snprintf(buf, sizeof(buf), "%s%s", func_name, KF_IMPL_SUFFIX);
+ if (len < 0 || len >= sizeof(buf)) {
+ bpf_log(log, "function name %s%s is too long\n",
+ func_name, KF_IMPL_SUFFIX);
return NULL;
}
impl_id = btf_find_by_name_kind(btf, buf, BTF_KIND_FUNC);
if (impl_id <= 0) {
- verbose(env, "cannot find function %s in BTF\n", buf);
+ bpf_log(log, "cannot find function %s in BTF\n", buf);
return NULL;
}
@@ -2653,7 +2653,7 @@ static int fetch_kfunc_meta(struct bpf_verifier_env *env,
* can be found through the counterpart _impl kfunc.
*/
if (kfunc_flags && (*kfunc_flags & KF_IMPLICIT_ARGS))
- func_proto = find_kfunc_impl_proto(env, btf, func_name);
+ func_proto = find_kfunc_impl_proto(&env->log, btf, func_name);
else
func_proto = btf_type_by_id(btf, func->type);
@@ -18873,6 +18873,31 @@ static int btf_id_allow_sleepable(u32 btf_id, unsigned long addr, const struct b
return -EINVAL;
}
+/*
+ * Resolve the prototype describing a trace target's real ABI. A
+ * KF_IMPLICIT_ARGS kfunc has its injected args stripped from the public
+ * prototype, so use the _impl prototype; other targets use their own.
+ */
+static const struct btf_type *
+btf_attach_func_proto(struct bpf_verifier_log *log, struct btf *btf, u32 func_id)
+{
+ const struct btf_type *func;
+ const char *name;
+ u32 kfunc_flags;
+
+ func = btf_type_by_id(btf, func_id);
+ if (!func || !btf_type_is_func(func))
+ return NULL;
+
+ kfunc_flags = btf_kfunc_accumulated_flags(btf, func_id);
+ if (kfunc_flags & KF_IMPLICIT_ARGS) {
+ name = btf_name_by_offset(btf, func->name_off);
+ return find_kfunc_impl_proto(log, btf, name);
+ }
+
+ return btf_type_by_id(btf, func->type);
+}
+
int bpf_check_attach_target(struct bpf_verifier_log *log,
const struct bpf_prog *prog,
const struct bpf_prog *tgt_prog,
@@ -19121,8 +19146,8 @@ int bpf_check_attach_target(struct bpf_verifier_log *log,
if (prog_extension &&
btf_check_type_match(log, prog, btf, t))
return -EINVAL;
- t = btf_type_by_id(btf, t->type);
- if (!btf_type_is_func_proto(t))
+ t = btf_attach_func_proto(log, btf, btf_id);
+ if (!t || !btf_type_is_func_proto(t))
return -EINVAL;
if ((prog->aux->saved_dst_prog_type || prog->aux->saved_dst_attach_type) &&
@@ -19407,8 +19432,8 @@ int bpf_check_attach_btf_id_multi(struct btf *btf, struct bpf_prog *prog, u32 bt
return -EINVAL;
if (!btf_type_is_func(t))
return -EINVAL;
- t = btf_type_by_id(btf, t->type);
- if (!btf_type_is_func_proto(t))
+ t = btf_attach_func_proto(NULL, btf, btf_id);
+ if (!t || !btf_type_is_func_proto(t))
return -EINVAL;
err = btf_distill_func_proto(NULL, btf, t, tname, &tgt_info->fmodel);
if (err < 0)
--
2.55.0
^ permalink raw reply related [flat|nested] 8+ messages in thread* [PATCH bpf v1 2/2] selftests/bpf: Cover tracing implicit kfunc args
2026-07-10 0:59 [PATCH bpf v1 1/2] bpf: Fix tracing of kfuncs with implicit args Ihor Solodrai
@ 2026-07-10 0:59 ` Ihor Solodrai
2026-07-10 1:21 ` [PATCH bpf v1 1/2] bpf: Fix tracing of kfuncs with implicit args sashiko-bot
1 sibling, 0 replies; 8+ messages in thread
From: Ihor Solodrai @ 2026-07-10 0:59 UTC (permalink / raw)
To: Alexei Starovoitov, Andrii Nakryiko, Daniel Borkmann,
Eduard Zingerman, Kumar Kartikeya Dwivedi
Cc: Tejun Heo, bpf, linux-kernel, kernel-team
From: Kumar Kartikeya Dwivedi <memxor@gmail.com>
KF_IMPLICIT_ARGS kfuncs have a BPF-call prototype and a real kernel
target prototype. Add a tracing selftest that attaches fentry and fexit
programs to bpf_kfunc_implicit_arg(), runs a syscall BPF program that
calls it, and checks that the tracing context exposes both the explicit
argument and the implicit prog aux pointer.
Signed-off-by: Kumar Kartikeya Dwivedi <memxor@gmail.com>
Co-developed-by: Ihor Solodrai <ihor.solodrai@linux.dev>
Signed-off-by: Ihor Solodrai <ihor.solodrai@linux.dev>
---
.../prog_tests/kfunc_implicit_args_tracing.c | 36 +++++++++
.../bpf/progs/kfunc_implicit_args_tracing.c | 77 +++++++++++++++++++
2 files changed, 113 insertions(+)
create mode 100644 tools/testing/selftests/bpf/prog_tests/kfunc_implicit_args_tracing.c
create mode 100644 tools/testing/selftests/bpf/progs/kfunc_implicit_args_tracing.c
diff --git a/tools/testing/selftests/bpf/prog_tests/kfunc_implicit_args_tracing.c b/tools/testing/selftests/bpf/prog_tests/kfunc_implicit_args_tracing.c
new file mode 100644
index 000000000000..61cc5aaba025
--- /dev/null
+++ b/tools/testing/selftests/bpf/prog_tests/kfunc_implicit_args_tracing.c
@@ -0,0 +1,36 @@
+// SPDX-License-Identifier: GPL-2.0
+/* Copyright (c) 2026 Meta Platforms, Inc. and affiliates. */
+
+#include <test_progs.h>
+#include "kfunc_implicit_args_tracing.skel.h"
+
+void test_kfunc_implicit_args_tracing(void)
+{
+ struct kfunc_implicit_args_tracing *skel;
+ LIBBPF_OPTS(bpf_test_run_opts, topts);
+ int err, fd;
+
+ skel = kfunc_implicit_args_tracing__open_and_load();
+ if (!ASSERT_OK_PTR(skel, "open_and_load"))
+ return;
+
+ err = kfunc_implicit_args_tracing__attach(skel);
+ if (!ASSERT_OK(err, "attach"))
+ goto cleanup;
+
+ fd = bpf_program__fd(skel->progs.trigger_implicit_arg);
+ err = bpf_prog_test_run_opts(fd, &topts);
+ if (!ASSERT_OK(err, "test_run"))
+ goto cleanup;
+
+ ASSERT_EQ(topts.retval, 5, "kfunc_retval");
+ ASSERT_EQ(skel->bss->fentry_arg_cnt, 2, "fentry_arg_cnt");
+ ASSERT_NEQ(skel->bss->fentry_aux_arg, 0, "fentry_aux_arg");
+ ASSERT_EQ(skel->bss->fentry_result, 1, "fentry_result");
+ ASSERT_EQ(skel->bss->fexit_arg_cnt, 2, "fexit_arg_cnt");
+ ASSERT_NEQ(skel->bss->fexit_aux_arg, 0, "fexit_aux_arg");
+ ASSERT_EQ(skel->bss->fexit_result, 1, "fexit_result");
+
+cleanup:
+ kfunc_implicit_args_tracing__destroy(skel);
+}
diff --git a/tools/testing/selftests/bpf/progs/kfunc_implicit_args_tracing.c b/tools/testing/selftests/bpf/progs/kfunc_implicit_args_tracing.c
new file mode 100644
index 000000000000..995f8b8b5b9e
--- /dev/null
+++ b/tools/testing/selftests/bpf/progs/kfunc_implicit_args_tracing.c
@@ -0,0 +1,77 @@
+// SPDX-License-Identifier: GPL-2.0
+/* Copyright (c) 2026 Meta Platforms, Inc. and affiliates. */
+
+#include <vmlinux.h>
+#include <bpf/bpf_helpers.h>
+#include <bpf/bpf_tracing.h>
+#include <errno.h>
+
+extern int bpf_kfunc_implicit_arg(int a) __weak __ksym;
+
+char _license[] SEC("license") = "GPL";
+
+/* Shared arg checks; reports arg count and aux, returns 1 on success. */
+static __always_inline __u64
+check_implicit_args(void *ctx, __u64 *arg_cnt, __u64 *aux_arg)
+{
+ __u64 a = 0, aux = 0, z = 0;
+ __u64 result;
+ __s64 err;
+
+ *arg_cnt = bpf_get_func_arg_cnt(ctx);
+ result = *arg_cnt == 2;
+
+ err = bpf_get_func_arg(ctx, 0, &a);
+ result &= err == 0 && (int)a == 5;
+
+ err = bpf_get_func_arg(ctx, 1, &aux);
+ *aux_arg = aux;
+ result &= err == 0 && aux != 0;
+
+ err = bpf_get_func_arg(ctx, 2, &z);
+ result &= err == -EINVAL;
+
+ return result;
+}
+
+__u64 fentry_result;
+__u64 fentry_arg_cnt;
+__u64 fentry_aux_arg;
+
+SEC("fentry/bpf_kfunc_implicit_arg")
+int BPF_PROG(trace_implicit_arg_fentry)
+{
+ __u64 ret = 0;
+ __s64 err;
+
+ fentry_result = check_implicit_args(ctx, &fentry_arg_cnt, &fentry_aux_arg);
+
+ err = bpf_get_func_ret(ctx, &ret);
+ fentry_result &= err == -EOPNOTSUPP;
+
+ return 0;
+}
+
+__u64 fexit_result;
+__u64 fexit_arg_cnt;
+__u64 fexit_aux_arg;
+
+SEC("fexit/bpf_kfunc_implicit_arg")
+int BPF_PROG(trace_implicit_arg_fexit)
+{
+ __u64 ret = 0;
+ __s64 err;
+
+ fexit_result = check_implicit_args(ctx, &fexit_arg_cnt, &fexit_aux_arg);
+
+ err = bpf_get_func_ret(ctx, &ret);
+ fexit_result &= err == 0 && ret == 5;
+
+ return 0;
+}
+
+SEC("syscall")
+int trigger_implicit_arg(void *ctx)
+{
+ return bpf_kfunc_implicit_arg(5);
+}
--
2.55.0
^ permalink raw reply related [flat|nested] 8+ messages in thread* Re: [PATCH bpf v1 1/2] bpf: Fix tracing of kfuncs with implicit args
2026-07-10 0:59 [PATCH bpf v1 1/2] bpf: Fix tracing of kfuncs with implicit args Ihor Solodrai
2026-07-10 0:59 ` [PATCH bpf v1 2/2] selftests/bpf: Cover tracing implicit kfunc args Ihor Solodrai
@ 2026-07-10 1:21 ` sashiko-bot
2026-07-10 17:08 ` Ihor Solodrai
1 sibling, 1 reply; 8+ messages in thread
From: sashiko-bot @ 2026-07-10 1:21 UTC (permalink / raw)
To: Ihor Solodrai; +Cc: bpf
Thank you for your contribution! Sashiko AI review found 1 potential issue(s) to consider:
- [High] Accessing `btf->kfunc_set_tab` without holding a module reference causes a use-after-free vulnerability.
--
--- Patch [1]: [PATCH bpf v1 1/2] bpf: Fix tracing of kfuncs with implicit args ---
commit 13df91bf669b3a55c459c1663fbf726f9f52bc5c
Author: Ihor Solodrai <ihor.solodrai@linux.dev>
bpf: Fix tracing of kfuncs with implicit args
A kfunc marked with KF_IMPLICIT_ARGS flag takes implicit arguments
(such as bpf_prog_aux) that the verifier injects at load time.
resolve_btfids strips those from the kfunc's BTF-visible prototype and
keeps the real kernel ABI in a counterpart _impl prototype.
Link: https://github.com/sched-ext/scx/issues/3687#issuecomment-4906694106
> diff --git a/kernel/bpf/btf.c b/kernel/bpf/btf.c
> index 64572f85edc83..20aeca6b4f95c 100644
> --- a/kernel/bpf/btf.c
> +++ b/kernel/bpf/btf.c
[ ... ]
> @@ -9114,6 +9114,26 @@ u32 *btf_kfunc_flags(const struct btf *btf, u32 kfunc_btf_id, const struct bpf_p
> return btf_kfunc_id_set_contains(btf, hook, kfunc_btf_id);
> }
>
> +/*
> + * Return the union of a kfunc's flags across all hooks.
> + * Unlike btf_kfunc_flags(), not restricted to a calling
> + * program's hook. Used when attaching to a kfunc for tracing.
> + */
> +u32 btf_kfunc_accumulated_flags(const struct btf *btf, u32 kfunc_btf_id)
> +{
> + enum btf_kfunc_hook hook;
> + u32 *hook_flags;
> + u32 flags = 0;
> +
> + for (hook = 0; hook < BTF_KFUNC_HOOK_MAX; hook++) {
> + hook_flags = btf_kfunc_id_set_contains(btf, hook, kfunc_btf_id);
> + if (hook_flags)
> + flags |= *hook_flags;
> + }
> +
> + return flags;
> +}
> +
> u32 *btf_kfunc_is_modify_return(const struct btf *btf, u32 kfunc_btf_id,
> const struct bpf_prog *prog)
> {
> diff --git a/kernel/bpf/verifier.c b/kernel/bpf/verifier.c
> index 6515d4d3c0032..fde11a2f6869a 100644
> --- a/kernel/bpf/verifier.c
> +++ b/kernel/bpf/verifier.c
[ ... ]
> @@ -19121,8 +19146,8 @@ int bpf_check_attach_target(struct bpf_verifier_log *log,
> if (prog_extension &&
> btf_check_type_match(log, prog, btf, t))
> return -EINVAL;
> - t = btf_type_by_id(btf, t->type);
> - if (!btf_type_is_func_proto(t))
> + t = btf_attach_func_proto(log, btf, btf_id);
> + if (!t || !btf_type_is_func_proto(t))
> return -EINVAL;
[Severity: High]
Does this introduce a use-after-free race condition during module load?
The new btf_attach_func_proto() call executes before the module reference is
safely acquired via btf_try_get_module() later in bpf_check_attach_target():
bpf_check_attach_target()
btf_attach_func_proto()
btf_kfunc_accumulated_flags()
btf_kfunc_id_set_contains()
// Accesses btf->kfunc_set_tab->sets[hook]
If a module is just loading (in MODULE_STATE_COMING) and its BTF is already
visible, a concurrent BPF_PROG_LOAD could trigger this path. Meanwhile, the
module's init function might call register_btf_kfunc_id_set(), which uses
krealloc() on the same array.
Could the sets array be freed by krealloc() while the verifier is performing a
binary search on it here?
--
Sashiko AI review · https://sashiko.dev/#/patchset/20260710005902.2234832-1-ihor.solodrai@linux.dev?part=1
^ permalink raw reply [flat|nested] 8+ messages in thread* Re: [PATCH bpf v1 1/2] bpf: Fix tracing of kfuncs with implicit args
2026-07-10 1:21 ` [PATCH bpf v1 1/2] bpf: Fix tracing of kfuncs with implicit args sashiko-bot
@ 2026-07-10 17:08 ` Ihor Solodrai
2026-07-10 17:23 ` Kumar Kartikeya Dwivedi
0 siblings, 1 reply; 8+ messages in thread
From: Ihor Solodrai @ 2026-07-10 17:08 UTC (permalink / raw)
To: sashiko-reviews; +Cc: bpf
On 7/9/26 6:21 PM, sashiko-bot@kernel.org wrote:
> Thank you for your contribution! Sashiko AI review found 1 potential issue(s) to consider:
> - [High] Accessing `btf->kfunc_set_tab` without holding a module reference causes a use-after-free vulnerability.
> --
>
> --- Patch [1]: [PATCH bpf v1 1/2] bpf: Fix tracing of kfuncs with implicit args ---
> commit 13df91bf669b3a55c459c1663fbf726f9f52bc5c
> Author: Ihor Solodrai <ihor.solodrai@linux.dev>
>
> bpf: Fix tracing of kfuncs with implicit args
>
> A kfunc marked with KF_IMPLICIT_ARGS flag takes implicit arguments
> (such as bpf_prog_aux) that the verifier injects at load time.
> resolve_btfids strips those from the kfunc's BTF-visible prototype and
> keeps the real kernel ABI in a counterpart _impl prototype.
>
> Link: https://github.com/sched-ext/scx/issues/3687#issuecomment-4906694106
>
>> diff --git a/kernel/bpf/btf.c b/kernel/bpf/btf.c
>> index 64572f85edc83..20aeca6b4f95c 100644
>> --- a/kernel/bpf/btf.c
>> +++ b/kernel/bpf/btf.c
> [ ... ]
>> @@ -9114,6 +9114,26 @@ u32 *btf_kfunc_flags(const struct btf *btf, u32 kfunc_btf_id, const struct bpf_p
>> return btf_kfunc_id_set_contains(btf, hook, kfunc_btf_id);
>> }
>>
>> +/*
>> + * Return the union of a kfunc's flags across all hooks.
>> + * Unlike btf_kfunc_flags(), not restricted to a calling
>> + * program's hook. Used when attaching to a kfunc for tracing.
>> + */
>> +u32 btf_kfunc_accumulated_flags(const struct btf *btf, u32 kfunc_btf_id)
>> +{
>> + enum btf_kfunc_hook hook;
>> + u32 *hook_flags;
>> + u32 flags = 0;
>> +
>> + for (hook = 0; hook < BTF_KFUNC_HOOK_MAX; hook++) {
>> + hook_flags = btf_kfunc_id_set_contains(btf, hook, kfunc_btf_id);
>> + if (hook_flags)
>> + flags |= *hook_flags;
>> + }
>> +
>> + return flags;
>> +}
>> +
>> u32 *btf_kfunc_is_modify_return(const struct btf *btf, u32 kfunc_btf_id,
>> const struct bpf_prog *prog)
>> {
>> diff --git a/kernel/bpf/verifier.c b/kernel/bpf/verifier.c
>> index 6515d4d3c0032..fde11a2f6869a 100644
>> --- a/kernel/bpf/verifier.c
>> +++ b/kernel/bpf/verifier.c
> [ ... ]
>> @@ -19121,8 +19146,8 @@ int bpf_check_attach_target(struct bpf_verifier_log *log,
>> if (prog_extension &&
>> btf_check_type_match(log, prog, btf, t))
>> return -EINVAL;
>> - t = btf_type_by_id(btf, t->type);
>> - if (!btf_type_is_func_proto(t))
>> + t = btf_attach_func_proto(log, btf, btf_id);
>> + if (!t || !btf_type_is_func_proto(t))
>> return -EINVAL;
>
> [Severity: High]
> Does this introduce a use-after-free race condition during module load?
>
> The new btf_attach_func_proto() call executes before the module reference is
> safely acquired via btf_try_get_module() later in bpf_check_attach_target():
>
> bpf_check_attach_target()
> btf_attach_func_proto()
> btf_kfunc_accumulated_flags()
> btf_kfunc_id_set_contains()
> // Accesses btf->kfunc_set_tab->sets[hook]
>
> If a module is just loading (in MODULE_STATE_COMING) and its BTF is already
> visible, a concurrent BPF_PROG_LOAD could trigger this path. Meanwhile, the
> module's init function might call register_btf_kfunc_id_set(), which uses
> krealloc() on the same array.
>
> Could the sets array be freed by krealloc() while the verifier is performing a
> binary search on it here?
Not for a module. A module registers each hook set exactly once.
btf_populate_kfunc_set() rejects a second registration with
WARN_ON_ONCE(set && !vmlinux_set), so it's always krealloc(NULL, ...)
And vmlinux sets are all built before BPF_PROG_LOAD can ever run.
That said, the change does violate "call btf_try_get_module() before
using module's btf" expectation. I'll fix this in v2.
>
^ permalink raw reply [flat|nested] 8+ messages in thread* Re: [PATCH bpf v1 1/2] bpf: Fix tracing of kfuncs with implicit args
2026-07-10 17:08 ` Ihor Solodrai
@ 2026-07-10 17:23 ` Kumar Kartikeya Dwivedi
2026-07-10 17:34 ` Ihor Solodrai
0 siblings, 1 reply; 8+ messages in thread
From: Kumar Kartikeya Dwivedi @ 2026-07-10 17:23 UTC (permalink / raw)
To: Ihor Solodrai, sashiko-reviews; +Cc: bpf
On Fri Jul 10, 2026 at 7:08 PM CEST, Ihor Solodrai wrote:
> On 7/9/26 6:21 PM, sashiko-bot@kernel.org wrote:
>> Thank you for your contribution! Sashiko AI review found 1 potential issue(s) to consider:
>> - [High] Accessing `btf->kfunc_set_tab` without holding a module reference causes a use-after-free vulnerability.
>> --
>>
>> --- Patch [1]: [PATCH bpf v1 1/2] bpf: Fix tracing of kfuncs with implicit args ---
>> commit 13df91bf669b3a55c459c1663fbf726f9f52bc5c
>> Author: Ihor Solodrai <ihor.solodrai@linux.dev>
>>
>> bpf: Fix tracing of kfuncs with implicit args
>>
>> A kfunc marked with KF_IMPLICIT_ARGS flag takes implicit arguments
>> (such as bpf_prog_aux) that the verifier injects at load time.
>> resolve_btfids strips those from the kfunc's BTF-visible prototype and
>> keeps the real kernel ABI in a counterpart _impl prototype.
>>
>> Link: https://github.com/sched-ext/scx/issues/3687#issuecomment-4906694106
>>
>>> diff --git a/kernel/bpf/btf.c b/kernel/bpf/btf.c
>>> index 64572f85edc83..20aeca6b4f95c 100644
>>> --- a/kernel/bpf/btf.c
>>> +++ b/kernel/bpf/btf.c
>> [ ... ]
>>> @@ -9114,6 +9114,26 @@ u32 *btf_kfunc_flags(const struct btf *btf, u32 kfunc_btf_id, const struct bpf_p
>>> return btf_kfunc_id_set_contains(btf, hook, kfunc_btf_id);
>>> }
>>>
>>> +/*
>>> + * Return the union of a kfunc's flags across all hooks.
>>> + * Unlike btf_kfunc_flags(), not restricted to a calling
>>> + * program's hook. Used when attaching to a kfunc for tracing.
>>> + */
>>> +u32 btf_kfunc_accumulated_flags(const struct btf *btf, u32 kfunc_btf_id)
>>> +{
>>> + enum btf_kfunc_hook hook;
>>> + u32 *hook_flags;
>>> + u32 flags = 0;
>>> +
>>> + for (hook = 0; hook < BTF_KFUNC_HOOK_MAX; hook++) {
>>> + hook_flags = btf_kfunc_id_set_contains(btf, hook, kfunc_btf_id);
>>> + if (hook_flags)
>>> + flags |= *hook_flags;
>>> + }
>>> +
>>> + return flags;
>>> +}
>>> +
>>> u32 *btf_kfunc_is_modify_return(const struct btf *btf, u32 kfunc_btf_id,
>>> const struct bpf_prog *prog)
>>> {
>>> diff --git a/kernel/bpf/verifier.c b/kernel/bpf/verifier.c
>>> index 6515d4d3c0032..fde11a2f6869a 100644
>>> --- a/kernel/bpf/verifier.c
>>> +++ b/kernel/bpf/verifier.c
>> [ ... ]
>>> @@ -19121,8 +19146,8 @@ int bpf_check_attach_target(struct bpf_verifier_log *log,
>>> if (prog_extension &&
>>> btf_check_type_match(log, prog, btf, t))
>>> return -EINVAL;
>>> - t = btf_type_by_id(btf, t->type);
>>> - if (!btf_type_is_func_proto(t))
>>> + t = btf_attach_func_proto(log, btf, btf_id);
>>> + if (!t || !btf_type_is_func_proto(t))
>>> return -EINVAL;
>>
Also, why not do it for BPF_TRACE_ITER case as well? I just feel keeping it
consistent may outweigh figuring out whether it's necessary in that case or not,
etc.
Please supply a cover letter in the next version too since you're sending
multiple patches.
Thanks
^ permalink raw reply [flat|nested] 8+ messages in thread* Re: [PATCH bpf v1 1/2] bpf: Fix tracing of kfuncs with implicit args
2026-07-10 17:23 ` Kumar Kartikeya Dwivedi
@ 2026-07-10 17:34 ` Ihor Solodrai
2026-07-10 17:40 ` Kumar Kartikeya Dwivedi
0 siblings, 1 reply; 8+ messages in thread
From: Ihor Solodrai @ 2026-07-10 17:34 UTC (permalink / raw)
To: Kumar Kartikeya Dwivedi, sashiko-reviews; +Cc: bpf
On 7/10/26 10:23 AM, Kumar Kartikeya Dwivedi wrote:
> On Fri Jul 10, 2026 at 7:08 PM CEST, Ihor Solodrai wrote:
>> On 7/9/26 6:21 PM, sashiko-bot@kernel.org wrote:
>>> Thank you for your contribution! Sashiko AI review found 1 potential issue(s) to consider:
>>> - [High] Accessing `btf->kfunc_set_tab` without holding a module reference causes a use-after-free vulnerability.
>>> --
>>>
>>> --- Patch [1]: [PATCH bpf v1 1/2] bpf: Fix tracing of kfuncs with implicit args ---
>>> commit 13df91bf669b3a55c459c1663fbf726f9f52bc5c
>>> Author: Ihor Solodrai <ihor.solodrai@linux.dev>
>>>
>>> bpf: Fix tracing of kfuncs with implicit args
>>>
>>> A kfunc marked with KF_IMPLICIT_ARGS flag takes implicit arguments
>>> (such as bpf_prog_aux) that the verifier injects at load time.
>>> resolve_btfids strips those from the kfunc's BTF-visible prototype and
>>> keeps the real kernel ABI in a counterpart _impl prototype.
>>>
>>> Link: https://github.com/sched-ext/scx/issues/3687#issuecomment-4906694106
>>>
>>>> diff --git a/kernel/bpf/btf.c b/kernel/bpf/btf.c
>>>> index 64572f85edc83..20aeca6b4f95c 100644
>>>> --- a/kernel/bpf/btf.c
>>>> +++ b/kernel/bpf/btf.c
>>> [ ... ]
>>>> @@ -9114,6 +9114,26 @@ u32 *btf_kfunc_flags(const struct btf *btf, u32 kfunc_btf_id, const struct bpf_p
>>>> return btf_kfunc_id_set_contains(btf, hook, kfunc_btf_id);
>>>> }
>>>>
>>>> +/*
>>>> + * Return the union of a kfunc's flags across all hooks.
>>>> + * Unlike btf_kfunc_flags(), not restricted to a calling
>>>> + * program's hook. Used when attaching to a kfunc for tracing.
>>>> + */
>>>> +u32 btf_kfunc_accumulated_flags(const struct btf *btf, u32 kfunc_btf_id)
>>>> +{
>>>> + enum btf_kfunc_hook hook;
>>>> + u32 *hook_flags;
>>>> + u32 flags = 0;
>>>> +
>>>> + for (hook = 0; hook < BTF_KFUNC_HOOK_MAX; hook++) {
>>>> + hook_flags = btf_kfunc_id_set_contains(btf, hook, kfunc_btf_id);
>>>> + if (hook_flags)
>>>> + flags |= *hook_flags;
>>>> + }
>>>> +
>>>> + return flags;
>>>> +}
>>>> +
>>>> u32 *btf_kfunc_is_modify_return(const struct btf *btf, u32 kfunc_btf_id,
>>>> const struct bpf_prog *prog)
>>>> {
>>>> diff --git a/kernel/bpf/verifier.c b/kernel/bpf/verifier.c
>>>> index 6515d4d3c0032..fde11a2f6869a 100644
>>>> --- a/kernel/bpf/verifier.c
>>>> +++ b/kernel/bpf/verifier.c
>>> [ ... ]
>>>> @@ -19121,8 +19146,8 @@ int bpf_check_attach_target(struct bpf_verifier_log *log,
>>>> if (prog_extension &&
>>>> btf_check_type_match(log, prog, btf, t))
>>>> return -EINVAL;
>>>> - t = btf_type_by_id(btf, t->type);
>>>> - if (!btf_type_is_func_proto(t))
>>>> + t = btf_attach_func_proto(log, btf, btf_id);
>>>> + if (!t || !btf_type_is_func_proto(t))
>>>> return -EINVAL;
>>>
>
> Also, why not do it for BPF_TRACE_ITER case as well? I just feel keeping it
> consistent may outweigh figuring out whether it's necessary in that case or not,
> etc.
I mentioned this in the patch description:
The BPF_TRACE_ITER case distills a registered iterator's
prototype, and bpf_struct_ops_desc_init() distills the
function-pointer members of a struct_ops type. Neither is
a kfunc, and so can't have implicit arguments.
>
> Please supply a cover letter in the next version too since you're sending
> multiple patches.
I used the first patch's message as a cover letter, since it's
just a fix + selftest pair.
The series could be split further into refactoring changes, but I thought
it'll be easier to backport if the fix is in a single self-contained patch.
I think we want the fix to get into 7.2, and backport it to 7.1.
If it's ok to carry separate refactoring patches there, then I'll split.
Kartikeya, wdyt?
>
> Thanks
^ permalink raw reply [flat|nested] 8+ messages in thread* Re: [PATCH bpf v1 1/2] bpf: Fix tracing of kfuncs with implicit args
2026-07-10 17:34 ` Ihor Solodrai
@ 2026-07-10 17:40 ` Kumar Kartikeya Dwivedi
2026-07-10 19:34 ` Ihor Solodrai
0 siblings, 1 reply; 8+ messages in thread
From: Kumar Kartikeya Dwivedi @ 2026-07-10 17:40 UTC (permalink / raw)
To: Ihor Solodrai, Kumar Kartikeya Dwivedi, sashiko-reviews; +Cc: bpf
On Fri Jul 10, 2026 at 7:34 PM CEST, Ihor Solodrai wrote:
> On 7/10/26 10:23 AM, Kumar Kartikeya Dwivedi wrote:
>> On Fri Jul 10, 2026 at 7:08 PM CEST, Ihor Solodrai wrote:
>>> On 7/9/26 6:21 PM, sashiko-bot@kernel.org wrote:
>>>> Thank you for your contribution! Sashiko AI review found 1 potential issue(s) to consider:
>>>> - [High] Accessing `btf->kfunc_set_tab` without holding a module reference causes a use-after-free vulnerability.
>>>> --
>>>>
>>>> --- Patch [1]: [PATCH bpf v1 1/2] bpf: Fix tracing of kfuncs with implicit args ---
>>>> commit 13df91bf669b3a55c459c1663fbf726f9f52bc5c
>>>> Author: Ihor Solodrai <ihor.solodrai@linux.dev>
>>>>
>>>> bpf: Fix tracing of kfuncs with implicit args
>>>>
>>>> A kfunc marked with KF_IMPLICIT_ARGS flag takes implicit arguments
>>>> (such as bpf_prog_aux) that the verifier injects at load time.
>>>> resolve_btfids strips those from the kfunc's BTF-visible prototype and
>>>> keeps the real kernel ABI in a counterpart _impl prototype.
>>>>
>>>> Link: https://github.com/sched-ext/scx/issues/3687#issuecomment-4906694106
>>>>
>>>>> diff --git a/kernel/bpf/btf.c b/kernel/bpf/btf.c
>>>>> index 64572f85edc83..20aeca6b4f95c 100644
>>>>> --- a/kernel/bpf/btf.c
>>>>> +++ b/kernel/bpf/btf.c
>>>> [ ... ]
>>>>> @@ -9114,6 +9114,26 @@ u32 *btf_kfunc_flags(const struct btf *btf, u32 kfunc_btf_id, const struct bpf_p
>>>>> return btf_kfunc_id_set_contains(btf, hook, kfunc_btf_id);
>>>>> }
>>>>>
>>>>> +/*
>>>>> + * Return the union of a kfunc's flags across all hooks.
>>>>> + * Unlike btf_kfunc_flags(), not restricted to a calling
>>>>> + * program's hook. Used when attaching to a kfunc for tracing.
>>>>> + */
>>>>> +u32 btf_kfunc_accumulated_flags(const struct btf *btf, u32 kfunc_btf_id)
>>>>> +{
>>>>> + enum btf_kfunc_hook hook;
>>>>> + u32 *hook_flags;
>>>>> + u32 flags = 0;
>>>>> +
>>>>> + for (hook = 0; hook < BTF_KFUNC_HOOK_MAX; hook++) {
>>>>> + hook_flags = btf_kfunc_id_set_contains(btf, hook, kfunc_btf_id);
>>>>> + if (hook_flags)
>>>>> + flags |= *hook_flags;
>>>>> + }
>>>>> +
>>>>> + return flags;
>>>>> +}
>>>>> +
>>>>> u32 *btf_kfunc_is_modify_return(const struct btf *btf, u32 kfunc_btf_id,
>>>>> const struct bpf_prog *prog)
>>>>> {
>>>>> diff --git a/kernel/bpf/verifier.c b/kernel/bpf/verifier.c
>>>>> index 6515d4d3c0032..fde11a2f6869a 100644
>>>>> --- a/kernel/bpf/verifier.c
>>>>> +++ b/kernel/bpf/verifier.c
>>>> [ ... ]
>>>>> @@ -19121,8 +19146,8 @@ int bpf_check_attach_target(struct bpf_verifier_log *log,
>>>>> if (prog_extension &&
>>>>> btf_check_type_match(log, prog, btf, t))
>>>>> return -EINVAL;
>>>>> - t = btf_type_by_id(btf, t->type);
>>>>> - if (!btf_type_is_func_proto(t))
>>>>> + t = btf_attach_func_proto(log, btf, btf_id);
>>>>> + if (!t || !btf_type_is_func_proto(t))
>>>>> return -EINVAL;
>>>>
>>
>> Also, why not do it for BPF_TRACE_ITER case as well? I just feel keeping it
>> consistent may outweigh figuring out whether it's necessary in that case or not,
>> etc.
>
>
> I mentioned this in the patch description:
>
> The BPF_TRACE_ITER case distills a registered iterator's
> prototype, and bpf_struct_ops_desc_init() distills the
> function-pointer members of a struct_ops type. Neither is
> a kfunc, and so can't have implicit arguments.
>
Yeah, I saw that, but abstracting that inside the function and not having to
think from the caller about those details would be better. That said, I don't
have any strong preference, it is obviously correct as it is.
>
>>
>> Please supply a cover letter in the next version too since you're sending
>> multiple patches.
>
> I used the first patch's message as a cover letter, since it's
> just a fix + selftest pair.
>
> The series could be split further into refactoring changes, but I thought
> it'll be easier to backport if the fix is in a single self-contained patch.
>
> I think we want the fix to get into 7.2, and backport it to 7.1.
>
> If it's ok to carry separate refactoring patches there, then I'll split.
> Kartikeya, wdyt?
>
You can still keep the patches as is, but just add a cover letter, it will not
affect backporting. The cover letter is just to summarize the series and the fix
in the merge commit. Also, my understanding is that with the Fixes: tag, it
should automatically be picked up for stable kernels.
^ permalink raw reply [flat|nested] 8+ messages in thread* Re: [PATCH bpf v1 1/2] bpf: Fix tracing of kfuncs with implicit args
2026-07-10 17:40 ` Kumar Kartikeya Dwivedi
@ 2026-07-10 19:34 ` Ihor Solodrai
0 siblings, 0 replies; 8+ messages in thread
From: Ihor Solodrai @ 2026-07-10 19:34 UTC (permalink / raw)
To: Kumar Kartikeya Dwivedi, sashiko-reviews; +Cc: bpf
On 7/10/26 10:40 AM, Kumar Kartikeya Dwivedi wrote:
> On Fri Jul 10, 2026 at 7:34 PM CEST, Ihor Solodrai wrote:
>> On 7/10/26 10:23 AM, Kumar Kartikeya Dwivedi wrote:
>>> [...]
>>>
>>> Also, why not do it for BPF_TRACE_ITER case as well? I just feel keeping it
>>> consistent may outweigh figuring out whether it's necessary in that case or not,
>>> etc.
>>
>>
>> I mentioned this in the patch description:
>>
>> The BPF_TRACE_ITER case distills a registered iterator's
>> prototype, and bpf_struct_ops_desc_init() distills the
>> function-pointer members of a struct_ops type. Neither is
>> a kfunc, and so can't have implicit arguments.
>>
>
> Yeah, I saw that, but abstracting that inside the function and not having to
> think from the caller about those details would be better. That said, I don't
> have any strong preference, it is obviously correct as it is.
Let's keep the fix patch minimal. I think we can do a more thoughtful
refactoring as a follow up for bpf-next. Hopefully I'll find time for
this in not so distant future.
Just sent a v2 with a module ref fix and a brief cover.
>
>>
>>>
>>> Please supply a cover letter in the next version too since you're sending
>>> multiple patches.
>>
>> I used the first patch's message as a cover letter, since it's
>> just a fix + selftest pair.
>>
>> The series could be split further into refactoring changes, but I thought
>> it'll be easier to backport if the fix is in a single self-contained patch.
>>
>> I think we want the fix to get into 7.2, and backport it to 7.1.
>>
>> If it's ok to carry separate refactoring patches there, then I'll split.
>> Kartikeya, wdyt?
>>
>
> You can still keep the patches as is, but just add a cover letter, it will not
> affect backporting. The cover letter is just to summarize the series and the fix
> in the merge commit. Also, my understanding is that with the Fixes: tag, it
> should automatically be picked up for stable kernels.
^ permalink raw reply [flat|nested] 8+ messages in thread
end of thread, other threads:[~2026-07-10 19:34 UTC | newest]
Thread overview: 8+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2026-07-10 0:59 [PATCH bpf v1 1/2] bpf: Fix tracing of kfuncs with implicit args Ihor Solodrai
2026-07-10 0:59 ` [PATCH bpf v1 2/2] selftests/bpf: Cover tracing implicit kfunc args Ihor Solodrai
2026-07-10 1:21 ` [PATCH bpf v1 1/2] bpf: Fix tracing of kfuncs with implicit args sashiko-bot
2026-07-10 17:08 ` Ihor Solodrai
2026-07-10 17:23 ` Kumar Kartikeya Dwivedi
2026-07-10 17:34 ` Ihor Solodrai
2026-07-10 17:40 ` Kumar Kartikeya Dwivedi
2026-07-10 19:34 ` Ihor Solodrai
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox