Skip to content

Commit 86f2225

Browse files
puranjaymohanAlexei Starovoitov
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" Signed-off-by: Puranjay Mohan <[email protected]> Acked-by: Eduard Zingerman <[email protected]> Link: https://lore.kernel.org/r/[email protected] Signed-off-by: Alexei Starovoitov <[email protected]>
1 parent edd03fc commit 86f2225

File tree

2 files changed

+190
-0
lines changed

2 files changed

+190
-0
lines changed

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

Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -57,3 +57,52 @@ void test_stream_syscall(void)
5757

5858
stream__destroy(skel);
5959
}
60+
61+
static void test_address(struct bpf_program *prog, unsigned long *fault_addr_p)
62+
{
63+
LIBBPF_OPTS(bpf_test_run_opts, opts);
64+
LIBBPF_OPTS(bpf_prog_stream_read_opts, ropts);
65+
int ret, prog_fd;
66+
char fault_addr[64];
67+
char buf[1024];
68+
69+
prog_fd = bpf_program__fd(prog);
70+
71+
ret = bpf_prog_test_run_opts(prog_fd, &opts);
72+
ASSERT_OK(ret, "ret");
73+
ASSERT_OK(opts.retval, "retval");
74+
75+
sprintf(fault_addr, "0x%lx", *fault_addr_p);
76+
77+
ret = bpf_prog_stream_read(prog_fd, BPF_STREAM_STDERR, buf, sizeof(buf), &ropts);
78+
ASSERT_GT(ret, 0, "stream read");
79+
ASSERT_LE(ret, 1023, "len for buf");
80+
buf[ret] = '\0';
81+
82+
if (!ASSERT_HAS_SUBSTR(buf, fault_addr, "fault_addr")) {
83+
fprintf(stderr, "Output from stream:\n%s\n", buf);
84+
fprintf(stderr, "Fault Addr: %s\n", fault_addr);
85+
}
86+
}
87+
88+
void test_stream_arena_fault_address(void)
89+
{
90+
struct stream *skel;
91+
92+
#if !defined(__x86_64__) && !defined(__aarch64__)
93+
printf("%s:SKIP: arena fault reporting not supported\n", __func__);
94+
test__skip();
95+
return;
96+
#endif
97+
98+
skel = stream__open_and_load();
99+
if (!ASSERT_OK_PTR(skel, "stream__open_and_load"))
100+
return;
101+
102+
if (test__start_subtest("read_fault"))
103+
test_address(skel->progs.stream_arena_read_fault, &skel->bss->fault_addr);
104+
if (test__start_subtest("write_fault"))
105+
test_address(skel->progs.stream_arena_write_fault, &skel->bss->fault_addr);
106+
107+
stream__destroy(skel);
108+
}

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

Lines changed: 141 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)
@@ -93,4 +113,125 @@ int stream_syscall(void *ctx)
93113
return 0;
94114
}
95115

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+
96237
char _license[] SEC("license") = "GPL";

0 commit comments

Comments
 (0)