Skip to content

Commit 3f2ac59

Browse files
author
Alexei Starovoitov
committed
Merge branch 'fix-caching-of-btf-for-kfuncs-in-the-verifier'
Toke Høiland-Jørgensen says: ==================== Fix caching of BTF for kfuncs in the verifier When playing around with defining kfuncs in some custom modules, we noticed that if a BPF program calls two functions with the same signature in two different modules, the function from the wrong module may sometimes end up being called. Whether this happens depends on the order of the calls in the BPF program, which turns out to be due to the use of sort() inside __find_kfunc_desc_btf() in the verifier code. This series contains a fix for the issue (first patch), and a selftest to trigger it (last patch). The middle commit is a small refactor to expose the module loading helper functions in testing_helpers.c. See the individual patch descriptions for more details. Changes in v2: - Drop patch that refactors module building in selftests (Alexei) - Get rid of expect_val function argument in selftest (Jiri) - Collect ACKs - Link to v1: https://lore.kernel.org/r/20241008-fix-kfunc-btf-caching-for-modules-v1-0-dfefd9aa4318@redhat.com ==================== Link: https://lore.kernel.org/r/20241010-fix-kfunc-btf-caching-for-modules-v2-0-745af6c1af98@redhat.com Signed-off-by: Alexei Starovoitov <ast@kernel.org>
2 parents 60f802e + f91b256 commit 3f2ac59

10 files changed

Lines changed: 251 additions & 14 deletions

File tree

kernel/bpf/verifier.c

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2750,10 +2750,16 @@ static struct btf *__find_kfunc_desc_btf(struct bpf_verifier_env *env,
27502750
b->module = mod;
27512751
b->offset = offset;
27522752

2753+
/* sort() reorders entries by value, so b may no longer point
2754+
* to the right entry after this
2755+
*/
27532756
sort(tab->descs, tab->nr_descs, sizeof(tab->descs[0]),
27542757
kfunc_btf_cmp_by_off, NULL);
2758+
} else {
2759+
btf = b->btf;
27552760
}
2756-
return b->btf;
2761+
2762+
return btf;
27572763
}
27582764

27592765
void bpf_free_kfunc_btf_tab(struct bpf_kfunc_btf_tab *tab)

tools/testing/selftests/bpf/Makefile

Lines changed: 19 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -157,7 +157,8 @@ TEST_GEN_PROGS_EXTENDED = \
157157
flow_dissector_load test_flow_dissector test_tcp_check_syncookie_user \
158158
test_lirc_mode2_user xdping test_cpp runqslower bench bpf_testmod.ko \
159159
xskxceiver xdp_redirect_multi xdp_synproxy veristat xdp_hw_metadata \
160-
xdp_features bpf_test_no_cfi.ko
160+
xdp_features bpf_test_no_cfi.ko bpf_test_modorder_x.ko \
161+
bpf_test_modorder_y.ko
161162

162163
TEST_GEN_FILES += liburandom_read.so urandom_read sign-file uprobe_multi
163164

