summaryrefslogtreecommitdiff
path: root/tools/testing/selftests/bpf/prog_tests/snprintf_btf.c
blob: edce9c1b54fbcdb734de549f03a6ab4cddca029c (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
// SPDX-License-Identifier: GPL-2.0
#include <test_progs.h>
#include <linux/btf.h>
#include <bpf/btf.h>
#include "netif_receive_skb.skel.h"
#include "snprintf_btf_void.skel.h"

/* Demonstrate that bpf_snprintf_btf succeeds and that various data types
 * are formatted correctly.
 */
void serial_test_snprintf_btf(void)
{
	struct netif_receive_skb *skel;
	struct netif_receive_skb__bss *bss;
	int err, duration = 0;

	skel = netif_receive_skb__open();
	if (CHECK(!skel, "skel_open", "failed to open skeleton\n"))
		return;

	err = netif_receive_skb__load(skel);
	if (CHECK(err, "skel_load", "failed to load skeleton: %d\n", err))
		goto cleanup;

	bss = skel->bss;

	err = netif_receive_skb__attach(skel);
	if (CHECK(err, "skel_attach", "skeleton attach failed: %d\n", err))
		goto cleanup;

	/* generate receive event */
	err = system("ping -c 1 127.0.0.1 > /dev/null");
	if (CHECK(err, "system", "ping failed: %d\n", err))
		goto cleanup;

	if (bss->skip) {
		printf("%s:SKIP:no __builtin_btf_type_id\n", __func__);
		test__skip();
		goto cleanup;
	}

	/*
	 * Make sure netif_receive_skb program was triggered
	 * and it set expected return values from bpf_trace_printk()s
	 * and all tests ran.
	 */
	if (!ASSERT_GT(bss->ret, 0, "bpf_snprintf_ret"))
		goto cleanup;

	if (CHECK(bss->ran_subtests == 0, "check if subtests ran",
		  "no subtests ran, did BPF program run?"))
		goto cleanup;

	if (CHECK(bss->num_subtests != bss->ran_subtests,
		  "check all subtests ran",
		  "only ran %d of %d tests\n", bss->num_subtests,
		  bss->ran_subtests))
		goto cleanup;

cleanup:
	netif_receive_skb__destroy(skel);
}

/*
 * bpf_snprintf_btf() renders a type_id taken straight from the vmlinux BTF.
 * Two such type_ids used to NULL-deref in the BTF show path:
 *   - a "const void" (a modifier resolving to void) in btf_modifier_show()
 *   - a BTF_KIND_VAR in btf_var_show() (base BTF has no resolved_ids)
 * A fixed kernel renders both without crashing.
 */
static long run(struct snprintf_btf_void *skel, __u32 type_id)
{
	LIBBPF_OPTS(bpf_test_run_opts, topts);
	char ctx[8] = {};

	skel->bss->type_id = type_id;
	topts.ctx_in = ctx;
	topts.ctx_size_in = sizeof(ctx);
	if (!ASSERT_OK(bpf_prog_test_run_opts(bpf_program__fd(skel->progs.dump_type),
					      &topts), "test_run"))
		return -1;
	return skel->bss->ret;
}

void test_snprintf_btf_void(void)
{
	const struct btf_type *t;
	struct snprintf_btf_void *skel;
	int i, n, cv = 0, var = 0;
	struct btf *btf;

	btf = btf__parse("/sys/kernel/btf/vmlinux", NULL);
	if (!btf) {
		test__skip();
		return;
	}

	skel = snprintf_btf_void__open_and_load();
	if (!ASSERT_OK_PTR(skel, "skel_open_and_load"))
		goto out_btf;

	n = btf__type_cnt(btf);
	for (i = 1; i < n && !(cv && var); i++) {
		t = btf__type_by_id(btf, i);
		if (!cv && btf_kind(t) == BTF_KIND_CONST && t->type == 0)
			cv = i;
		/* Pick a VAR small enough to render from the program's buffer. */
		if (!var && btf_kind(t) == BTF_KIND_VAR) {
			long sz = btf__resolve_size(btf, t->type);

			if (sz > 0 && sz <= (long)sizeof(skel->bss->obj))
				var = i;
		}
	}

	/* "const void" renders the "<unsupported kind:0>" placeholder. */
	if (test__start_subtest("const_void")) {
		if (cv) {
			ASSERT_EQ(run(skel, cv),
				  sizeof("<unsupported kind:0>") - 1, "ret");
			ASSERT_STREQ(skel->bss->out, "<unsupported kind:0>",
				     "placeholder");
		} else {
			test__skip();
		}
	}

	/* A BTF_KIND_VAR must resolve and render without error. */
	if (test__start_subtest("var")) {
		if (var)
			ASSERT_GT(run(skel, var), 0, "ret");
		else
			test__skip();
	}

	snprintf_btf_void__destroy(skel);
out_btf:
	btf__free(btf);
}