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
275 THIS_MODULE }275 THIS_MODULE }
276 276
277extern struct kfunc_btf_id_list bpf_tcp_ca_kfunc_list;277extern struct kfunc_btf_id_list bpf_tcp_ca_kfunc_list;
278extern struct kfunc_btf_id_list prog_test_kfunc_list;
278 279
279#endif280#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
6397 EXPORT_SYMBOL_GPL(name)6397 EXPORT_SYMBOL_GPL(name)
6398 6398
6399DEFINE_KFUNC_BTF_ID_LIST(bpf_tcp_ca_kfunc_list);6399DEFINE_KFUNC_BTF_ID_LIST(bpf_tcp_ca_kfunc_list);
6400DEFINE_KFUNC_BTF_ID_LIST(prog_test_kfunc_list);
net/bpf/test_run.c+4−1
@@ -2,6 +2,7 @@ · 1 unmodified line
2/* Copyright (c) 2017 Facebook2/* Copyright (c) 2017 Facebook
3 */3 */
4#include <linux/bpf.h>4#include <linux/bpf.h>
5#include <linux/btf.h>
5#include <linux/btf_ids.h>6#include <linux/btf_ids.h>
6#include <linux/slab.h>7#include <linux/slab.h>
7#include <linux/vmalloc.h>8#include <linux/vmalloc.h>
@@ -243,7 +244,9 @@ BTF_SET_END(test_sk_kfunc_ids) · 235 unmodified lines
243 244
244bool bpf_prog_test_check_kfunc_call(u32 kfunc_id, struct module *owner)245bool bpf_prog_test_check_kfunc_call(u32 kfunc_id, struct module *owner)
245{246{
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);
247}250}
248 251
249static void *bpf_test_init(const union bpf_attr *kattr, u32 size,252static 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
315 linked_vars.skel.h linked_maps.skel.h315 linked_vars.skel.h linked_maps.skel.h
316 316
317LSKELS := kfunc_call_test.c fentry_test.c fexit_test.c fexit_sleep.c \317LSKELS := 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 \318 test_ringbuf.c atomics.c trace_printk.c trace_vprintk.c
319 trace_vprintk.c319# Generate both light skeleton and libbpf skeleton for these
320LSKELS_EXTRA := test_ksyms_module.c
320SKEL_BLACKLIST += $$(LSKELS)321SKEL_BLACKLIST += $$(LSKELS)
321 322
322test_static_linked.skel.h-deps := test_static_linked1.o test_static_linked2.o323test_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
346TRUNNER_BPF_SKELS := $$(patsubst %.c,$$(TRUNNER_OUTPUT)/%.skel.h, \347TRUNNER_BPF_SKELS := $$(patsubst %.c,$$(TRUNNER_OUTPUT)/%.skel.h, \
347 $$(filter-out $(SKEL_BLACKLIST) $(LINKED_BPF_SRCS),\348 $$(filter-out $(SKEL_BLACKLIST) $(LINKED_BPF_SRCS),\
348 $$(TRUNNER_BPF_SRCS)))349 $$(TRUNNER_BPF_SRCS)))
349TRUNNER_BPF_LSKELS := $$(patsubst %.c,$$(TRUNNER_OUTPUT)/%.lskel.h, $$(LSKELS))350TRUNNER_BPF_LSKELS := $$(patsubst %.c,$$(TRUNNER_OUTPUT)/%.lskel.h, $$(LSKELS) $$(LSKELS_EXTRA))
350TRUNNER_BPF_SKELS_LINKED := $$(addprefix $$(TRUNNER_OUTPUT)/,$(LINKED_SKELS))351TRUNNER_BPF_SKELS_LINKED := $$(addprefix $$(TRUNNER_OUTPUT)/,$(LINKED_SKELS))
351TEST_GEN_FILES += $$(TRUNNER_BPF_OBJS)352TEST_GEN_FILES += $$(TRUNNER_BPF_OBJS)
352 353
tools/testing/selftests/bpf/bpf_testmod/bpf_testmod.c+22−1
@@ -1,5 +1,7 @@
1// SPDX-License-Identifier: GPL-2.01// SPDX-License-Identifier: GPL-2.0
2/* Copyright (c) 2020 Facebook */2/* Copyright (c) 2020 Facebook */
3#include <linux/btf.h>
4#include <linux/btf_ids.h>
3#include <linux/error-injection.h>5#include <linux/error-injection.h>
4#include <linux/init.h>6#include <linux/init.h>
5#include <linux/module.h>7#include <linux/module.h>
@@ -13,6 +15,12 @@ · 7 unmodified lines
13 15
14DEFINE_PER_CPU(int, bpf_testmod_ksym_percpu) = 123;16DEFINE_PER_CPU(int, bpf_testmod_ksym_percpu) = 123;
15 17
18noinline void
19bpf_testmod_test_mod_kfunc(int i)
20{
21 *(int *)this_cpu_ptr(&bpf_testmod_ksym_percpu) = i;
22}
23
16noinline int bpf_testmod_loop_test(int n)24noinline int bpf_testmod_loop_test(int n)
17{25{
18 int i, sum = 0;26 int i, sum = 0;
@@ -71,13 +79,26 @@ static struct bin_attribute bin_attr_bpf_testmod_file __ro_after_init = { · 52 unmodified lines
71 .write = bpf_testmod_test_write,79 .write = bpf_testmod_test_write,
72};80};
73 81
82BTF_SET_START(bpf_testmod_kfunc_ids)
83BTF_ID(func, bpf_testmod_test_mod_kfunc)
84BTF_SET_END(bpf_testmod_kfunc_ids)
85
86static DEFINE_KFUNC_BTF_ID_SET(&bpf_testmod_kfunc_ids, bpf_testmod_kfunc_btf_set);
87
74static int bpf_testmod_init(void)88static int bpf_testmod_init(void)
75{89{
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;
77}97}
78 98
79static void bpf_testmod_exit(void)99static void bpf_testmod_exit(void)
80{100{
101 unregister_kfunc_btf_id_set(&prog_test_kfunc_list, &bpf_testmod_kfunc_btf_set);
81 return sysfs_remove_bin_file(kernel_kobj, &bin_attr_bpf_testmod_file);102 return sysfs_remove_bin_file(kernel_kobj, &bin_attr_bpf_testmod_file);
82}103}
83 104
tools/testing/selftests/bpf/prog_tests/ksyms_module.c+14−15
@@ -2,30 +2,29 @@ · 1 unmodified line
2/* Copyright (c) 2021 Facebook */2/* Copyright (c) 2021 Facebook */
3 3
4#include <test_progs.h>4#include <test_progs.h>
5#include <bpf/libbpf.h>5#include <network_helpers.h>
6#include <bpf/btf.h>
7#include "test_ksyms_module.lskel.h"6#include "test_ksyms_module.lskel.h"
8 7
9static int duration;
10
11void test_ksyms_module(void)8void test_ksyms_module(void)
12{9{
13 struct test_ksyms_module* skel;10 struct test_ksyms_module *skel;
11 int retval;
14 int err;12 int err;
15 13
16 skel = test_ksyms_module__open_and_load();14 if (!env.has_testmod) {
17 if (CHECK(!skel, "skel_open", "failed to open skeleton\n"))15 test__skip();
18 return;16 return;
17 }
19 18
20 err = test_ksyms_module__attach(skel);19 skel = test_ksyms_module__open_and_load();
21 if (CHECK(err, "skel_attach", "skeleton attach failed: %d\n", err))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"))
22 goto cleanup;25 goto cleanup;
23 26 ASSERT_EQ(retval, 0, "retval");
24 usleep(1);27 ASSERT_EQ(skel->bss->out_bpf_testmod_ksym, 42, "bpf_testmod_ksym");
25
26 ASSERT_EQ(skel->bss->triggered, true, "triggered");
27 ASSERT_EQ(skel->bss->out_mod_ksym_global, 123, "global_ksym_val");
28
29cleanup:28cleanup:
30 test_ksyms_module__destroy(skel);29 test_ksyms_module__destroy(skel);
31}30}
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
7void 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");
26cleanup:
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
2/* Copyright (c) 2021 Facebook */2/* Copyright (c) 2021 Facebook */
3 3
4#include "vmlinux.h"4#include "vmlinux.h"
5
6#include <bpf/bpf_helpers.h>5#include <bpf/bpf_helpers.h>
7 6
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
8extern const int bpf_testmod_ksym_percpu __ksym;20extern const int bpf_testmod_ksym_percpu __ksym;
21extern void bpf_testmod_test_mod_kfunc(int i) __ksym;
22extern void bpf_testmod_invalid_mod_kfunc(void) __ksym __weak;
9 23
10int out_mod_ksym_global = 0;24int out_bpf_testmod_ksym = 0;
11bool triggered = false;25const volatile int x = 0;
12 26
13SEC("raw_tp/sys_enter")27SEC("tc")
14int handler(const void *ctx)28int load(struct __sk_buff *skb)
15{29{
16 int *val;30 /* This will be kept by clang, but removed by verifier. Since it is
17 __u32 cpu;31 * marked as __weak, libbpf and gen_loader don't error out if BTF ID
18 32 * is not found for it, instead imm and off is set to 0 for it.
19 val = (int *)bpf_this_cpu_ptr(&bpf_testmod_ksym_percpu);33 */
20 out_mod_ksym_global = *val;34 if (x)
21 triggered = true;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}
22 40
41SEC("tc")
42int 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);
23 return 0;47 return 0;
24}48}
25 49
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},
1{24{
2 "calls: basic sanity",25 "calls: basic sanity",
3 .insns = {26 .insns = {