Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

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 <puranjay@kernel.org>
Acked-by: Eduard Zingerman <eddyz87@gmail.com>
Link: https://lore.kernel.org/r/20250911145808.58042-7-puranjay@kernel.org
Signed-off-by: Alexei Starovoitov <ast@kernel.org>

authored by

Puranjay Mohan and committed by
Alexei Starovoitov
86f22250 edd03fcd

+190
+49
tools/testing/selftests/bpf/prog_tests/stream.c
··· 57 57 58 58 stream__destroy(skel); 59 59 } 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 + }
+141
tools/testing/selftests/bpf/progs/stream.c
··· 5 5 #include <bpf/bpf_helpers.h> 6 6 #include "bpf_misc.h" 7 7 #include "bpf_experimental.h" 8 + #include "bpf_arena_common.h" 8 9 9 10 struct arr_elem { 10 11 struct bpf_res_spin_lock lock; ··· 18 17 __type(value, struct arr_elem); 19 18 } arrmap SEC(".maps"); 20 19 20 + struct { 21 + __uint(type, BPF_MAP_TYPE_ARENA); 22 + __uint(map_flags, BPF_F_MMAPABLE); 23 + __uint(max_entries, 1); /* number of pages */ 24 + } arena SEC(".maps"); 25 + 26 + struct elem { 27 + struct bpf_timer timer; 28 + }; 29 + 30 + struct { 31 + __uint(type, BPF_MAP_TYPE_ARRAY); 32 + __uint(max_entries, 1); 33 + __type(key, int); 34 + __type(value, struct elem); 35 + } array SEC(".maps"); 36 + 21 37 #define ENOSPC 28 22 38 #define _STR "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx" 23 39 24 40 int size; 41 + u64 fault_addr; 42 + void *arena_ptr; 25 43 26 44 SEC("syscall") 27 45 __success __retval(0) ··· 110 90 int stream_syscall(void *ctx) 111 91 { 112 92 bpf_stream_printk(BPF_STDOUT, "foo"); 93 + return 0; 94 + } 95 + 96 + SEC("syscall") 97 + __arch_x86_64 98 + __arch_arm64 99 + __success __retval(0) 100 + __stderr("ERROR: Arena WRITE access at unmapped address 0x{{.*}}") 101 + __stderr("CPU: {{[0-9]+}} UID: 0 PID: {{[0-9]+}} Comm: {{.*}}") 102 + __stderr("Call trace:\n" 103 + "{{([a-zA-Z_][a-zA-Z0-9_]*\\+0x[0-9a-fA-F]+/0x[0-9a-fA-F]+\n" 104 + "|[ \t]+[^\n]+\n)*}}") 105 + int stream_arena_write_fault(void *ctx) 106 + { 107 + struct bpf_arena *ptr = (void *)&arena; 108 + u64 user_vm_start; 109 + 110 + /* Prevent GCC bounds warning: casting &arena to struct bpf_arena * 111 + * triggers bounds checking since the map definition is smaller than struct 112 + * bpf_arena. barrier_var() makes the pointer opaque to GCC, preventing the 113 + * bounds analysis 114 + */ 115 + barrier_var(ptr); 116 + user_vm_start = ptr->user_vm_start; 117 + fault_addr = user_vm_start + 0x7fff; 118 + bpf_addr_space_cast(user_vm_start, 0, 1); 119 + asm volatile ( 120 + "r1 = %0;" 121 + "r2 = 1;" 122 + "*(u32 *)(r1 + 0x7fff) = r2;" 123 + : 124 + : "r" (user_vm_start) 125 + : "r1", "r2" 126 + ); 127 + return 0; 128 + } 129 + 130 + SEC("syscall") 131 + __arch_x86_64 132 + __arch_arm64 133 + __success __retval(0) 134 + __stderr("ERROR: Arena READ access at unmapped address 0x{{.*}}") 135 + __stderr("CPU: {{[0-9]+}} UID: 0 PID: {{[0-9]+}} Comm: {{.*}}") 136 + __stderr("Call trace:\n" 137 + "{{([a-zA-Z_][a-zA-Z0-9_]*\\+0x[0-9a-fA-F]+/0x[0-9a-fA-F]+\n" 138 + "|[ \t]+[^\n]+\n)*}}") 139 + int stream_arena_read_fault(void *ctx) 140 + { 141 + struct bpf_arena *ptr = (void *)&arena; 142 + u64 user_vm_start; 143 + 144 + /* Prevent GCC bounds warning: casting &arena to struct bpf_arena * 145 + * triggers bounds checking since the map definition is smaller than struct 146 + * bpf_arena. barrier_var() makes the pointer opaque to GCC, preventing the 147 + * bounds analysis 148 + */ 149 + barrier_var(ptr); 150 + user_vm_start = ptr->user_vm_start; 151 + fault_addr = user_vm_start + 0x7fff; 152 + bpf_addr_space_cast(user_vm_start, 0, 1); 153 + asm volatile ( 154 + "r1 = %0;" 155 + "r1 = *(u32 *)(r1 + 0x7fff);" 156 + : 157 + : "r" (user_vm_start) 158 + : "r1" 159 + ); 160 + return 0; 161 + } 162 + 163 + static __noinline void subprog(void) 164 + { 165 + int __arena *addr = (int __arena *)0xdeadbeef; 166 + 167 + arena_ptr = &arena; 168 + *addr = 1; 169 + } 170 + 171 + SEC("syscall") 172 + __arch_x86_64 173 + __arch_arm64 174 + __success __retval(0) 175 + __stderr("ERROR: Arena WRITE access at unmapped address 0x{{.*}}") 176 + __stderr("CPU: {{[0-9]+}} UID: 0 PID: {{[0-9]+}} Comm: {{.*}}") 177 + __stderr("Call trace:\n" 178 + "{{([a-zA-Z_][a-zA-Z0-9_]*\\+0x[0-9a-fA-F]+/0x[0-9a-fA-F]+\n" 179 + "|[ \t]+[^\n]+\n)*}}") 180 + int stream_arena_subprog_fault(void *ctx) 181 + { 182 + subprog(); 183 + return 0; 184 + } 185 + 186 + static __noinline int timer_cb(void *map, int *key, struct bpf_timer *timer) 187 + { 188 + int __arena *addr = (int __arena *)0xdeadbeef; 189 + 190 + arena_ptr = &arena; 191 + *addr = 1; 192 + return 0; 193 + } 194 + 195 + SEC("syscall") 196 + __arch_x86_64 197 + __arch_arm64 198 + __success __retval(0) 199 + __stderr("ERROR: Arena WRITE access at unmapped address 0x{{.*}}") 200 + __stderr("CPU: {{[0-9]+}} UID: 0 PID: {{[0-9]+}} Comm: {{.*}}") 201 + __stderr("Call trace:\n" 202 + "{{([a-zA-Z_][a-zA-Z0-9_]*\\+0x[0-9a-fA-F]+/0x[0-9a-fA-F]+\n" 203 + "|[ \t]+[^\n]+\n)*}}") 204 + int stream_arena_callback_fault(void *ctx) 205 + { 206 + struct bpf_timer *arr_timer; 207 + 208 + arr_timer = bpf_map_lookup_elem(&array, &(int){0}); 209 + if (!arr_timer) 210 + return 0; 211 + bpf_timer_init(arr_timer, &array, 1); 212 + bpf_timer_set_callback(arr_timer, timer_cb); 213 + bpf_timer_start(arr_timer, 0, 0); 113 214 return 0; 114 215 } 115 216