Skip to content

Commit dca2037

Browse files
committed
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 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 <puranjay@kernel.org>
1 parent c853669 commit dca2037

File tree

2 files changed

+94
-1
lines changed

2 files changed

+94
-1
lines changed

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

Lines changed: 33 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,22 @@ struct {
4141
"([a-zA-Z_][a-zA-Z0-9_]*\\+0x[0-9a-fA-F]+/0x[0-9a-fA-F]+\n"
4242
"|[ \t]+[^\n]+\n)*",
4343
},
44+
{
45+
offsetof(struct stream, progs.stream_arena_read_fault),
46+
"ERROR: Arena READ access at unmapped address 0x.*\n"
47+
"CPU: [0-9]+ UID: 0 PID: [0-9]+ Comm: .*\n"
48+
"Call trace:\n"
49+
"([a-zA-Z_][a-zA-Z0-9_]*\\+0x[0-9a-fA-F]+/0x[0-9a-fA-F]+\n"
50+
"|[ \t]+[^\n]+\n)*",
51+
},
52+
{
53+
offsetof(struct stream, progs.stream_arena_write_fault),
54+
"ERROR: Arena WRITE access at unmapped address 0x.*\n"
55+
"CPU: [0-9]+ UID: 0 PID: [0-9]+ Comm: .*\n"
56+
"Call trace:\n"
57+
"([a-zA-Z_][a-zA-Z0-9_]*\\+0x[0-9a-fA-F]+/0x[0-9a-fA-F]+\n"
58+
"|[ \t]+[^\n]+\n)*",
59+
},
4460
};
4561

4662
static int match_regex(const char *pattern, const char *string)
@@ -63,6 +79,7 @@ void test_stream_errors(void)
6379
struct stream *skel;
6480
int ret, prog_fd;
6581
char buf[1024];
82+
char fault_addr[64] = {0};
6683

6784
skel = stream__open_and_load();
6885
if (!ASSERT_OK_PTR(skel, "stream__open_and_load"))
@@ -85,15 +102,30 @@ void test_stream_errors(void)
85102
continue;
86103
}
87104
#endif
105+
#if !defined(__x86_64__) && !defined(__aarch64__)
106+
ASSERT_TRUE(1, "Arena fault reporting unsupported, skip.");
107+
if (i == 2 || i == 3) {
108+
ret = bpf_prog_stream_read(prog_fd, 2, buf, sizeof(buf), &ropts);
109+
ASSERT_EQ(ret, 0, "stream read");
110+
continue;
111+
}
112+
#endif
88113

89114
ret = bpf_prog_stream_read(prog_fd, BPF_STREAM_STDERR, buf, sizeof(buf), &ropts);
90115
ASSERT_GT(ret, 0, "stream read");
91116
ASSERT_LE(ret, 1023, "len for buf");
92117
buf[ret] = '\0';
93118

94119
ret = match_regex(stream_error_arr[i].errstr, buf);
95-
if (!ASSERT_TRUE(ret == 1, "regex match"))
120+
if (ret && (i == 2 || i == 3)) {
121+
sprintf(fault_addr, "0x%lx", skel->bss->fault_addr);
122+
ret = match_regex(fault_addr, buf);
123+
}
124+
if (!ASSERT_TRUE(ret == 1, "regex match")) {
96125
fprintf(stderr, "Output from stream:\n%s\n", buf);
126+
if (i == 2 || i == 3)
127+
fprintf(stderr, "Fault Addr: 0x%lx\n", skel->bss->fault_addr);
128+
}
97129
}
98130

99131
stream__destroy(skel);

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

Lines changed: 61 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,17 @@ 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+
2027
#define ENOSPC 28
2128
#define _STR "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"
2229

2330
int size;
31+
u64 fault_addr;
2432

2533
SEC("syscall")
2634
__success __retval(0)
@@ -76,4 +84,57 @@ int stream_syscall(void *ctx)
7684
return 0;
7785
}
7886

87+
SEC("syscall")
88+
__success __retval(0)
89+
int stream_arena_write_fault(void *ctx)
90+
{
91+
struct bpf_arena *ptr = (void *)&arena;
92+
u64 user_vm_start;
93+
94+
/* Prevent GCC bounds warning: casting &arena to struct bpf_arena *
95+
* triggers bounds checking since the map definition is smaller than struct
96+
* bpf_arena. barrier_var() makes the pointer opaque to GCC, preventing the
97+
* bounds analysis
98+
*/
99+
barrier_var(ptr);
100+
user_vm_start = ptr->user_vm_start;
101+
fault_addr = user_vm_start + 0x7fff;
102+
bpf_addr_space_cast(user_vm_start, 0, 1);
103+
asm volatile (
104+
"r1 = %0;"
105+
"r2 = 1;"
106+
"*(u32 *)(r1 + 0x7fff) = r2;"
107+
:
108+
: "r" (user_vm_start)
109+
: "r1", "r2"
110+
);
111+
return 0;
112+
}
113+
114+
SEC("syscall")
115+
__success __retval(0)
116+
int stream_arena_read_fault(void *ctx)
117+
{
118+
struct bpf_arena *ptr = (void *)&arena;
119+
u64 user_vm_start;
120+
121+
/* Prevent GCC bounds warning: casting &arena to struct bpf_arena *
122+
* triggers bounds checking since the map definition is smaller than struct
123+
* bpf_arena. barrier_var() makes the pointer opaque to GCC, preventing the
124+
* bounds analysis
125+
*/
126+
barrier_var(ptr);
127+
user_vm_start = ptr->user_vm_start;
128+
fault_addr = user_vm_start + 0x7fff;
129+
bpf_addr_space_cast(user_vm_start, 0, 1);
130+
asm volatile (
131+
"r1 = %0;"
132+
"r1 = *(u32 *)(r1 + 0x7fff);"
133+
:
134+
: "r" (user_vm_start)
135+
: "r1"
136+
);
137+
return 0;
138+
}
139+
79140
char _license[] SEC("license") = "GPL";

0 commit comments

Comments
 (0)