1 // SPDX-License-Identifier: GPL-2.0 2 /* Copyright (c) 2025 Meta Platforms, Inc. and affiliates. */ 3 #include <vmlinux.h> 4 #include <bpf/bpf_tracing.h> 5 #include <bpf/bpf_helpers.h> 6 #include "bpf_misc.h" 7 #include "bpf_experimental.h" 8 #include "bpf_arena_common.h" 9 10 struct arr_elem { 11 struct bpf_res_spin_lock lock; 12 }; 13 14 struct { 15 __uint(type, BPF_MAP_TYPE_ARRAY); 16 __uint(max_entries, 1); 17 __type(key, int); 18 __type(value, struct arr_elem); 19 } arrmap SEC(".maps"); 20 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 38 #define ENOSPC 28 39 #define _STR "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx" 40 41 int size; 42 u64 fault_addr; 43 void *arena_ptr; 44 45 #define private(name) SEC(".bss." #name) __hidden __attribute__((aligned(8))) 46 47 private(STREAM) struct bpf_spin_lock block; 48 49 SEC("syscall") 50 __success __retval(0) 51 int stream_exhaust(void *ctx) 52 { 53 /* Use global variable for loop convergence. */ 54 size = 0; 55 bpf_repeat(BPF_MAX_LOOPS) { 56 if (bpf_stream_printk(BPF_STDOUT, _STR) == -ENOSPC && size == 99954) 57 return 0; 58 size += sizeof(_STR) - 1; 59 } 60 return 1; 61 } 62 63 SEC("syscall") 64 __arch_x86_64 65 __arch_arm64 66 __arch_s390x 67 __success __retval(0) 68 __stderr("ERROR: Timeout detected for may_goto instruction") 69 __stderr("CPU: {{[0-9]+}} UID: 0 PID: {{[0-9]+}} Comm: {{.*}}") 70 __stderr("Call trace:\n" 71 "{{([a-zA-Z_][a-zA-Z0-9_]*\\+0x[0-9a-fA-F]+/0x[0-9a-fA-F]+\n" 72 "|[ \t]+[^\n]+\n)*}}") 73 int stream_cond_break(void *ctx) 74 { 75 while (can_loop) 76 ; 77 return 0; 78 } 79 80 SEC("syscall") 81 __success __retval(0) 82 __stderr("ERROR: AA or ABBA deadlock detected for bpf_res_spin_lock") 83 __stderr("{{Attempted lock = (0x[0-9a-fA-F]+)\n" 84 "Total held locks = 1\n" 85 "Held lock\\[ 0\\] = \\1}}") 86 __stderr("...") 87 __stderr("CPU: {{[0-9]+}} UID: 0 PID: {{[0-9]+}} Comm: {{.*}}") 88 __stderr("Call trace:\n" 89 "{{([a-zA-Z_][a-zA-Z0-9_]*\\+0x[0-9a-fA-F]+/0x[0-9a-fA-F]+\n" 90 "|[ \t]+[^\n]+\n)*}}") 91 int stream_deadlock(void *ctx) 92 { 93 struct bpf_res_spin_lock *lock, *nlock; 94 95 lock = bpf_map_lookup_elem(&arrmap, &(int){0}); 96 if (!lock) 97 return 1; 98 nlock = bpf_map_lookup_elem(&arrmap, &(int){0}); 99 if (!nlock) 100 return 1; 101 if (bpf_res_spin_lock(lock)) 102 return 1; 103 if (bpf_res_spin_lock(nlock)) { 104 bpf_res_spin_unlock(lock); 105 return 0; 106 } 107 bpf_res_spin_unlock(nlock); 108 bpf_res_spin_unlock(lock); 109 return 1; 110 } 111 112 SEC("syscall") 113 __success __retval(0) 114 int stream_syscall(void *ctx) 115 { 116 bpf_stream_printk(BPF_STDOUT, "foo"); 117 return 0; 118 } 119 120 SEC("syscall") 121 __arch_x86_64 122 __arch_arm64 123 __success __retval(0) 124 __stderr("ERROR: Arena WRITE access at unmapped address 0x{{.*}}") 125 __stderr("CPU: {{[0-9]+}} UID: 0 PID: {{[0-9]+}} Comm: {{.*}}") 126 __stderr("Call trace:\n" 127 "{{([a-zA-Z_][a-zA-Z0-9_]*\\+0x[0-9a-fA-F]+/0x[0-9a-fA-F]+\n" 128 "|[ \t]+[^\n]+\n)*}}") 129 int stream_arena_write_fault(void *ctx) 130 { 131 struct bpf_arena *ptr = (void *)&arena; 132 u64 user_vm_start; 133 134 /* Prevent GCC bounds warning: casting &arena to struct bpf_arena * 135 * triggers bounds checking since the map definition is smaller than struct 136 * bpf_arena. barrier_var() makes the pointer opaque to GCC, preventing the 137 * bounds analysis 138 */ 139 barrier_var(ptr); 140 user_vm_start = ptr->user_vm_start; 141 fault_addr = user_vm_start + 0x7fff; 142 bpf_addr_space_cast(user_vm_start, 0, 1); 143 asm volatile ( 144 "r1 = %0;" 145 "r2 = 1;" 146 "*(u32 *)(r1 + 0x7fff) = r2;" 147 : 148 : "r" (user_vm_start) 149 : "r1", "r2" 150 ); 151 return 0; 152 } 153 154 SEC("syscall") 155 __arch_x86_64 156 __arch_arm64 157 __success __retval(0) 158 __stderr("ERROR: Arena READ access at unmapped address 0x{{.*}}") 159 __stderr("CPU: {{[0-9]+}} UID: 0 PID: {{[0-9]+}} Comm: {{.*}}") 160 __stderr("Call trace:\n" 161 "{{([a-zA-Z_][a-zA-Z0-9_]*\\+0x[0-9a-fA-F]+/0x[0-9a-fA-F]+\n" 162 "|[ \t]+[^\n]+\n)*}}") 163 int stream_arena_read_fault(void *ctx) 164 { 165 struct bpf_arena *ptr = (void *)&arena; 166 u64 user_vm_start; 167 168 /* Prevent GCC bounds warning: casting &arena to struct bpf_arena * 169 * triggers bounds checking since the map definition is smaller than struct 170 * bpf_arena. barrier_var() makes the pointer opaque to GCC, preventing the 171 * bounds analysis 172 */ 173 barrier_var(ptr); 174 user_vm_start = ptr->user_vm_start; 175 fault_addr = user_vm_start + 0x7fff; 176 bpf_addr_space_cast(user_vm_start, 0, 1); 177 asm volatile ( 178 "r1 = %0;" 179 "r1 = *(u32 *)(r1 + 0x7fff);" 180 : 181 : "r" (user_vm_start) 182 : "r1" 183 ); 184 return 0; 185 } 186 187 static __noinline void subprog(void) 188 { 189 int __arena *addr = (int __arena *)0xdeadbeef; 190 191 arena_ptr = &arena; 192 *addr = 1; 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_subprog_fault(void *ctx) 205 { 206 subprog(); 207 return 0; 208 } 209 210 static __noinline int timer_cb(void *map, int *key, struct bpf_timer *timer) 211 { 212 int __arena *addr = (int __arena *)0xdeadbeef; 213 214 arena_ptr = &arena; 215 *addr = 1; 216 return 0; 217 } 218 219 SEC("syscall") 220 __arch_x86_64 221 __arch_arm64 222 __success __retval(0) 223 __stderr("ERROR: Arena WRITE access at unmapped address 0x{{.*}}") 224 __stderr("CPU: {{[0-9]+}} UID: 0 PID: {{[0-9]+}} Comm: {{.*}}") 225 __stderr("Call trace:\n" 226 "{{([a-zA-Z_][a-zA-Z0-9_]*\\+0x[0-9a-fA-F]+/0x[0-9a-fA-F]+\n" 227 "|[ \t]+[^\n]+\n)*}}") 228 int stream_arena_callback_fault(void *ctx) 229 { 230 struct bpf_timer *arr_timer; 231 232 arr_timer = bpf_map_lookup_elem(&array, &(int){0}); 233 if (!arr_timer) 234 return 0; 235 bpf_timer_init(arr_timer, &array, 1); 236 bpf_timer_set_callback(arr_timer, timer_cb); 237 bpf_timer_start(arr_timer, 0, 0); 238 return 0; 239 } 240 241 SEC("syscall") 242 __arch_x86_64 243 __arch_arm64 244 __success __retval(0) 245 __stderr("CPU: {{[0-9]+}} UID: 0 PID: {{[0-9]+}} Comm: {{.*}}") 246 __stderr("Call trace:\n" 247 "{{([a-zA-Z_][a-zA-Z0-9_]*\\+0x[0-9a-fA-F]+/0x[0-9a-fA-F]+\n" 248 "|[ \t]+[^\n]+\n)*}}") 249 int stream_print_stack_kfunc(void *ctx) 250 { 251 return bpf_stream_print_stack(BPF_STDERR); 252 } 253 254 SEC("syscall") 255 __success __retval(-2) 256 int stream_print_stack_invalid_id(void *ctx) 257 { 258 /* Try to pass an invalid stream ID. */ 259 return bpf_stream_print_stack((enum bpf_stream_id)0xbadcafe); 260 } 261 262 SEC("syscall") 263 __arch_x86_64 264 __arch_arm64 265 __success __retval(0) 266 __stdout(_STR) 267 __stderr("CPU: {{[0-9]+}} UID: 0 PID: {{[0-9]+}} Comm: {{.*}}") 268 __stderr("Call trace:\n" 269 "{{([a-zA-Z_][a-zA-Z0-9_]*\\+0x[0-9a-fA-F]+/0x[0-9a-fA-F]+\n" 270 "|[ \t]+[^\n]+\n)*}}") 271 int stream_print_kfuncs_locked(void *ctx) 272 { 273 int ret; 274 275 bpf_spin_lock(&block); 276 277 ret = bpf_stream_printk(BPF_STDOUT, _STR); 278 if (ret) 279 goto out; 280 281 ret = bpf_stream_print_stack(BPF_STDERR); 282 283 out: 284 bpf_spin_unlock(&block); 285 286 return ret; 287 } 288 289 290 char _license[] SEC("license") = "GPL"; 291