1 // SPDX-License-Identifier: GPL-2.0
2 #include <test_progs.h>
3 #include <linux/btf.h>
4 #include <bpf/btf.h>
5 #include "netif_receive_skb.skel.h"
6 #include "snprintf_btf_void.skel.h"
7
8 /* Demonstrate that bpf_snprintf_btf succeeds and that various data types
9 * are formatted correctly.
10 */
serial_test_snprintf_btf(void)11 void serial_test_snprintf_btf(void)
12 {
13 struct netif_receive_skb *skel;
14 struct netif_receive_skb__bss *bss;
15 int err, duration = 0;
16
17 skel = netif_receive_skb__open();
18 if (CHECK(!skel, "skel_open", "failed to open skeleton\n"))
19 return;
20
21 err = netif_receive_skb__load(skel);
22 if (CHECK(err, "skel_load", "failed to load skeleton: %d\n", err))
23 goto cleanup;
24
25 bss = skel->bss;
26
27 err = netif_receive_skb__attach(skel);
28 if (CHECK(err, "skel_attach", "skeleton attach failed: %d\n", err))
29 goto cleanup;
30
31 /* generate receive event */
32 err = system("ping -c 1 127.0.0.1 > /dev/null");
33 if (CHECK(err, "system", "ping failed: %d\n", err))
34 goto cleanup;
35
36 if (bss->skip) {
37 printf("%s:SKIP:no __builtin_btf_type_id\n", __func__);
38 test__skip();
39 goto cleanup;
40 }
41
42 /*
43 * Make sure netif_receive_skb program was triggered
44 * and it set expected return values from bpf_trace_printk()s
45 * and all tests ran.
46 */
47 if (!ASSERT_GT(bss->ret, 0, "bpf_snprintf_ret"))
48 goto cleanup;
49
50 if (CHECK(bss->ran_subtests == 0, "check if subtests ran",
51 "no subtests ran, did BPF program run?"))
52 goto cleanup;
53
54 if (CHECK(bss->num_subtests != bss->ran_subtests,
55 "check all subtests ran",
56 "only ran %d of %d tests\n", bss->num_subtests,
57 bss->ran_subtests))
58 goto cleanup;
59
60 cleanup:
61 netif_receive_skb__destroy(skel);
62 }
63
64 /*
65 * bpf_snprintf_btf() renders a type_id taken straight from the vmlinux BTF.
66 * Two such type_ids used to NULL-deref in the BTF show path:
67 * - a "const void" (a modifier resolving to void) in btf_modifier_show()
68 * - a BTF_KIND_VAR in btf_var_show() (base BTF has no resolved_ids)
69 * A fixed kernel renders both without crashing.
70 */
run(struct snprintf_btf_void * skel,__u32 type_id)71 static long run(struct snprintf_btf_void *skel, __u32 type_id)
72 {
73 LIBBPF_OPTS(bpf_test_run_opts, topts);
74 char ctx[8] = {};
75
76 skel->bss->type_id = type_id;
77 topts.ctx_in = ctx;
78 topts.ctx_size_in = sizeof(ctx);
79 if (!ASSERT_OK(bpf_prog_test_run_opts(bpf_program__fd(skel->progs.dump_type),
80 &topts), "test_run"))
81 return -1;
82 return skel->bss->ret;
83 }
84
test_snprintf_btf_void(void)85 void test_snprintf_btf_void(void)
86 {
87 const struct btf_type *t;
88 struct snprintf_btf_void *skel;
89 int i, n, cv = 0, var = 0;
90 struct btf *btf;
91
92 btf = btf__parse("/sys/kernel/btf/vmlinux", NULL);
93 if (!btf) {
94 test__skip();
95 return;
96 }
97
98 skel = snprintf_btf_void__open_and_load();
99 if (!ASSERT_OK_PTR(skel, "skel_open_and_load"))
100 goto out_btf;
101
102 n = btf__type_cnt(btf);
103 for (i = 1; i < n && !(cv && var); i++) {
104 t = btf__type_by_id(btf, i);
105 if (!cv && btf_kind(t) == BTF_KIND_CONST && t->type == 0)
106 cv = i;
107 /* Pick a VAR small enough to render from the program's buffer. */
108 if (!var && btf_kind(t) == BTF_KIND_VAR) {
109 long sz = btf__resolve_size(btf, t->type);
110
111 if (sz > 0 && sz <= (long)sizeof(skel->bss->obj))
112 var = i;
113 }
114 }
115
116 /* "const void" renders the "<unsupported kind:0>" placeholder. */
117 if (test__start_subtest("const_void")) {
118 if (cv) {
119 ASSERT_EQ(run(skel, cv),
120 sizeof("<unsupported kind:0>") - 1, "ret");
121 ASSERT_STREQ(skel->bss->out, "<unsupported kind:0>",
122 "placeholder");
123 } else {
124 test__skip();
125 }
126 }
127
128 /* A BTF_KIND_VAR must resolve and render without error. */
129 if (test__start_subtest("var")) {
130 if (var)
131 ASSERT_GT(run(skel, var), 0, "ret");
132 else
133 test__skip();
134 }
135
136 snprintf_btf_void__destroy(skel);
137 out_btf:
138 btf__free(btf);
139 }
140