* [PATCH bpf-next v3 0/2] bpf: copy BPF token from main program to subprograms
@ 2026-04-15 18:35 Eduard Zingerman
2026-04-15 18:35 ` [PATCH bpf-next v3 1/2] " Eduard Zingerman
2026-04-15 18:35 ` [PATCH bpf-next v3 2/2] selftests/bpf: verify kallsyms entries for token-loaded subprograms Eduard Zingerman
0 siblings, 2 replies; 5+ messages in thread
From: Eduard Zingerman @ 2026-04-15 18:35 UTC (permalink / raw)
To: bpf, ast, andrii
Cc: daniel, martin.lau, kernel-team, yonghong.song, eddyz87, ctao
bpf_jit_subprogs() omits aux->token when it creates a struct
bpf_prog_aux instances for a subprograms.
This means that for programs loaded via BPF token (i.e., from a
non-init user namespace), subprograms fail the bpf_token_capable()
check in bpf_prog_kallsyms_add() and don't appear in /proc/kallsyms.
Which in-turn makes it impossible to freplace such subprograms.
Changelog:
v2 -> v3:
- mark selftest as serial (sashiko).
v1 -> v2:
- target bpf-next tree (fixups.c) instead of bpf tree (verifier.c).
v1: https://lore.kernel.org/bpf/20260414-subprog-token-fix-v1-0-5b1a38e01546@gmail.com/T/
v2: https://lore.kernel.org/bpf/20260414-subprog-token-fix-v2-0-59146c31f6f1@gmail.com/T/
---
Eduard Zingerman (2):
bpf: copy BPF token from main program to subprograms
selftests/bpf: verify kallsyms entries for token-loaded subprograms
kernel/bpf/fixups.c | 1 +
tools/testing/selftests/bpf/Makefile | 1 +
tools/testing/selftests/bpf/prog_tests/token.c | 83 +++++++++++++++++++++-
.../selftests/bpf/prog_tests/unpriv_bpf_disabled.c | 21 +-----
tools/testing/selftests/bpf/progs/token_kallsyms.c | 19 +++++
tools/testing/selftests/bpf/sysctl_helpers.c | 26 +++++++
tools/testing/selftests/bpf/sysctl_helpers.h | 7 ++
7 files changed, 135 insertions(+), 23 deletions(-)
---
base-commit: 71b500afd2f7336f5b6c6026f2af546fc079be26
change-id: 20260414-subprog-token-fix-96c29ffa7e12
^ permalink raw reply [flat|nested] 5+ messages in thread* [PATCH bpf-next v3 1/2] bpf: copy BPF token from main program to subprograms 2026-04-15 18:35 [PATCH bpf-next v3 0/2] bpf: copy BPF token from main program to subprograms Eduard Zingerman @ 2026-04-15 18:35 ` Eduard Zingerman 2026-04-15 18:35 ` [PATCH bpf-next v3 2/2] selftests/bpf: verify kallsyms entries for token-loaded subprograms Eduard Zingerman 1 sibling, 0 replies; 5+ messages in thread From: Eduard Zingerman @ 2026-04-15 18:35 UTC (permalink / raw) To: bpf, ast, andrii Cc: daniel, martin.lau, kernel-team, yonghong.song, eddyz87, ctao bpf_jit_subprogs() copies various fields from the main program's aux to each subprogram's aux, but omits the BPF token. This causes bpf_prog_kallsyms_add() to fail for subprograms loaded via BPF token, as bpf_token_capable() falls back to capable() in init_user_ns when token is NULL. Copy prog->aux->token to func[i]->aux->token so that subprograms inherit the same capability delegation as the main program. Fixes: d79a35497547 ("bpf: Consistently use BPF token throughout BPF verifier logic") Signed-off-by: Tao Chen <ctao@meta.com> Signed-off-by: Eduard Zingerman <eddyz87@gmail.com> --- kernel/bpf/fixups.c | 1 + 1 file changed, 1 insertion(+) diff --git a/kernel/bpf/fixups.c b/kernel/bpf/fixups.c index 67c9b28767e1..dd00a680e4ea 100644 --- a/kernel/bpf/fixups.c +++ b/kernel/bpf/fixups.c @@ -1110,6 +1110,7 @@ int bpf_jit_subprogs(struct bpf_verifier_env *env) func[i]->aux->exception_cb = env->subprog_info[i].is_exception_cb; func[i]->aux->changes_pkt_data = env->subprog_info[i].changes_pkt_data; func[i]->aux->might_sleep = env->subprog_info[i].might_sleep; + func[i]->aux->token = prog->aux->token; if (!i) func[i]->aux->exception_boundary = env->seen_exception; -- 2.53.0 ^ permalink raw reply related [flat|nested] 5+ messages in thread
* [PATCH bpf-next v3 2/2] selftests/bpf: verify kallsyms entries for token-loaded subprograms 2026-04-15 18:35 [PATCH bpf-next v3 0/2] bpf: copy BPF token from main program to subprograms Eduard Zingerman 2026-04-15 18:35 ` [PATCH bpf-next v3 1/2] " Eduard Zingerman @ 2026-04-15 18:35 ` Eduard Zingerman 2026-04-15 19:17 ` sashiko-bot 1 sibling, 1 reply; 5+ messages in thread From: Eduard Zingerman @ 2026-04-15 18:35 UTC (permalink / raw) To: bpf, ast, andrii Cc: daniel, martin.lau, kernel-team, yonghong.song, eddyz87, ctao Add a test that loads an XDP program with a global subprogram using a BPF token from a user namespace, then verifies that both the main program and the subprogram appear in /proc/kallsyms. This exercises the bpf_prog_kallsyms_add() path for subprograms and would have caught the missing aux->token copy in bpf_jit_subprogs(). load_kallsyms_local() filters out kallsyms with zero addresses. For a process with limited capabilities to read kallsym addresses the following sysctl variables have to be set to zero: - /proc/sys/kernel/perf_event_paranoid - /proc/sys/kernel/kptr_restrict Set these variables using sysctl_set() utility function extracted from unpriv_bpf_disabled.c to a separate c/header. Since the test modifies global system state, mark it as serial. Signed-off-by: Eduard Zingerman <eddyz87@gmail.com> --- tools/testing/selftests/bpf/Makefile | 1 + tools/testing/selftests/bpf/prog_tests/token.c | 83 +++++++++++++++++++++- .../selftests/bpf/prog_tests/unpriv_bpf_disabled.c | 21 +----- tools/testing/selftests/bpf/progs/token_kallsyms.c | 19 +++++ tools/testing/selftests/bpf/sysctl_helpers.c | 26 +++++++ tools/testing/selftests/bpf/sysctl_helpers.h | 7 ++ 6 files changed, 134 insertions(+), 23 deletions(-) diff --git a/tools/testing/selftests/bpf/Makefile b/tools/testing/selftests/bpf/Makefile index f75c4f52c028..da81a1853cfd 100644 --- a/tools/testing/selftests/bpf/Makefile +++ b/tools/testing/selftests/bpf/Makefile @@ -750,6 +750,7 @@ TRUNNER_EXTRA_SOURCES := test_progs.c \ btf_helpers.c \ cap_helpers.c \ unpriv_helpers.c \ + sysctl_helpers.c \ netlink_helpers.c \ jit_disasm_helpers.c \ io_helpers.c \ diff --git a/tools/testing/selftests/bpf/prog_tests/token.c b/tools/testing/selftests/bpf/prog_tests/token.c index b81dde283052..cf5b94512cff 100644 --- a/tools/testing/selftests/bpf/prog_tests/token.c +++ b/tools/testing/selftests/bpf/prog_tests/token.c @@ -1,9 +1,7 @@ // SPDX-License-Identifier: GPL-2.0 /* Copyright (c) 2023 Meta Platforms, Inc. and affiliates. */ #define _GNU_SOURCE -#include <test_progs.h> #include <bpf/btf.h> -#include "cap_helpers.h" #include <fcntl.h> #include <sched.h> #include <signal.h> @@ -15,9 +13,17 @@ #include <sys/stat.h> #include <sys/syscall.h> #include <sys/un.h> + +#include "bpf_util.h" +#include "cap_helpers.h" +#include "sysctl_helpers.h" +#include "test_progs.h" +#include "trace_helpers.h" + #include "priv_map.skel.h" #include "priv_prog.skel.h" #include "dummy_st_ops_success.skel.h" +#include "token_kallsyms.skel.h" #include "token_lsm.skel.h" #include "priv_freplace_prog.skel.h" @@ -1045,6 +1051,58 @@ static int userns_obj_priv_implicit_token_envvar(int mnt_fd, struct token_lsm *l return -EINVAL; } +static bool kallsyms_has_bpf_func(struct ksyms *ksyms, const char *func_name) +{ + char name[256]; + int i; + + for (i = 0; i < ksyms->sym_cnt; i++) { + if (sscanf(ksyms->syms[i].name, "bpf_prog_%*[^_]_%255s", name) == 1 && + strcmp(name, func_name) == 0) + return true; + } + return false; +} + +static int userns_obj_priv_prog_kallsyms(int mnt_fd, struct token_lsm *lsm_skel) +{ + const char *func_names[] = { "xdp_main", "token_ksym_subprog" }; + LIBBPF_OPTS(bpf_object_open_opts, opts); + struct token_kallsyms *skel; + struct ksyms *ksyms = NULL; + char buf[256]; + int i, err; + + snprintf(buf, sizeof(buf), "/proc/self/fd/%d", mnt_fd); + opts.bpf_token_path = buf; + skel = token_kallsyms__open_opts(&opts); + if (!ASSERT_OK_PTR(skel, "token_kallsyms__open_opts")) + return -EINVAL; + + err = token_kallsyms__load(skel); + if (!ASSERT_OK(err, "token_kallsyms__load")) + goto cleanup; + + ksyms = load_kallsyms_local(); + if (!ASSERT_OK_PTR(ksyms, "load_kallsyms_local")) { + err = -EINVAL; + goto cleanup; + } + + for (i = 0; i < ARRAY_SIZE(func_names); i++) { + if (!ASSERT_TRUE(kallsyms_has_bpf_func(ksyms, func_names[i]), + func_names[i])) { + err = -EINVAL; + break; + } + } + +cleanup: + free_kallsyms_local(ksyms); + token_kallsyms__destroy(skel); + return err; +} + #define bit(n) (1ULL << (n)) static int userns_bpf_token_info(int mnt_fd, struct token_lsm *lsm_skel) @@ -1082,7 +1140,7 @@ static int userns_bpf_token_info(int mnt_fd, struct token_lsm *lsm_skel) return err; } -void test_token(void) +void serial_test_token(void) { if (test__start_subtest("map_token")) { struct bpffs_opts opts = { @@ -1194,4 +1252,23 @@ void test_token(void) subtest_userns(&opts, userns_bpf_token_info); } + if (test__start_subtest("obj_priv_prog_kallsyms")) { + char perf_paranoid_orig[32] = {}; + char kptr_restrict_orig[32] = {}; + struct bpffs_opts opts = { + .cmds = bit(BPF_BTF_LOAD) | bit(BPF_PROG_LOAD), + .progs = bit(BPF_PROG_TYPE_XDP), + .attachs = ~0ULL, + }; + + sysctl_set("/proc/sys/kernel/perf_event_paranoid", perf_paranoid_orig, "0"); + sysctl_set("/proc/sys/kernel/kptr_restrict", kptr_restrict_orig, "0"); + + subtest_userns(&opts, userns_obj_priv_prog_kallsyms); + + if (perf_paranoid_orig[0]) + sysctl_set("/proc/sys/kernel/perf_event_paranoid", NULL, perf_paranoid_orig); + if (kptr_restrict_orig[0]) + sysctl_set("/proc/sys/kernel/kptr_restrict", NULL, kptr_restrict_orig); + } } diff --git a/tools/testing/selftests/bpf/prog_tests/unpriv_bpf_disabled.c b/tools/testing/selftests/bpf/prog_tests/unpriv_bpf_disabled.c index 472f4f9fa95f..64404602b9ab 100644 --- a/tools/testing/selftests/bpf/prog_tests/unpriv_bpf_disabled.c +++ b/tools/testing/selftests/bpf/prog_tests/unpriv_bpf_disabled.c @@ -8,6 +8,7 @@ #include "cap_helpers.h" #include "bpf_util.h" +#include "sysctl_helpers.h" /* Using CAP_LAST_CAP is risky here, since it can get pulled in from * an old /usr/include/linux/capability.h and be < CAP_BPF; as a result @@ -36,26 +37,6 @@ static void process_perfbuf(void *ctx, int cpu, void *data, __u32 len) got_perfbuf_val = *(__u32 *)data; } -static int sysctl_set(const char *sysctl_path, char *old_val, const char *new_val) -{ - int ret = 0; - FILE *fp; - - fp = fopen(sysctl_path, "r+"); - if (!fp) - return -errno; - if (old_val && fscanf(fp, "%s", old_val) <= 0) { - ret = -ENOENT; - } else if (!old_val || strcmp(old_val, new_val) != 0) { - fseek(fp, 0, SEEK_SET); - if (fprintf(fp, "%s", new_val) < 0) - ret = -errno; - } - fclose(fp); - - return ret; -} - static void test_unpriv_bpf_disabled_positive(struct test_unpriv_bpf_disabled *skel, __u32 prog_id, int prog_fd, int perf_fd, char **map_paths, int *map_fds) diff --git a/tools/testing/selftests/bpf/progs/token_kallsyms.c b/tools/testing/selftests/bpf/progs/token_kallsyms.c new file mode 100644 index 000000000000..c9f9344f3eb2 --- /dev/null +++ b/tools/testing/selftests/bpf/progs/token_kallsyms.c @@ -0,0 +1,19 @@ +// SPDX-License-Identifier: GPL-2.0 +/* Copyright (c) 2026 Meta Platforms, Inc. and affiliates. */ + +#include "vmlinux.h" +#include <bpf/bpf_helpers.h> + +char _license[] SEC("license") = "GPL"; + +__weak +int token_ksym_subprog(void) +{ + return 0; +} + +SEC("xdp") +int xdp_main(struct xdp_md *xdp) +{ + return token_ksym_subprog(); +} diff --git a/tools/testing/selftests/bpf/sysctl_helpers.c b/tools/testing/selftests/bpf/sysctl_helpers.c new file mode 100644 index 000000000000..e58c23adaa3e --- /dev/null +++ b/tools/testing/selftests/bpf/sysctl_helpers.c @@ -0,0 +1,26 @@ +// SPDX-License-Identifier: GPL-2.0 +#include <stdio.h> +#include <errno.h> +#include <string.h> + +#include "sysctl_helpers.h" + +int sysctl_set(const char *sysctl_path, char *old_val, const char *new_val) +{ + int ret = 0; + FILE *fp; + + fp = fopen(sysctl_path, "r+"); + if (!fp) + return -errno; + if (old_val && fscanf(fp, "%s", old_val) <= 0) { + ret = -ENOENT; + } else if (!old_val || strcmp(old_val, new_val) != 0) { + fseek(fp, 0, SEEK_SET); + if (fprintf(fp, "%s", new_val) < 0) + ret = -errno; + } + fclose(fp); + + return ret; +} diff --git a/tools/testing/selftests/bpf/sysctl_helpers.h b/tools/testing/selftests/bpf/sysctl_helpers.h new file mode 100644 index 000000000000..28d7e209df72 --- /dev/null +++ b/tools/testing/selftests/bpf/sysctl_helpers.h @@ -0,0 +1,7 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +#ifndef __SYSCTL_HELPERS_H +#define __SYSCTL_HELPERS_H + +int sysctl_set(const char *sysctl_path, char *old_val, const char *new_val); + +#endif -- 2.53.0 ^ permalink raw reply related [flat|nested] 5+ messages in thread
* Re: [PATCH bpf-next v3 2/2] selftests/bpf: verify kallsyms entries for token-loaded subprograms 2026-04-15 18:35 ` [PATCH bpf-next v3 2/2] selftests/bpf: verify kallsyms entries for token-loaded subprograms Eduard Zingerman @ 2026-04-15 19:17 ` sashiko-bot 2026-04-15 19:32 ` Eduard Zingerman 0 siblings, 1 reply; 5+ messages in thread From: sashiko-bot @ 2026-04-15 19:17 UTC (permalink / raw) To: Eduard Zingerman; +Cc: bpf Sashiko AI review found 1 potential issue(s): - [Medium] Unchecked return values of `sysctl_set()` in `serial_test_token()` can lead to confusing test failures. -- commit e7dc629b9f37dc08742ff9bbddeaaf65c1e34956 Author: Eduard Zingerman <eddyz87@gmail.com> selftests/bpf: verify kallsyms entries for token-loaded subprograms This commit adds a test to verify that both the main XDP program and its global subprogram appear in /proc/kallsyms when loaded using a user namespace BPF token. It also introduces a sysctl_set utility to configure system parameters needed to read kallsyms. > diff --git a/tools/testing/selftests/bpf/prog_tests/token.c b/tools/testing/selftests/bpf/prog_tests/token.c > index b81dde283052e..cf5b94512cff4 100644 > --- a/tools/testing/selftests/bpf/prog_tests/token.c > +++ b/tools/testing/selftests/bpf/prog_tests/token.c [ ... ] > @@ -1194,4 +1252,23 @@ void test_token(void) > > subtest_userns(&opts, userns_bpf_token_info); > } > + if (test__start_subtest("obj_priv_prog_kallsyms")) { > + char perf_paranoid_orig[32] = {}; > + char kptr_restrict_orig[32] = {}; > + struct bpffs_opts opts = { > + .cmds = bit(BPF_BTF_LOAD) | bit(BPF_PROG_LOAD), > + .progs = bit(BPF_PROG_TYPE_XDP), > + .attachs = ~0ULL, > + }; > + > + sysctl_set("/proc/sys/kernel/perf_event_paranoid", perf_paranoid_orig, "0"); > + sysctl_set("/proc/sys/kernel/kptr_restrict", kptr_restrict_orig, "0"); Should the return values of these sysctl_set calls be checked? If the test is executed in an environment where these sysctls are read-only or inaccessible (such as inside a container), sysctl_set will silently fail. This would cause load_kallsyms_local to read zeroed addresses because kptr_restrict wasn't successfully disabled, resulting in confusing test failures later at ASSERT_TRUE instead of catching the setup failure early. Would it be better to wrap these with ASSERT_OK? > + > + subtest_userns(&opts, userns_obj_priv_prog_kallsyms); > + > + if (perf_paranoid_orig[0]) > + sysctl_set("/proc/sys/kernel/perf_event_paranoid", NULL, perf_paranoid_orig); > + if (kptr_restrict_orig[0]) > + sysctl_set("/proc/sys/kernel/kptr_restrict", NULL, kptr_restrict_orig); > + } > } -- Sashiko AI review · https://sashiko.dev/#/patchset/20260415-subprog-token-fix-v3-0-6fefe1d51646@gmail.com?part=2 ^ permalink raw reply [flat|nested] 5+ messages in thread
* Re: [PATCH bpf-next v3 2/2] selftests/bpf: verify kallsyms entries for token-loaded subprograms 2026-04-15 19:17 ` sashiko-bot @ 2026-04-15 19:32 ` Eduard Zingerman 0 siblings, 0 replies; 5+ messages in thread From: Eduard Zingerman @ 2026-04-15 19:32 UTC (permalink / raw) To: sashiko; +Cc: bpf On Wed, 2026-04-15 at 19:17 +0000, sashiko-bot@kernel.org wrote: > Sashiko AI review found 1 potential issue(s): > - [Medium] Unchecked return values of `sysctl_set()` in `serial_test_token()` can lead to confusing test failures. > -- > > commit e7dc629b9f37dc08742ff9bbddeaaf65c1e34956 > Author: Eduard Zingerman <eddyz87@gmail.com> > > selftests/bpf: verify kallsyms entries for token-loaded subprograms > > This commit adds a test to verify that both the main XDP program and its > global subprogram appear in /proc/kallsyms when loaded using a user > namespace BPF token. It also introduces a sysctl_set utility to configure > system parameters needed to read kallsyms. > > > diff --git a/tools/testing/selftests/bpf/prog_tests/token.c b/tools/testing/selftests/bpf/prog_tests/token.c > > index b81dde283052e..cf5b94512cff4 100644 > > --- a/tools/testing/selftests/bpf/prog_tests/token.c > > +++ b/tools/testing/selftests/bpf/prog_tests/token.c > > [ ... ] > > > @@ -1194,4 +1252,23 @@ void test_token(void) > > > > subtest_userns(&opts, userns_bpf_token_info); > > } > > + if (test__start_subtest("obj_priv_prog_kallsyms")) { > > + char perf_paranoid_orig[32] = {}; > > + char kptr_restrict_orig[32] = {}; > > + struct bpffs_opts opts = { > > + .cmds = bit(BPF_BTF_LOAD) | bit(BPF_PROG_LOAD), > > + .progs = bit(BPF_PROG_TYPE_XDP), > > + .attachs = ~0ULL, > > + }; > > + > > + sysctl_set("/proc/sys/kernel/perf_event_paranoid", perf_paranoid_orig, "0"); > > + sysctl_set("/proc/sys/kernel/kptr_restrict", kptr_restrict_orig, "0"); > > Should the return values of these sysctl_set calls be checked? > > If the test is executed in an environment where these sysctls are read-only > or inaccessible (such as inside a container), sysctl_set will silently fail. > > This would cause load_kallsyms_local to read zeroed addresses because > kptr_restrict wasn't successfully disabled, resulting in confusing test > failures later at ASSERT_TRUE instead of catching the setup failure early. > > Would it be better to wrap these with ASSERT_OK? Makes sense. > > + > > + subtest_userns(&opts, userns_obj_priv_prog_kallsyms); > > + > > + if (perf_paranoid_orig[0]) > > + sysctl_set("/proc/sys/kernel/perf_event_paranoid", NULL, perf_paranoid_orig); > > + if (kptr_restrict_orig[0]) > > + sysctl_set("/proc/sys/kernel/kptr_restrict", NULL, kptr_restrict_orig); > > + } > > } ^ permalink raw reply [flat|nested] 5+ messages in thread
end of thread, other threads:[~2026-04-15 19:32 UTC | newest] Thread overview: 5+ messages (download: mbox.gz follow: Atom feed -- links below jump to the message on this page -- 2026-04-15 18:35 [PATCH bpf-next v3 0/2] bpf: copy BPF token from main program to subprograms Eduard Zingerman 2026-04-15 18:35 ` [PATCH bpf-next v3 1/2] " Eduard Zingerman 2026-04-15 18:35 ` [PATCH bpf-next v3 2/2] selftests/bpf: verify kallsyms entries for token-loaded subprograms Eduard Zingerman 2026-04-15 19:17 ` sashiko-bot 2026-04-15 19:32 ` Eduard Zingerman
This is a public inbox, see mirroring instructions for how to clone and mirror all data and code used for this inbox