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 SEC("syscall") 46 __success __retval(0) 47 int stream_exhaust(void *ctx) 48 { 49 /* Use global variable for loop convergence. */ 50 size = 0; 51 bpf_repeat(BPF_MAX_LOOPS) { 52 if (bpf_stream_printk(BPF_STDOUT, _STR) == -ENOSPC && size == 99954) 53 return 0; 54 size += sizeof(_STR) - 1; 55 } 56 return 1; 57 } 58 59 SEC("syscall") 60 __arch_x86_64 61 __arch_arm64 62 __arch_s390x 63 __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)*}}") 69 int stream_cond_break(void *ctx) 70 { 71 while (can_loop) 72 ; 73 return 0; 74 } 75 76 SEC("syscall") 77 __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)*}}") 87 int stream_deadlock(void *ctx) 88 { 89 struct bpf_res_spin_lock *lock, *nlock; 90 91 lock = bpf_map_lookup_elem(&arrmap, &(int){0}); 92 if (!lock) 93 return 1; 94 nlock = bpf_map_lookup_elem(&arrmap, &(int){0}); 95 if (!nlock) 96 return 1; 97 if (bpf_res_spin_lock(lock)) 98 return 1; 99 if (bpf_res_spin_lock(nlock)) { 100 bpf_res_spin_unlock(lock); 101 return 0; 102 } 103 bpf_res_spin_unlock(nlock); 104 bpf_res_spin_unlock(lock); 105 return 1; 106 } 107 108 SEC("syscall") 109 __success __retval(0) 110 int stream_syscall(void *ctx) 111 { 112 bpf_stream_printk(BPF_STDOUT, "foo"); 113 return 0; 114 } 115 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 237 char _license[] SEC("license") = "GPL"; 238