Skip to content

Commit 68cf33d

Browse files
anakryikokernel-patches-bot
authored andcommitted
selftests/bpf: validate libbpf's auto-sizing of LD/ST/STX instructions
Add selftests validating libbpf's auto-resizing of load/store instructions when used with CO-RE relocations. An explicit and manual approach with using bpf_core_read() is also demonstrated and tested. Separate BPF program is supposed to fail due to using signed integers of sizes that differ from kernel's sizes. To reliably simulate 32-bit BTF (i.e., the one with sizeof(long) == sizeof(void *) == 4), selftest generates its own custom BTF and passes it as a replacement for real kernel BTF. This allows to test 32/64-bitness mix on all architectures. Signed-off-by: Andrii Nakryiko <[email protected]>
1 parent fc3cb8b commit 68cf33d

File tree

2 files changed

+347
-0
lines changed

2 files changed

+347
-0
lines changed
Lines changed: 199 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,199 @@
1+
// SPDX-License-Identifier: GPL-2.0
2+
/* Copyright (c) 2020 Facebook */
3+
4+
#include <test_progs.h>
5+
#include "test_core_autosize.skel.h"
6+
#include <bpf/btf.h>
7+
8+
static int duration = 0;
9+
10+
static struct {
11+
unsigned long long ptr_samesized;
12+
unsigned long long val1_samesized;
13+
unsigned long long val2_samesized;
14+
unsigned long long val3_samesized;
15+
unsigned long long val4_samesized;
16+
17+
unsigned long long ptr_downsized;
18+
unsigned long long val1_downsized;
19+
unsigned long long val2_downsized;
20+
unsigned long long val3_downsized;
21+
unsigned long long val4_downsized;
22+
23+
unsigned long long ptr_probed;
24+
unsigned long long val1_probed;
25+
unsigned long long val2_probed;
26+
unsigned long long val3_probed;
27+
unsigned long long val4_probed;
28+
29+
unsigned long long ptr_signed;
30+
unsigned long long val1_signed;
31+
unsigned long long val2_signed;
32+
unsigned long long val3_signed;
33+
unsigned long long val4_signed;
34+
} out;
35+
36+
void test_core_autosize(void)
37+
{
38+
char btf_file[] = "/tmp/core_autosize.btf.XXXXXX";
39+
int err, fd = -1, zero = 0;
40+
int char_id, short_id, int_id, long_long_id, void_ptr_id, id;
41+
struct test_core_autosize* skel = NULL;
42+
struct bpf_object_load_attr load_attr = {};
43+
struct bpf_program *prog;
44+
struct bpf_map *bss_map;
45+
struct btf *btf = NULL;
46+
size_t written;
47+
const void *raw_data;
48+
__u32 raw_sz;
49+
FILE *f = NULL;
50+
51+
btf = btf__new_empty();
52+
if (!ASSERT_OK_PTR(btf, "empty_btf"))
53+
return;
54+
/* Emit the following struct with 32-bit pointer size:
55+
*
56+
* struct test_struct {
57+
* void *ptr;
58+
* unsigned long val2;
59+
* unsigned long long val1;
60+
* unsigned short val3;
61+
* unsigned char val4;
62+
* char: 8;
63+
* };
64+
*
65+
* This struct is going to be used as the "kernel BTF" for this test.
66+
*/
67+
68+
/* force 32-bit pointer size */
69+
btf__set_pointer_size(btf, 4);
70+
71+
char_id = btf__add_int(btf, "unsigned char", 1, 0);
72+
ASSERT_EQ(char_id, 1, "char_id");
73+
short_id = btf__add_int(btf, "unsigned short", 2, 0);
74+
ASSERT_EQ(short_id, 2, "short_id");
75+
/* "long unsigned int" of 4 byte size tells BTF that sizeof(void *) == 4 */
76+
int_id = btf__add_int(btf, "long unsigned int", 4, 0);
77+
ASSERT_EQ(int_id, 3, "int_id");
78+
long_long_id = btf__add_int(btf, "unsigned long long", 8, 0);
79+
ASSERT_EQ(long_long_id, 4, "long_long_id");
80+
void_ptr_id = btf__add_ptr(btf, 0);
81+
ASSERT_EQ(void_ptr_id, 5, "void_ptr_id");
82+
83+
id = btf__add_struct(btf, "test_struct", 20 /* bytes */);
84+
ASSERT_EQ(id, 6, "struct_id");
85+
err = btf__add_field(btf, "ptr", void_ptr_id, 0, 0);
86+
err = err ?: btf__add_field(btf, "val2", int_id, 32, 0);
87+
err = err ?: btf__add_field(btf, "val1", long_long_id, 64, 0);
88+
err = err ?: btf__add_field(btf, "val3", short_id, 128, 0);
89+
err = err ?: btf__add_field(btf, "val4", char_id, 144, 0);
90+
ASSERT_OK(err, "struct_fields");
91+
92+
fd = mkstemp(btf_file);
93+
if (CHECK(fd < 0, "btf_tmp", "failed to create file: %d\n", fd))
94+
goto cleanup;
95+
f = fdopen(fd, "w");
96+
if (!ASSERT_OK_PTR(f, "btf_fdopen"))
97+
goto cleanup;
98+
99+
raw_data = btf__get_raw_data(btf, &raw_sz);
100+
if (!ASSERT_OK_PTR(raw_data, "raw_data"))
101+
goto cleanup;
102+
written = fwrite(raw_data, 1, raw_sz, f);
103+
if (CHECK(written != raw_sz, "btf_write", "written: %zu, errno: %d\n", written, errno))
104+
goto cleanup;
105+
fflush(f);
106+
fclose(f);
107+
f = NULL;
108+
close(fd);
109+
fd = -1;
110+
111+
/* open and load BPF program with custom BTF as the kernel BTF */
112+
skel = test_core_autosize__open();
113+
if (!ASSERT_OK_PTR(skel, "skel_open"))
114+
return;
115+
116+
/* disable handle_signed() for now */
117+
prog = bpf_object__find_program_by_name(skel->obj, "handle_signed");
118+
if (!ASSERT_OK_PTR(prog, "prog_find"))
119+
goto cleanup;
120+
bpf_program__set_autoload(prog, false);
121+
122+
load_attr.obj = skel->obj;
123+
load_attr.target_btf_path = btf_file;
124+
err = bpf_object__load_xattr(&load_attr);
125+
if (!ASSERT_OK(err, "prog_load"))
126+
goto cleanup;
127+
128+
prog = bpf_object__find_program_by_name(skel->obj, "handle_samesize");
129+
if (!ASSERT_OK_PTR(prog, "prog_find"))
130+
goto cleanup;
131+
skel->links.handle_samesize = bpf_program__attach(prog);
132+
if (!ASSERT_OK_PTR(skel->links.handle_samesize, "prog_attach"))
133+
goto cleanup;
134+
135+
prog = bpf_object__find_program_by_name(skel->obj, "handle_downsize");
136+
if (!ASSERT_OK_PTR(prog, "prog_find"))
137+
goto cleanup;
138+
skel->links.handle_downsize = bpf_program__attach(prog);
139+
if (!ASSERT_OK_PTR(skel->links.handle_downsize, "prog_attach"))
140+
goto cleanup;
141+
142+
prog = bpf_object__find_program_by_name(skel->obj, "handle_probed");
143+
if (!ASSERT_OK_PTR(prog, "prog_find"))
144+
goto cleanup;
145+
skel->links.handle_probed = bpf_program__attach(prog);
146+
if (!ASSERT_OK_PTR(skel->links.handle_probed, "prog_attach"))
147+
goto cleanup;
148+
149+
usleep(1);
150+
151+
bss_map = bpf_object__find_map_by_name(skel->obj, "test_cor.bss");
152+
if (!ASSERT_OK_PTR(bss_map, "bss_map_find"))
153+
goto cleanup;
154+
155+
err = bpf_map_lookup_elem(bpf_map__fd(bss_map), &zero, (void *)&out);
156+
if (!ASSERT_OK(err, "bss_lookup"))
157+
goto cleanup;
158+
159+
ASSERT_EQ(out.ptr_samesized, 0x01020304, "ptr_samesized");
160+
ASSERT_EQ(out.val1_samesized, 0x1020304050607080, "val1_samesized");
161+
ASSERT_EQ(out.val2_samesized, 0x0a0b0c0d, "val2_samesized");
162+
ASSERT_EQ(out.val3_samesized, 0xfeed, "val3_samesized");
163+
ASSERT_EQ(out.val4_samesized, 0xb9, "val4_samesized");
164+
165+
ASSERT_EQ(out.ptr_downsized, 0x01020304, "ptr_downsized");
166+
ASSERT_EQ(out.val1_downsized, 0x1020304050607080, "val1_downsized");
167+
ASSERT_EQ(out.val2_downsized, 0x0a0b0c0d, "val2_downsized");
168+
ASSERT_EQ(out.val3_downsized, 0xfeed, "val3_downsized");
169+
ASSERT_EQ(out.val4_downsized, 0xb9, "val4_downsized");
170+
171+
ASSERT_EQ(out.ptr_probed, 0x01020304, "ptr_probed");
172+
ASSERT_EQ(out.val1_probed, 0x1020304050607080, "val1_probed");
173+
ASSERT_EQ(out.val2_probed, 0x0a0b0c0d, "val2_probed");
174+
ASSERT_EQ(out.val3_probed, 0xfeed, "val3_probed");
175+
ASSERT_EQ(out.val4_probed, 0xb9, "val4_probed");
176+
177+
test_core_autosize__destroy(skel);
178+
skel = NULL;
179+
180+
/* now re-load with handle_signed() enabled, it should fail loading */
181+
skel = test_core_autosize__open();
182+
if (!ASSERT_OK_PTR(skel, "skel_open"))
183+
return;
184+
185+
load_attr.obj = skel->obj;
186+
load_attr.target_btf_path = btf_file;
187+
err = bpf_object__load_xattr(&load_attr);
188+
if (!ASSERT_ERR(err, "bad_prog_load"))
189+
goto cleanup;
190+
191+
cleanup:
192+
if (f)
193+
fclose(f);
194+
if (fd >= 0)
195+
close(fd);
196+
remove(btf_file);
197+
btf__free(btf);
198+
test_core_autosize__destroy(skel);
199+
}
Lines changed: 148 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,148 @@
1+
// SPDX-License-Identifier: GPL-2.0
2+
// Copyright (c) 2019 Facebook
3+
4+
#include <linux/bpf.h>
5+
#include <stdint.h>
6+
#include <bpf/bpf_helpers.h>
7+
#include <bpf/bpf_core_read.h>
8+
9+
char _license[] SEC("license") = "GPL";
10+
11+
/* fields of exactly the same size */
12+
struct test_struct___samesize {
13+
void *ptr;
14+
unsigned long long val1;
15+
unsigned int val2;
16+
unsigned short val3;
17+
unsigned char val4;
18+
} __attribute((preserve_access_index));
19+
20+
/* unsigned fields that have to be downsized by libbpf */
21+
struct test_struct___downsize {
22+
void *ptr;
23+
unsigned long val1;
24+
unsigned long val2;
25+
unsigned long val3;
26+
unsigned long val4;
27+
/* total sz: 40 */
28+
} __attribute__((preserve_access_index));
29+
30+
/* fields with signed integers of wrong size, should be rejected */
31+
struct test_struct___signed {
32+
void *ptr;
33+
long val1;
34+
long val2;
35+
long val3;
36+
long val4;
37+
} __attribute((preserve_access_index));
38+
39+
/* real layout and sizes according to test's (32-bit) BTF */
40+
struct {
41+
unsigned int ptr; /* can't use `void *`, it is always 8 byte in BPF target */
42+
unsigned int val2;
43+
unsigned long long val1;
44+
unsigned short val3;
45+
unsigned char val4;
46+
unsigned char _pad;
47+
/* total sz: 20 */
48+
} input = {
49+
.ptr = 0x01020304,
50+
.val1 = 0x1020304050607080,
51+
.val2 = 0x0a0b0c0d,
52+
.val3 = 0xfeed,
53+
.val4 = 0xb9,
54+
._pad = 0xff, /* make sure no accidental zeros are present */
55+
};
56+
57+
unsigned long long ptr_samesized = 0;
58+
unsigned long long val1_samesized = 0;
59+
unsigned long long val2_samesized = 0;
60+
unsigned long long val3_samesized = 0;
61+
unsigned long long val4_samesized = 0;
62+
63+
unsigned long long ptr_downsized = 0;
64+
unsigned long long val1_downsized = 0;
65+
unsigned long long val2_downsized = 0;
66+
unsigned long long val3_downsized = 0;
67+
unsigned long long val4_downsized = 0;
68+
69+
unsigned long long ptr_probed = 0;
70+
unsigned long long val1_probed = 0;
71+
unsigned long long val2_probed = 0;
72+
unsigned long long val3_probed = 0;
73+
unsigned long long val4_probed = 0;
74+
75+
unsigned long long ptr_signed = 0;
76+
unsigned long long val1_signed = 0;
77+
unsigned long long val2_signed = 0;
78+
unsigned long long val3_signed = 0;
79+
unsigned long long val4_signed = 0;
80+
81+
SEC("raw_tp/sys_exit")
82+
int handle_samesize(void *ctx)
83+
{
84+
struct test_struct___samesize *in = (void *)&input;
85+
86+
ptr_samesized = (unsigned long long)in->ptr;
87+
val1_samesized = in->val1;
88+
val2_samesized = in->val2;
89+
val3_samesized = in->val3;
90+
val4_samesized = in->val4;
91+
92+
return 0;
93+
}
94+
95+
SEC("raw_tp/sys_exit")
96+
int handle_downsize(void *ctx)
97+
{
98+
struct test_struct___downsize *in = (void *)&input;
99+
100+
ptr_downsized = (unsigned long long)in->ptr;
101+
val1_downsized = in->val1;
102+
val2_downsized = in->val2;
103+
val3_downsized = in->val3;
104+
val4_downsized = in->val4;
105+
106+
return 0;
107+
}
108+
109+
SEC("raw_tp/sys_enter")
110+
int handle_probed(void *ctx)
111+
{
112+
struct test_struct___downsize *in = (void *)&input;
113+
__u64 tmp;
114+
115+
tmp = 0;
116+
bpf_core_read(&tmp, bpf_core_field_size(in->ptr), &in->ptr);
117+
ptr_probed = tmp;
118+
119+
tmp = 0;
120+
bpf_core_read(&tmp, bpf_core_field_size(in->val1), &in->val1);
121+
val1_probed = tmp;
122+
123+
tmp = 0;
124+
bpf_core_read(&tmp, bpf_core_field_size(in->val2), &in->val2);
125+
val2_probed = tmp;
126+
127+
tmp = 0;
128+
bpf_core_read(&tmp, bpf_core_field_size(in->val3), &in->val3);
129+
val3_probed = tmp;
130+
131+
tmp = 0;
132+
bpf_core_read(&tmp, bpf_core_field_size(in->val4), &in->val4);
133+
val4_probed = tmp;
134+
135+
return 0;
136+
}
137+
138+
SEC("raw_tp/sys_enter")
139+
int handle_signed(void *ctx)
140+
{
141+
struct test_struct___signed *in = (void *)&input;
142+
143+
val2_signed = in->val2;
144+
val3_signed = in->val3;
145+
val4_signed = in->val4;
146+
147+
return 0;
148+
}

0 commit comments

Comments
 (0)