Skip to content

Commit 52b7202

Browse files
puranjaymohanKernel Patches Daemon
authored andcommitted
selftests/bpf: Add tests for arena fault reporting
Add selftests for testing the reporting of arena page faults through BPF streams. Two new bpf programs are added that read and write to an unmapped arena address and the fault reporting is verified in the userspace through streams. The added bpf programs need to access the user_vm_start in struct bpf_arena, this is done by casting &arena to struct bpf_arena *, but barrier_var() is used on this ptr before accessing ptr->user_vm_start; to stop GCC from issuing an out-of-bound access due to the cast from smaller map struct to larger "struct bpf_arena" Other tests related to streams have been converted to use __stderr in progs/stream.c directly and test_stream_errors() in prog_tests/stream.c has been repurposed to validate the arena fault address printed in the stderr stream. This can't be directly validated using __stderr because the address is dynamic. Signed-off-by: Puranjay Mohan <[email protected]>
1 parent c68afe5 commit 52b7202

File tree

2 files changed

+176
-35
lines changed

2 files changed

+176
-35
lines changed

tools/testing/selftests/bpf/prog_tests/stream.c

Lines changed: 18 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -18,29 +18,9 @@ void test_stream_success(void)
1818
return;
1919
}
2020

21-
struct {
22-
int prog_off;
23-
const char *errstr;
24-
} stream_error_arr[] = {
25-
{
26-
offsetof(struct stream, progs.stream_cond_break),
27-
"ERROR: Timeout detected for may_goto instruction\n"
28-
"CPU: [0-9]+ UID: 0 PID: [0-9]+ Comm: .*\n"
29-
"Call trace:\n"
30-
"([a-zA-Z_][a-zA-Z0-9_]*\\+0x[0-9a-fA-F]+/0x[0-9a-fA-F]+\n"
31-
"|[ \t]+[^\n]+\n)*",
32-
},
33-
{
34-
offsetof(struct stream, progs.stream_deadlock),
35-
"ERROR: AA or ABBA deadlock detected for bpf_res_spin_lock\n"
36-
"Attempted lock = (0x[0-9a-fA-F]+)\n"
37-
"Total held locks = 1\n"
38-
"Held lock\\[ 0\\] = \\1\n" // Lock address must match
39-
"CPU: [0-9]+ UID: 0 PID: [0-9]+ Comm: .*\n"
40-
"Call trace:\n"
41-
"([a-zA-Z_][a-zA-Z0-9_]*\\+0x[0-9a-fA-F]+/0x[0-9a-fA-F]+\n"
42-
"|[ \t]+[^\n]+\n)*",
43-
},
21+
int prog_off[] = {
22+
offsetof(struct stream, progs.stream_arena_read_fault),
23+
offsetof(struct stream, progs.stream_arena_write_fault),
4424
};
4525

4626
static int match_regex(const char *pattern, const char *string)
@@ -56,44 +36,47 @@ static int match_regex(const char *pattern, const char *string)
5636
return rc == 0 ? 1 : 0;
5737
}
5838