@@ -303,6 +304,19 @@ $(OUTPUT)/bpf_test_no_cfi.ko: $(VMLINUX_BTF) $(RESOLVE_BTFIDS) $(wildcard bpf_te
303304
$(Q)$(MAKE) $(submake_extras) RESOLVE_BTFIDS=$(RESOLVE_BTFIDS) -C bpf_test_no_cfi
304305
$(Q)cp bpf_test_no_cfi/bpf_test_no_cfi.ko $@
305306

307+
$(OUTPUT)/bpf_test_modorder_x.ko: $(VMLINUX_BTF) $(RESOLVE_BTFIDS) $(wildcard bpf_test_modorder_x/Makefile bpf_test_modorder_x/*.[ch])
308+
$(call msg,MOD,,$@)
309+
$(Q)$(RM) bpf_test_modorder_x/bpf_test_modorder_x.ko # force re-compilation
310+
$(Q)$(MAKE) $(submake_extras) RESOLVE_BTFIDS=$(RESOLVE_BTFIDS) -C bpf_test_modorder_x
311+
$(Q)cp bpf_test_modorder_x/bpf_test_modorder_x.ko $@
312+
313+
$(OUTPUT)/bpf_test_modorder_y.ko: $(VMLINUX_BTF) $(RESOLVE_BTFIDS) $(wildcard bpf_test_modorder_y/Makefile bpf_test_modorder_y/*.[ch])
314+
$(call msg,MOD,,$@)
315+
$(Q)$(RM) bpf_test_modorder_y/bpf_test_modorder_y.ko # force re-compilation
316+
$(Q)$(MAKE) $(submake_extras) RESOLVE_BTFIDS=$(RESOLVE_BTFIDS) -C bpf_test_modorder_y
317+
$(Q)cp bpf_test_modorder_y/bpf_test_modorder_y.ko $@
318+
319+
306320
DEFAULT_BPFTOOL := $(HOST_SCRATCH_DIR)/sbin/bpftool
307321
ifneq ($(CROSS_COMPILE),)
308322
CROSS_BPFTOOL := $(SCRATCH_DIR)/sbin/bpftool
@@ -722,6 +736,8 @@ TRUNNER_EXTRA_SOURCES := test_progs.c \
722736
ip_check_defrag_frags.h
723737
TRUNNER_EXTRA_FILES := $(OUTPUT)/urandom_read $(OUTPUT)/bpf_testmod.ko \
724738
$(OUTPUT)/bpf_test_no_cfi.ko \
739+
$(OUTPUT)/bpf_test_modorder_x.ko \
740+
$(OUTPUT)/bpf_test_modorder_y.ko \
725741
$(OUTPUT)/liburandom_read.so \
726742
$(OUTPUT)/xdp_synproxy \
727743
$(OUTPUT)/sign-file \
@@ -856,6 +872,8 @@ EXTRA_CLEAN := $(SCRATCH_DIR) $(HOST_SCRATCH_DIR) \
856872
$(addprefix $(OUTPUT)/,*.o *.d *.skel.h *.lskel.h *.subskel.h \
857873
no_alu32 cpuv4 bpf_gcc bpf_testmod.ko \
858874
bpf_test_no_cfi.ko \
875+
bpf_test_modorder_x.ko \
876+
bpf_test_modorder_y.ko \
859877
liburandom_read.so) \
860878
$(OUTPUT)/FEATURE-DUMP.selftests
861879

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
BPF_TESTMOD_DIR := $(realpath $(dir $(abspath $(lastword $(MAKEFILE_LIST)))))
2+
KDIR ?= $(abspath $(BPF_TESTMOD_DIR)/../../../../..)
3+
4+
ifeq ($(V),1)
5+
Q =
6+
else
7+
Q = @
8+
endif
9+
10+
MODULES = bpf_test_modorder_x.ko
11+
12+
obj-m += bpf_test_modorder_x.o
13+
14+
all:
15+
+$(Q)make -C $(KDIR) M=$(BPF_TESTMOD_DIR) modules
16+
17+
clean:
18+
+$(Q)make -C $(KDIR) M=$(BPF_TESTMOD_DIR) clean
19+
Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
// SPDX-License-Identifier: GPL-2.0
2+
#include <linux/bpf.h>
3+
#include <linux/btf.h>
4+
#include <linux/module.h>
5+
#include <linux/init.h>
6+
7+
__bpf_kfunc_start_defs();
8+
9+
__bpf_kfunc int bpf_test_modorder_retx(void)
10+
{
11+
return 'x';
12+
}
13+
14+
__bpf_kfunc_end_defs();
15+
16+
BTF_KFUNCS_START(bpf_test_modorder_kfunc_x_ids)
17+
BTF_ID_FLAGS(func, bpf_test_modorder_retx);
18+
BTF_KFUNCS_END(bpf_test_modorder_kfunc_x_ids)
19+
20+
static const struct btf_kfunc_id_set bpf_test_modorder_x_set = {
21+
.owner = THIS_MODULE,
22+
.set = &bpf_test_modorder_kfunc_x_ids,
23+
};
24+
25+
static int __init bpf_test_modorder_x_init(void)
26+
{
27+
return register_btf_kfunc_id_set(BPF_PROG_TYPE_SCHED_CLS,
28+
&bpf_test_modorder_x_set);
29+
}
30+
31+
static void __exit bpf_test_modorder_x_exit(void)
32+
{
33+
}
34+
35+
module_init(bpf_test_modorder_x_init);
36+
module_exit(bpf_test_modorder_x_exit);
37+
38+
MODULE_DESCRIPTION("BPF selftest ordertest module X");
39+
MODULE_LICENSE("GPL");
Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
BPF_TESTMOD_DIR := $(realpath $(dir $(abspath $(lastword $(MAKEFILE_LIST)))))
2+
KDIR ?= $(abspath $(BPF_TESTMOD_DIR)/../../../../..)
3+
4+
ifeq ($(V),1)
5+
Q =
6+
else
7+
Q = @
8+
endif
9+
10+
MODULES = bpf_test_modorder_y.ko
11+
12+
obj-m += bpf_test_modorder_y.o
13+
14+
all:
15+
+$(Q)make -C $(KDIR) M=$(BPF_TESTMOD_DIR) modules
16+
17+
clean:
18+
+$(Q)make -C $(KDIR) M=$(BPF_TESTMOD_DIR) clean
19+
Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
// SPDX-License-Identifier: GPL-2.0
2+
#include <linux/bpf.h>
3+
#include <linux/btf.h>
4+
#include <linux/module.h>
5+
#include <linux/init.h>
6+
7+
__bpf_kfunc_start_defs();
8+
9+
__bpf_kfunc int bpf_test_modorder_rety(void)
10+
{
11+
return 'y';
12+
}
13+
14+
__bpf_kfunc_end_defs();
15+
16+
BTF_KFUNCS_START(bpf_test_modorder_kfunc_y_ids)
17+
BTF_ID_FLAGS(func, bpf_test_modorder_rety);
18+
BTF_KFUNCS_END(bpf_test_modorder_kfunc_y_ids)
19+
20+
static const struct btf_kfunc_id_set bpf_test_modorder_y_set = {
21+
.owner = THIS_MODULE,
22+
.set = &bpf_test_modorder_kfunc_y_ids,
23+
};
24+
25+
static int __init bpf_test_modorder_y_init(void)
26+
{
27+
return register_btf_kfunc_id_set(BPF_PROG_TYPE_SCHED_CLS,
28+
&bpf_test_modorder_y_set);
29+
}
30+
31+
static void __exit bpf_test_modorder_y_exit(void)
32+
{
33+
}
34+
35+
module_init(bpf_test_modorder_y_init);
36+
module_exit(bpf_test_modorder_y_exit);
37+
38+
MODULE_DESCRIPTION("BPF selftest ordertest module Y");
39+
MODULE_LICENSE("GPL");
Lines changed: 55 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,55 @@
1+
// SPDX-License-Identifier: GPL-2.0
2+
#include <test_progs.h>
3+
#include <testing_helpers.h>
4+
5+
#include "kfunc_module_order.skel.h"
6+
7+
static int test_run_prog(const struct bpf_program *prog,
8+
struct bpf_test_run_opts *opts)
9+
{
10+
int err;
11+
12+
err = bpf_prog_test_run_opts(bpf_program__fd(prog), opts);
13+
if (!ASSERT_OK(err, "bpf_prog_test_run_opts"))
14+
return err;
15+
16+
if (!ASSERT_EQ((int)opts->retval, 0, bpf_program__name(prog)))
17+
return -EINVAL;
18+
19+
return 0;
20+
}
21+
22+
void test_kfunc_module_order(void)
23+
{
24+
struct kfunc_module_order *skel;
25+
char pkt_data[64] = {};
26+
int err = 0;
27+
28+
DECLARE_LIBBPF_OPTS(bpf_test_run_opts, test_opts, .data_in = pkt_data,
29+
.data_size_in = sizeof(pkt_data));
30+
31+
err = load_module("bpf_test_modorder_x.ko",
32+
env_verbosity > VERBOSE_NONE);
33+
if (!ASSERT_OK(err, "load bpf_test_modorder_x.ko"))
34+
return;
35+
36+
err = load_module("bpf_test_modorder_y.ko",
37+
env_verbosity > VERBOSE_NONE);
38+
if (!ASSERT_OK(err, "load bpf_test_modorder_y.ko"))
39+
goto exit_modx;
40+
41+
skel = kfunc_module_order__open_and_load();
42+
if (!ASSERT_OK_PTR(skel, "kfunc_module_order__open_and_load()")) {
43+
err = -EINVAL;
44+
goto exit_mods;
45+
}
46+
47+
test_run_prog(skel->progs.call_kfunc_xy, &test_opts);
48+
test_run_prog(skel->progs.call_kfunc_yx, &test_opts);
49+
50+
kfunc_module_order__destroy(skel);
51+
exit_mods:
52+
unload_module("bpf_test_modorder_y", env_verbosity > VERBOSE_NONE);
53+
exit_modx:
54+
unload_module("bpf_test_modorder_x", env_verbosity > VERBOSE_NONE);
55+
}
Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
// SPDX-License-Identifier: GPL-2.0
2+
#include <linux/bpf.h>
3+
#include <bpf/bpf_helpers.h>
4+
5+
extern int bpf_test_modorder_retx(void) __ksym;
6+
extern int bpf_test_modorder_rety(void) __ksym;
7+
8+
SEC("classifier")
9+
int call_kfunc_xy(struct __sk_buff *skb)
10+
{
11+
int ret1, ret2;
12+
13+
ret1 = bpf_test_modorder_retx();
14+
ret2 = bpf_test_modorder_rety();
15+
16+
return ret1 == 'x' && ret2 == 'y' ? 0 : -1;
17+
}
18+
19+
SEC("classifier")
20+
int call_kfunc_yx(struct __sk_buff *skb)
21+
{
22+
int ret1, ret2;
23+
24+
ret1 = bpf_test_modorder_rety();
25+
ret2 = bpf_test_modorder_retx();
26+
27+
return ret1 == 'y' && ret2 == 'x' ? 0 : -1;
28+
}
29+
30+
char _license[] SEC("license") = "GPL";

tools/testing/selftests/bpf/testing_helpers.c

Lines changed: 22 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -367,19 +367,19 @@ int delete_module(const char *name, int flags)
367367
return syscall(__NR_delete_module, name, flags);
368368
}
369369

370-
int unload_bpf_testmod(bool verbose)
370+
int unload_module(const char *name, bool verbose)
371371
{
372372
int ret, cnt = 0;
373373

374374
if (kern_sync_rcu())
375375
fprintf(stdout, "Failed to trigger kernel-side RCU sync!\n");
376376

377377
for (;;) {
378-
ret = delete_module("bpf_testmod", 0);
378+
ret = delete_module(name, 0);
379379
if (!ret || errno != EAGAIN)
380380
break;
381381
if (++cnt > 10000) {
382-
fprintf(stdout, "Unload of bpf_testmod timed out\n");
382+
fprintf(stdout, "Unload of %s timed out\n", name);
383383
break;
384384
}
385385
usleep(100);
@@ -388,41 +388,51 @@ int unload_bpf_testmod(bool verbose)
388388
if (ret) {
389389
if (errno == ENOENT) {
390390
if (verbose)
391-
fprintf(stdout, "bpf_testmod.ko is already unloaded.\n");
391+
fprintf(stdout, "%s.ko is already unloaded.\n", name);
392392
return -1;
393393
}
394-
fprintf(stdout, "Failed to unload bpf_testmod.ko from kernel: %d\n", -errno);
394+
fprintf(stdout, "Failed to unload %s.ko from kernel: %d\n", name, -errno);
395395
return -1;
396396
}
397397
if (verbose)
398-
fprintf(stdout, "Successfully unloaded bpf_testmod.ko.\n");
398+
fprintf(stdout, "Successfully unloaded %s.ko.\n", name);
399399
return 0;
400400
}
401401

402-
int load_bpf_testmod(bool verbose)
402+
int load_module(const char *path, bool verbose)
403403
{
404404
int fd;
405405

406406
if (verbose)
407-
fprintf(stdout, "Loading bpf_testmod.ko...\n");
407+
fprintf(stdout, "Loading %s...\n", path);
408408

409-
fd = open("bpf_testmod.ko", O_RDONLY);
409+
fd = open(path, O_RDONLY);
410410
if (fd < 0) {
411-
fprintf(stdout, "Can't find bpf_testmod.ko kernel module: %d\n", -errno);
411+
fprintf(stdout, "Can't find %s kernel module: %d\n", path, -errno);
412412
return -ENOENT;
413413
}
414414
if (finit_module(fd, "", 0)) {
415-
fprintf(stdout, "Failed to load bpf_testmod.ko into the kernel: %d\n", -errno);
415+
fprintf(stdout, "Failed to load %s into the kernel: %d\n", path, -errno);
416416
close(fd);
417417
return -EINVAL;
418418
}
419419
close(fd);
420420

421421
if (verbose)
422-
fprintf(stdout, "Successfully loaded bpf_testmod.ko.\n");
422+
fprintf(stdout, "Successfully loaded %s.\n", path);
423423
return 0;
424424
}
425425

426+
int unload_bpf_testmod(bool verbose)
427+
{
428+
return unload_module("bpf_testmod", verbose);
429+
}
430+
431+
int load_bpf_testmod(bool verbose)
432+
{
433+
return load_module("bpf_testmod.ko", verbose);
434+
}
435+
426436
/*
427437
* Trigger synchronize_rcu() in kernel.
428438
*/

tools/testing/selftests/bpf/testing_helpers.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,8 @@ int unload_bpf_testmod(bool verbose);
3838
int kern_sync_rcu(void);
3939
int finit_module(int fd, const char *param_values, int flags);
4040
int delete_module(const char *name, int flags);
41+
int load_module(const char *path, bool verbose);
42+
int unload_module(const char *name, bool verbose);
4143

4244
static inline __u64 get_time_ns(void)
4345
{

0 commit comments

Comments
 (0)