History

bpf: selftests: Add selftests for module kfunc support

This adds selftests that tests the success and failure path for modules kfuncs (in presence of invalid kfunc calls) for both libbpf and gen_loader. It also adds a prog_test kfunc_btf_id_list so that we can add module BTF ID set from bpf_testmod. This also introduces a couple of test cases to verifier selftests for validating whether we get an error or not depending on if invalid kfunc call remains after elimination of unreachable instructions.

Kumar Kartikeya Dwivedi committed 5 years ago

Commit c48e51c1 parent 18f4fcc9 files · +132 −31Browse files

Unified · Split

9 files
  1. include/linux/btf.h +1 −0
  2. kernel/bpf/btf.c +1 −0
  3. net/bpf/test_run.c +4 −1
  4. tools/testing/selftests/bpf/Makefile +4 −3
  5. tools/testing/selftests/bpf/bpf_testmod/bpf_testmod.c +22 −1
  6. tools/testing/selftests/bpf/prog_tests/ksyms_module.c +14 −15
  7. tools/testing/selftests/bpf/prog_tests/ksyms_module_libbpf.c +28 −0
  8. tools/testing/selftests/bpf/progs/test_ksyms_module.c +35 −11
  9. tools/testing/selftests/bpf/verifier/calls.c +23 −0