59-
void test_stream_errors(void)
39+
void test_stream_arena_fault_address(void)
6040
{
6141
LIBBPF_OPTS(bpf_test_run_opts, opts);
6242
LIBBPF_OPTS(bpf_prog_stream_read_opts, ropts);
6343
struct stream *skel;
6444
int ret, prog_fd;
6545
char buf[1024];
46+
char fault_addr[64];
6647

6748
skel = stream__open_and_load();
6849
if (!ASSERT_OK_PTR(skel, "stream__open_and_load"))
6950
return;
7051

71-
for (int i = 0; i < ARRAY_SIZE(stream_error_arr); i++) {
52+
for (int i = 0; i < ARRAY_SIZE(prog_off); i++) {
7253
struct bpf_program **prog;
7354

74-
prog = (struct bpf_program **)(((char *)skel) + stream_error_arr[i].prog_off);
55+
prog = (struct bpf_program **)(((char *)skel) + prog_off[i]);
7556
prog_fd = bpf_program__fd(*prog);
7657
ret = bpf_prog_test_run_opts(prog_fd, &opts);
7758
ASSERT_OK(ret, "ret");
7859
ASSERT_OK(opts.retval, "retval");
7960

80-
#if !defined(__x86_64__) && !defined(__s390x__) && !defined(__aarch64__)
81-
ASSERT_TRUE(1, "Timed may_goto unsupported, skip.");
82-
if (i == 0) {
83-
ret = bpf_prog_stream_read(prog_fd, 2, buf, sizeof(buf), &ropts);
84-
ASSERT_EQ(ret, 0, "stream read");
85-
continue;
86-
}
61+
#if !defined(__x86_64__) && !defined(__aarch64__)
62+
ASSERT_TRUE(1, "Arena fault reporting unsupported, skip.");
63+
ret = bpf_prog_stream_read(prog_fd, 2, buf, sizeof(buf), &ropts);
64+
ASSERT_EQ(ret, 0, "stream read");
65+
continue;
8766
#endif
8867

8968
ret = bpf_prog_stream_read(prog_fd, BPF_STREAM_STDERR, buf, sizeof(buf), &ropts);
9069
ASSERT_GT(ret, 0, "stream read");
9170
ASSERT_LE(ret, 1023, "len for buf");
9271
buf[ret] = '\0';
9372

94-
ret = match_regex(stream_error_arr[i].errstr, buf);
95-
if (!ASSERT_TRUE(ret == 1, "regex match"))
73+
sprintf(fault_addr, "0x%lx", skel->bss->fault_addr);
74+
ret = match_regex(fault_addr, buf);
75+
76+
if (!ASSERT_TRUE(ret == 1, "regex match")) {
9677
fprintf(stderr, "Output from stream:\n%s\n", buf);
78+
fprintf(stderr, "Fault Addr: 0x%lx\n", skel->bss->fault_addr);
79+
}
9780
}
9881

9982
stream__destroy(skel);

tools/testing/selftests/bpf/progs/stream.c

Lines changed: 158 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
#include <bpf/bpf_helpers.h>
66
#include "bpf_misc.h"
77
#include "bpf_experimental.h"
8+
#include "bpf_arena_common.h"
89

910
struct arr_elem {
1011
struct bpf_res_spin_lock lock;
@@ -17,10 +18,29 @@ struct {
1718
__type(value, struct arr_elem);
1819
} arrmap SEC(".maps");
1920

21+
struct {
22+
__uint(type, BPF_MAP_TYPE_ARENA);
23+
__uint(map_flags, BPF_F_MMAPABLE);
24+
__uint(max_entries, 1); /* number of pages */
25+
} arena SEC(".maps");
26+
27+
struct elem {
28+
struct bpf_timer timer;
29+
};
30+
31+
struct {
32+
__uint(type, BPF_MAP_TYPE_ARRAY);
33+
__uint(max_entries, 1);
34+
__type(key, int);
35+
__type(value, struct elem);
36+
} array SEC(".maps");
37+
2038
#define ENOSPC 28
2139
#define _STR "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"
2240

2341
int size;
42+
u64 fault_addr;
43+
void *arena_ptr;
2444

2545
SEC("syscall")
2646
__success __retval(0)
@@ -37,7 +57,15 @@ int stream_exhaust(void *ctx)
3757
}
3858

3959
SEC("syscall")
60+
__arch_x86_64
61+
__arch_arm64
62+
__arch_s390x
4063
__success __retval(0)
64+
__stderr("ERROR: Timeout detected for may_goto instruction")
65+
__stderr("CPU: {{[0-9]+}} UID: 0 PID: {{[0-9]+}} Comm: {{.*}}")
66+
__stderr("Call trace:\n"
67+
"{{([a-zA-Z_][a-zA-Z0-9_]*\\+0x[0-9a-fA-F]+/0x[0-9a-fA-F]+\n"
68+
"|[ \t]+[^\n]+\n)*}}")
4169
int stream_cond_break(void *ctx)
4270
{
4371
while (can_loop)
@@ -47,6 +75,15 @@ int stream_cond_break(void *ctx)
4775

4876
SEC("syscall")
4977
__success __retval(0)
78+
__stderr("ERROR: AA or ABBA deadlock detected for bpf_res_spin_lock")
79+
__stderr("{{Attempted lock = (0x[0-9a-fA-F]+)\n"
80+
"Total held locks = 1\n"
81+
"Held lock\\[ 0\\] = \\1}}")
82+
__stderr("...")
83+
__stderr("CPU: {{[0-9]+}} UID: 0 PID: {{[0-9]+}} Comm: {{.*}}")
84+
__stderr("Call trace:\n"
85+
"{{([a-zA-Z_][a-zA-Z0-9_]*\\+0x[0-9a-fA-F]+/0x[0-9a-fA-F]+\n"
86+
"|[ \t]+[^\n]+\n)*}}")
5087
int stream_deadlock(void *ctx)
5188
{
5289
struct bpf_res_spin_lock *lock, *nlock;
@@ -76,4 +113,125 @@ int stream_syscall(void *ctx)
76113
return 0;
77114
}
78115

116+
SEC("syscall")
117+
__arch_x86_64
118+
__arch_arm64
119+
__success __retval(0)
120+
__stderr("ERROR: Arena WRITE access at unmapped address 0x{{.*}}")
121+
__stderr("CPU: {{[0-9]+}} UID: 0 PID: {{[0-9]+}} Comm: {{.*}}")
122+
__stderr("Call trace:\n"
123+
"{{([a-zA-Z_][a-zA-Z0-9_]*\\+0x[0-9a-fA-F]+/0x[0-9a-fA-F]+\n"
124+
"|[ \t]+[^\n]+\n)*}}")
125+
int stream_arena_write_fault(void *ctx)
126+
{
127+
struct bpf_arena *ptr = (void *)&arena;
128+
u64 user_vm_start;
129+
130+
/* Prevent GCC bounds warning: casting &arena to struct bpf_arena *
131+
* triggers bounds checking since the map definition is smaller than struct
132+
* bpf_arena. barrier_var() makes the pointer opaque to GCC, preventing the
133+
* bounds analysis
134+
*/
135+
barrier_var(ptr);
136+
user_vm_start = ptr->user_vm_start;
137+
fault_addr = user_vm_start + 0x7fff;
138+
bpf_addr_space_cast(user_vm_start, 0, 1);
139+
asm volatile (
140+
"r1 = %0;"
141+
"r2 = 1;"
142+
"*(u32 *)(r1 + 0x7fff) = r2;"
143+
:
144+
: "r" (user_vm_start)
145+
: "r1", "r2"
146+
);
147+
return 0;
148+
}
149+
150+
SEC("syscall")
151+
__arch_x86_64
152+
__arch_arm64
153+
__success __retval(0)
154+
__stderr("ERROR: Arena READ access at unmapped address 0x{{.*}}")
155+
__stderr("CPU: {{[0-9]+}} UID: 0 PID: {{[0-9]+}} Comm: {{.*}}")
156+
__stderr("Call trace:\n"
157+
"{{([a-zA-Z_][a-zA-Z0-9_]*\\+0x[0-9a-fA-F]+/0x[0-9a-fA-F]+\n"
158+
"|[ \t]+[^\n]+\n)*}}")
159+
int stream_arena_read_fault(void *ctx)
160+
{
161+
struct bpf_arena *ptr = (void *)&arena;
162+
u64 user_vm_start;
163+
164+
/* Prevent GCC bounds warning: casting &arena to struct bpf_arena *
165+
* triggers bounds checking since the map definition is smaller than struct
166+
* bpf_arena. barrier_var() makes the pointer opaque to GCC, preventing the
167+
* bounds analysis
168+
*/
169+
barrier_var(ptr);
170+
user_vm_start = ptr->user_vm_start;
171+
fault_addr = user_vm_start + 0x7fff;
172+
bpf_addr_space_cast(user_vm_start, 0, 1);
173+
asm volatile (
174+
"r1 = %0;"
175+
"r1 = *(u32 *)(r1 + 0x7fff);"
176+
:
177+
: "r" (user_vm_start)
178+
: "r1"
179+
);
180+
return 0;
181+
}
182+
183+
static __noinline void subprog(void)
184+
{
185+
int __arena *addr = (int __arena *)0xdeadbeef;
186+
187+
arena_ptr = &arena;
188+
*addr = 1;
189+
}
190+
191+
SEC("syscall")
192+
__arch_x86_64
193+
__arch_arm64
194+
__success __retval(0)
195+
__stderr("ERROR: Arena WRITE access at unmapped address 0x{{.*}}")
196+
__stderr("CPU: {{[0-9]+}} UID: 0 PID: {{[0-9]+}} Comm: {{.*}}")
197+
__stderr("Call trace:\n"
198+
"{{([a-zA-Z_][a-zA-Z0-9_]*\\+0x[0-9a-fA-F]+/0x[0-9a-fA-F]+\n"
199+
"|[ \t]+[^\n]+\n)*}}")
200+
int stream_arena_subprog_fault(void *ctx)
201+
{
202+
subprog();
203+
return 0;
204+
}
205+
206+
static __noinline int timer_cb(void *map, int *key, struct bpf_timer *timer)
207+
{
208+
int __arena *addr = (int __arena *)0xdeadbeef;
209+
210+
arena_ptr = &arena;
211+
*addr = 1;
212+
return 0;
213+
}
214+
215+
SEC("syscall")
216+
__arch_x86_64
217+
__arch_arm64
218+
__success __retval(0)
219+
__stderr("ERROR: Arena WRITE access at unmapped address 0x{{.*}}")
220+
__stderr("CPU: {{[0-9]+}} UID: 0 PID: {{[0-9]+}} Comm: {{.*}}")
221+
__stderr("Call trace:\n"
222+
"{{([a-zA-Z_][a-zA-Z0-9_]*\\+0x[0-9a-fA-F]+/0x[0-9a-fA-F]+\n"
223+
"|[ \t]+[^\n]+\n)*}}")
224+
int stream_arena_callback_fault(void *ctx)
225+
{
226+
struct bpf_timer *arr_timer;
227+
228+
arr_timer = bpf_map_lookup_elem(&array, &(int){0});
229+
if (!arr_timer)
230+
return 0;
231+
bpf_timer_init(arr_timer, &array, 1);
232+
bpf_timer_set_callback(arr_timer, timer_cb);
233+
bpf_timer_start(arr_timer, 0, 0);
234+
return 0;
235+
}
236+
79237
char _license[] SEC("license") = "GPL";

0 commit comments

Comments
 (0)