include/linux/btf.h+1−0
@@ -275,5 +275,6 @@ static inline bool bpf_check_mod_kfunc_call(struct kfunc_btf_id_list *klist, · 274 unmodified lines
275275 THIS_MODULE }
276276
277277 extern struct kfunc_btf_id_list bpf_tcp_ca_kfunc_list;
278+extern struct kfunc_btf_id_list prog_test_kfunc_list;
278279
279280 #endif
kernel/bpf/btf.c+1−0
@@ -6397,3 +6397,4 @@ bool bpf_check_mod_kfunc_call(struct kfunc_btf_id_list *klist, u32 kfunc_id, · 6396 unmodified lines
63976397 EXPORT_SYMBOL_GPL(name)
63986398
63996399 DEFINE_KFUNC_BTF_ID_LIST(bpf_tcp_ca_kfunc_list);
6400+DEFINE_KFUNC_BTF_ID_LIST(prog_test_kfunc_list);
net/bpf/test_run.c+4−1
@@ -2,6 +2,7 @@ · 1 unmodified line
22 /* Copyright (c) 2017 Facebook
33 */
44 #include <linux/bpf.h>
5+#include <linux/btf.h>
56 #include <linux/btf_ids.h>
67 #include <linux/slab.h>
78 #include <linux/vmalloc.h>
@@ -243,7 +244,9 @@ BTF_SET_END(test_sk_kfunc_ids) · 235 unmodified lines
243244
244245 bool bpf_prog_test_check_kfunc_call(u32 kfunc_id, struct module *owner)
245246 {
246− return btf_id_set_contains(&test_sk_kfunc_ids, kfunc_id);
247+ if (btf_id_set_contains(&test_sk_kfunc_ids, kfunc_id))
248+ return true;
249+ return bpf_check_mod_kfunc_call(&prog_test_kfunc_list, kfunc_id, owner);
247250 }
248251
249252 static void *bpf_test_init(const union bpf_attr *kattr, u32 size,
tools/testing/selftests/bpf/Makefile+4−3
@@ -315,8 +315,9 @@ LINKED_SKELS := test_static_linked.skel.h linked_funcs.skel.h \ · 314 unmodified lines
315315 linked_vars.skel.h linked_maps.skel.h
316316
317317 LSKELS := kfunc_call_test.c fentry_test.c fexit_test.c fexit_sleep.c \
318− test_ksyms_module.c test_ringbuf.c atomics.c trace_printk.c \
319− trace_vprintk.c
318+ test_ringbuf.c atomics.c trace_printk.c trace_vprintk.c
319+# Generate both light skeleton and libbpf skeleton for these
320+LSKELS_EXTRA := test_ksyms_module.c
320321 SKEL_BLACKLIST += $$(LSKELS)
321322
322323 test_static_linked.skel.h-deps := test_static_linked1.o test_static_linked2.o
@@ -346,7 +347,7 @@ TRUNNER_BPF_OBJS := $$(patsubst %.c,$$(TRUNNER_OUTPUT)/%.o, $$(TRUNNER_BPF_SRCS) · 23 unmodified lines
346347 TRUNNER_BPF_SKELS := $$(patsubst %.c,$$(TRUNNER_OUTPUT)/%.skel.h, \
347348 $$(filter-out $(SKEL_BLACKLIST) $(LINKED_BPF_SRCS),\
348349 $$(TRUNNER_BPF_SRCS)))
349−TRUNNER_BPF_LSKELS := $$(patsubst %.c,$$(TRUNNER_OUTPUT)/%.lskel.h, $$(LSKELS))
350+TRUNNER_BPF_LSKELS := $$(patsubst %.c,$$(TRUNNER_OUTPUT)/%.lskel.h, $$(LSKELS) $$(LSKELS_EXTRA))
350351 TRUNNER_BPF_SKELS_LINKED := $$(addprefix $$(TRUNNER_OUTPUT)/,$(LINKED_SKELS))
351352 TEST_GEN_FILES += $$(TRUNNER_BPF_OBJS)
352353
tools/testing/selftests/bpf/bpf_testmod/bpf_testmod.c+22−1
@@ -1,5 +1,7 @@
11 // SPDX-License-Identifier: GPL-2.0
22 /* Copyright (c) 2020 Facebook */
3+#include <linux/btf.h>
4+#include <linux/btf_ids.h>
35 #include <linux/error-injection.h>
46 #include <linux/init.h>
57 #include <linux/module.h>
@@ -13,6 +15,12 @@ · 7 unmodified lines
1315
1416 DEFINE_PER_CPU(int, bpf_testmod_ksym_percpu) = 123;
1517
18+noinline void
19+bpf_testmod_test_mod_kfunc(int i)
20+{
21+ *(int *)this_cpu_ptr(&bpf_testmod_ksym_percpu) = i;
22+}
23+
1624 noinline int bpf_testmod_loop_test(int n)
1725 {
1826 int i, sum = 0;
@@ -71,13 +79,26 @@ static struct bin_attribute bin_attr_bpf_testmod_file __ro_after_init = { · 52 unmodified lines
7179 .write = bpf_testmod_test_write,
7280 };
7381
82+BTF_SET_START(bpf_testmod_kfunc_ids)
83+BTF_ID(func, bpf_testmod_test_mod_kfunc)
84+BTF_SET_END(bpf_testmod_kfunc_ids)
85+
86+static DEFINE_KFUNC_BTF_ID_SET(&bpf_testmod_kfunc_ids, bpf_testmod_kfunc_btf_set);
87+
7488 static int bpf_testmod_init(void)
7589 {
76− return sysfs_create_bin_file(kernel_kobj, &bin_attr_bpf_testmod_file);
90+ int ret;
91+
92+ ret = sysfs_create_bin_file(kernel_kobj, &bin_attr_bpf_testmod_file);
93+ if (ret)
94+ return ret;
95+ register_kfunc_btf_id_set(&prog_test_kfunc_list, &bpf_testmod_kfunc_btf_set);
96+ return 0;
7797 }
7898
7999 static void bpf_testmod_exit(void)
80100 {
101+ unregister_kfunc_btf_id_set(&prog_test_kfunc_list, &bpf_testmod_kfunc_btf_set);
81102 return sysfs_remove_bin_file(kernel_kobj, &bin_attr_bpf_testmod_file);
82103 }
83104
tools/testing/selftests/bpf/prog_tests/ksyms_module.c+14−15
@@ -2,30 +2,29 @@ · 1 unmodified line
22 /* Copyright (c) 2021 Facebook */
33
44 #include <test_progs.h>
5−#include <bpf/libbpf.h>
6−#include <bpf/btf.h>
5+#include <network_helpers.h>
76 #include "test_ksyms_module.lskel.h"
87
9−static int duration;
10−
118 void test_ksyms_module(void)
129 {
13− struct test_ksyms_module* skel;
10+ struct test_ksyms_module *skel;
11+ int retval;
1412 int err;
1513
16− skel = test_ksyms_module__open_and_load();
17− if (CHECK(!skel, "skel_open", "failed to open skeleton\n"))
14+ if (!env.has_testmod) {
15+ test__skip();
1816 return;
17+ }
1918
20− err = test_ksyms_module__attach(skel);
21− if (CHECK(err, "skel_attach", "skeleton attach failed: %d\n", err))
19+ skel = test_ksyms_module__open_and_load();
20+ if (!ASSERT_OK_PTR(skel, "test_ksyms_module__open_and_load"))
21+ return;
22+ err = bpf_prog_test_run(skel->progs.load.prog_fd, 1, &pkt_v4, sizeof(pkt_v4),
23+ NULL, NULL, (__u32 *)&retval, NULL);
24+ if (!ASSERT_OK(err, "bpf_prog_test_run"))
2225 goto cleanup;
23−
24− usleep(1);
25−
26− ASSERT_EQ(skel->bss->triggered, true, "triggered");
27− ASSERT_EQ(skel->bss->out_mod_ksym_global, 123, "global_ksym_val");
28−
26+ ASSERT_EQ(retval, 0, "retval");
27+ ASSERT_EQ(skel->bss->out_bpf_testmod_ksym, 42, "bpf_testmod_ksym");
2928 cleanup:
3029 test_ksyms_module__destroy(skel);
3130 }
tools/testing/selftests/bpf/prog_tests/ksyms_module_libbpf.c+28−0
@@ -0,0 +1,28 @@
1+// SPDX-License-Identifier: GPL-2.0
2+
3+#include <test_progs.h>
4+#include <network_helpers.h>
5+#include "test_ksyms_module.skel.h"
6+
7+void test_ksyms_module_libbpf(void)
8+{
9+ struct test_ksyms_module *skel;
10+ int retval, err;
11+
12+ if (!env.has_testmod) {
13+ test__skip();
14+ return;
15+ }
16+
17+ skel = test_ksyms_module__open_and_load();
18+ if (!ASSERT_OK_PTR(skel, "test_ksyms_module__open"))
19+ return;
20+ err = bpf_prog_test_run(bpf_program__fd(skel->progs.load), 1, &pkt_v4,
21+ sizeof(pkt_v4), NULL, NULL, (__u32 *)&retval, NULL);
22+ if (!ASSERT_OK(err, "bpf_prog_test_run"))
23+ goto cleanup;
24+ ASSERT_EQ(retval, 0, "retval");
25+ ASSERT_EQ(skel->bss->out_bpf_testmod_ksym, 42, "bpf_testmod_ksym");
26+cleanup:
27+ test_ksyms_module__destroy(skel);
28+}
tools/testing/selftests/bpf/progs/test_ksyms_module.c+35−11
@@ -2,24 +2,48 @@ · 1 unmodified line
22 /* Copyright (c) 2021 Facebook */
33
44 #include "vmlinux.h"
5−
65 #include <bpf/bpf_helpers.h>
76
7+#define X_0(x)
8+#define X_1(x) x X_0(x)
9+#define X_2(x) x X_1(x)
10+#define X_3(x) x X_2(x)
11+#define X_4(x) x X_3(x)
12+#define X_5(x) x X_4(x)
13+#define X_6(x) x X_5(x)
14+#define X_7(x) x X_6(x)
15+#define X_8(x) x X_7(x)
16+#define X_9(x) x X_8(x)
17+#define X_10(x) x X_9(x)
18+#define REPEAT_256(Y) X_2(X_10(X_10(Y))) X_5(X_10(Y)) X_6(Y)
19+
820 extern const int bpf_testmod_ksym_percpu __ksym;
21+extern void bpf_testmod_test_mod_kfunc(int i) __ksym;
22+extern void bpf_testmod_invalid_mod_kfunc(void) __ksym __weak;
923
10−int out_mod_ksym_global = 0;
11−bool triggered = false;
24+int out_bpf_testmod_ksym = 0;
25+const volatile int x = 0;
1226
13−SEC("raw_tp/sys_enter")
14−int handler(const void *ctx)
27+SEC("tc")
28+int load(struct __sk_buff *skb)
1529 {
16− int *val;
17− __u32 cpu;
18−
19− val = (int *)bpf_this_cpu_ptr(&bpf_testmod_ksym_percpu);
20− out_mod_ksym_global = *val;
21− triggered = true;
30+ /* This will be kept by clang, but removed by verifier. Since it is
31+ * marked as __weak, libbpf and gen_loader don't error out if BTF ID
32+ * is not found for it, instead imm and off is set to 0 for it.
33+ */
34+ if (x)
35+ bpf_testmod_invalid_mod_kfunc();
36+ bpf_testmod_test_mod_kfunc(42);
37+ out_bpf_testmod_ksym = *(int *)bpf_this_cpu_ptr(&bpf_testmod_ksym_percpu);
38+ return 0;
39+}
2240
41+SEC("tc")
42+int load_256(struct __sk_buff *skb)
43+{
44+ /* this will fail if kfunc doesn't reuse its own btf fd index */
45+ REPEAT_256(bpf_testmod_test_mod_kfunc(42););
46+ bpf_testmod_test_mod_kfunc(42);
2347 return 0;
2448 }
2549
tools/testing/selftests/bpf/verifier/calls.c+23−0
@@ -1,3 +1,26 @@
1+{
2+ "calls: invalid kfunc call not eliminated",
3+ .insns = {
4+ BPF_RAW_INSN(BPF_JMP | BPF_CALL, 0, BPF_PSEUDO_KFUNC_CALL, 0, 0),
5+ BPF_MOV64_IMM(BPF_REG_0, 1),
6+ BPF_EXIT_INSN(),
7+ },
8+ .prog_type = BPF_PROG_TYPE_TRACEPOINT,
9+ .result = REJECT,
10+ .errstr = "invalid kernel function call not eliminated in verifier pass",
11+},
12+{
13+ "calls: invalid kfunc call unreachable",
14+ .insns = {
15+ BPF_MOV64_IMM(BPF_REG_0, 1),
16+ BPF_JMP_IMM(BPF_JGT, BPF_REG_0, 0, 2),
17+ BPF_RAW_INSN(BPF_JMP | BPF_CALL, 0, BPF_PSEUDO_KFUNC_CALL, 0, 0),
18+ BPF_MOV64_IMM(BPF_REG_0, 1),
19+ BPF_EXIT_INSN(),
20+ },
21+ .prog_type = BPF_PROG_TYPE_TRACEPOINT,
22+ .result = ACCEPT,
23+},
124 {
225 "calls: basic sanity",
326 .insns = {