xref: /linux/tools/testing/selftests/bpf/progs/stream.c (revision 9d19ca5d0e8b4a3f4b2eaa14e86a25f1c93ff35b)
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 __arch_riscv64
68 __arch_loongarch
69 __success __retval(0)
70 __stderr("ERROR: Timeout detected for may_goto instruction")
71 __stderr("CPU: {{[0-9]+}} UID: 0 PID: {{[0-9]+}} Comm: {{.*}}")
72 __stderr("Call trace:\n"
73 "{{([a-zA-Z_][a-zA-Z0-9_]*\\+0x[0-9a-fA-F]+/0x[0-9a-fA-F]+\n"
74 "|[ \t]+[^\n]+\n)*}}")
75 int stream_cond_break(void *ctx)
76 {
77 	while (can_loop)
78 		;
79 	return 0;
80 }
81 
82 SEC("syscall")
83 __success __retval(0)
84 __stderr("ERROR: AA or ABBA deadlock detected for bpf_res_spin_lock")
85 __stderr("{{Attempted lock   = (0x[0-9a-fA-F]+)\n"
86 "Total held locks = 1\n"
87 "Held lock\\[ 0\\] = \\1}}")
88 __stderr("...")
89 __stderr("CPU: {{[0-9]+}} UID: 0 PID: {{[0-9]+}} Comm: {{.*}}")
90 __stderr("Call trace:\n"
91 "{{([a-zA-Z_][a-zA-Z0-9_]*\\+0x[0-9a-fA-F]+/0x[0-9a-fA-F]+\n"
92 "|[ \t]+[^\n]+\n)*}}")
93 int stream_deadlock(void *ctx)
94 {
95 	struct bpf_res_spin_lock *lock, *nlock;
96 
97 	lock = bpf_map_lookup_elem(&arrmap, &(int){0});
98 	if (!lock)
99 		return 1;
100 	nlock = bpf_map_lookup_elem(&arrmap, &(int){0});
101 	if (!nlock)
102 		return 1;
103 	if (bpf_res_spin_lock(lock))
104 		return 1;
105 	if (bpf_res_spin_lock(nlock)) {
106 		bpf_res_spin_unlock(lock);
107 		return 0;
108 	}
109 	bpf_res_spin_unlock(nlock);
110 	bpf_res_spin_unlock(lock);
111 	return 1;
112 }
113 
114 SEC("syscall")
115 __success __retval(0)
116 int stream_syscall(void *ctx)
117 {
118 	bpf_stream_printk(BPF_STDOUT, "foo");
119 	return 0;
120 }
121 
122 SEC("syscall")
123 __arch_x86_64
124 __arch_arm64
125 __success __retval(0)
126 __stderr("ERROR: Arena WRITE access at unmapped address 0x{{.*}}")
127 __stderr("CPU: {{[0-9]+}} UID: 0 PID: {{[0-9]+}} Comm: {{.*}}")
128 __stderr("Call trace:\n"
129 "{{([a-zA-Z_][a-zA-Z0-9_]*\\+0x[0-9a-fA-F]+/0x[0-9a-fA-F]+\n"
130 "|[ \t]+[^\n]+\n)*}}")
131 int stream_arena_write_fault(void *ctx)
132 {
133 	struct bpf_arena *ptr = (void *)&arena;
134 	u64 user_vm_start;
135 
136 	/* Prevent GCC bounds warning: casting &arena to struct bpf_arena *
137 	 * triggers bounds checking since the map definition is smaller than struct
138 	 * bpf_arena. barrier_var() makes the pointer opaque to GCC, preventing the
139 	 * bounds analysis
140 	 */
141 	barrier_var(ptr);
142 	user_vm_start = ptr->user_vm_start;
143 	fault_addr = user_vm_start + 0x7fff;
144 	bpf_addr_space_cast(user_vm_start, 0, 1);
145 	asm volatile (
146 		"r1 = %0;"
147 		"r2 = 1;"
148 		"*(u32 *)(r1 + 0x7fff) = r2;"
149 		:
150 		: "r" (user_vm_start)
151 		: "r1", "r2"
152 	);
153 	return 0;
154 }
155 
156 SEC("syscall")
157 __arch_x86_64
158 __arch_arm64
159 __success __retval(0)
160 __stderr("ERROR: Arena READ access at unmapped address 0x{{.*}}")
161 __stderr("CPU: {{[0-9]+}} UID: 0 PID: {{[0-9]+}} Comm: {{.*}}")
162 __stderr("Call trace:\n"
163 "{{([a-zA-Z_][a-zA-Z0-9_]*\\+0x[0-9a-fA-F]+/0x[0-9a-fA-F]+\n"
164 "|[ \t]+[^\n]+\n)*}}")
165 int stream_arena_read_fault(void *ctx)
166 {
167 	struct bpf_arena *ptr = (void *)&arena;
168 	u64 user_vm_start;
169 
170 	/* Prevent GCC bounds warning: casting &arena to struct bpf_arena *
171 	 * triggers bounds checking since the map definition is smaller than struct
172 	 * bpf_arena. barrier_var() makes the pointer opaque to GCC, preventing the
173 	 * bounds analysis
174 	 */
175 	barrier_var(ptr);
176 	user_vm_start = ptr->user_vm_start;
177 	fault_addr = user_vm_start + 0x7fff;
178 	bpf_addr_space_cast(user_vm_start, 0, 1);
179 	asm volatile (
180 		"r1 = %0;"
181 		"r1 = *(u32 *)(r1 + 0x7fff);"
182 		:
183 		: "r" (user_vm_start)
184 		: "r1"
185 	);
186 	return 0;
187 }
188 
189 SEC("syscall")
190 __arch_x86_64
191 __arch_arm64
192 __success __retval(0)
193 __stderr("ERROR: Arena READ access at unmapped address 0x{{.*}}")
194 __stderr("CPU: {{[0-9]+}} UID: 0 PID: {{[0-9]+}} Comm: {{.*}}")
195 __stderr("Call trace:\n"
196 "{{([a-zA-Z_][a-zA-Z0-9_]*\\+0x[0-9a-fA-F]+/0x[0-9a-fA-F]+\n"
197 "|[ \t]+[^\n]+\n)*}}")
198 int stream_arena_load_acquire_fault(void *ctx)
199 {
200 	static const struct bpf_insn load_acquire_insn = {
201 		.code	 = 0xc3,	/* BPF_STX | BPF_ATOMIC | BPF_W */
202 		.dst_reg = 0,		/* BPF_REG_0 */
203 		.src_reg = 1,		/* BPF_REG_1 */
204 		.off	 = 0x7fff,
205 		.imm	 = 0x100,	/* BPF_LOAD_ACQ */
206 	};
207 	struct bpf_arena *ptr = (void *)&arena;
208 	u64 user_vm_start, val;
209 
210 	/*
211 	 * Prevent GCC bounds warning: casting &arena to struct bpf_arena *
212 	 * triggers bounds checking since the map definition is smaller than
213 	 * struct bpf_arena. barrier_var() makes the pointer opaque to GCC,
214 	 * preventing the bounds analysis.
215 	 */
216 	barrier_var(ptr);
217 	user_vm_start = ptr->user_vm_start;
218 	fault_addr = user_vm_start + 0x7fff;
219 	bpf_addr_space_cast(user_vm_start, 0, 1);
220 	asm volatile (
221 		"r1 = %[user_vm_start];"
222 		"r0 = 1;"
223 		".8byte %[load_acquire_insn];" /* r0 = load_acquire((u32 *)(r1 + 0x7fff)) */
224 		"%[val] = r0;"
225 		: [val] "=r" (val)
226 		: [user_vm_start] "r" (user_vm_start),
227 		  __imm_insn(load_acquire_insn, load_acquire_insn)
228 		: "r0", "r1"
229 	);
230 	return val;
231 }
232 
233 SEC("syscall")
234 __arch_x86_64
235 __arch_arm64
236 __success __retval(0)
237 __stderr("ERROR: Arena WRITE access at unmapped address 0x{{.*}}")
238 __stderr("CPU: {{[0-9]+}} UID: 0 PID: {{[0-9]+}} Comm: {{.*}}")
239 __stderr("Call trace:\n"
240 "{{([a-zA-Z_][a-zA-Z0-9_]*\\+0x[0-9a-fA-F]+/0x[0-9a-fA-F]+\n"
241 "|[ \t]+[^\n]+\n)*}}")
242 int stream_arena_xchg_fault(void *ctx)
243 {
244 	static const struct bpf_insn xchg_insn = {
245 		.code	 = 0xc3,	/* BPF_STX | BPF_ATOMIC | BPF_W */
246 		.dst_reg = 1,		/* BPF_REG_1 */
247 		.src_reg = 2,		/* BPF_REG_2 */
248 		.off	 = 0x7fff,
249 		.imm	 = 0xe1,	/* BPF_XCHG */
250 	};
251 	struct bpf_arena *ptr = (void *)&arena;
252 	u64 user_vm_start, val;
253 
254 	/*
255 	 * Prevent GCC bounds warning: casting &arena to struct bpf_arena *
256 	 * triggers bounds checking since the map definition is smaller than
257 	 * struct bpf_arena. barrier_var() makes the pointer opaque to GCC,
258 	 * preventing the bounds analysis.
259 	 */
260 	barrier_var(ptr);
261 	user_vm_start = ptr->user_vm_start;
262 	fault_addr = user_vm_start + 0x7fff;
263 	bpf_addr_space_cast(user_vm_start, 0, 1);
264 	/*
265 	 * A read-modify-write carrying BPF_FETCH writes to memory, so the fault
266 	 * has to be reported as a WRITE from the dst_reg address, but it also
267 	 * reads the old value into src_reg, so the exception handler has to
268 	 * clear src_reg. Poison it up front, the returned value must be 0.
269 	 */
270 	asm volatile (
271 		"r1 = %[user_vm_start];"
272 		"r2 = 1;"
273 		".8byte %[xchg_insn];" /* r2 = xchg((u32 *)(r1 + 0x7fff), r2) */
274 		"%[val] = r2;"
275 		: [val] "=r" (val)
276 		: [user_vm_start] "r" (user_vm_start),
277 		  __imm_insn(xchg_insn, xchg_insn)
278 		: "r1", "r2"
279 	);
280 	return val;
281 }
282 
283 SEC("syscall")
284 __arch_x86_64
285 __arch_arm64
286 __success __retval(0)
287 __stderr("ERROR: Arena WRITE access at unmapped address 0x{{.*}}")
288 __stderr("CPU: {{[0-9]+}} UID: 0 PID: {{[0-9]+}} Comm: {{.*}}")
289 __stderr("Call trace:\n"
290 "{{([a-zA-Z_][a-zA-Z0-9_]*\\+0x[0-9a-fA-F]+/0x[0-9a-fA-F]+\n"
291 "|[ \t]+[^\n]+\n)*}}")
292 int stream_arena_cmpxchg_fault(void *ctx)
293 {
294 	static const struct bpf_insn cmpxchg_insn = {
295 		.code	 = 0xc3,	/* BPF_STX | BPF_ATOMIC | BPF_W */
296 		.dst_reg = 1,		/* BPF_REG_1 */
297 		.src_reg = 2,		/* BPF_REG_2 */
298 		.off	 = 0x7fff,
299 		.imm	 = 0xf1,	/* BPF_CMPXCHG */
300 	};
301 	struct bpf_arena *ptr = (void *)&arena;
302 	u64 user_vm_start, val;
303 
304 	/*
305 	 * Prevent GCC bounds warning: casting &arena to struct bpf_arena *
306 	 * triggers bounds checking since the map definition is smaller than
307 	 * struct bpf_arena. barrier_var() makes the pointer opaque to GCC,
308 	 * preventing the bounds analysis.
309 	 */
310 	barrier_var(ptr);
311 	user_vm_start = ptr->user_vm_start;
312 	fault_addr = user_vm_start + 0x7fff;
313 	bpf_addr_space_cast(user_vm_start, 0, 1);
314 	/*
315 	 * Same as the exchange above, except that a BPF_CMPXCHG reads the old
316 	 * value into r0 rather than into src_reg, so r0 is the register the
317 	 * exception handler has to clear. It doubles as the compare value, but
318 	 * the comparison never happens since the access faults first.
319 	 */
320 	asm volatile (
321 		"r1 = %[user_vm_start];"
322 		"r0 = 1;"
323 		"r2 = 2;"
324 		".8byte %[cmpxchg_insn];" /* r0 = cmpxchg((u32 *)(r1 + 0x7fff), r0, r2) */
325 		"%[val] = r0;"
326 		: [val] "=r" (val)
327 		: [user_vm_start] "r" (user_vm_start),
328 		  __imm_insn(cmpxchg_insn, cmpxchg_insn)
329 		: "r0", "r1", "r2"
330 	);
331 	return val;
332 }
333 
334 static __noinline void subprog(void)
335 {
336 	int __arena *addr = (int __arena *)0xdeadbeef;
337 
338 	arena_ptr = &arena;
339 	*addr = 1;
340 }
341 
342 SEC("syscall")
343 __arch_x86_64
344 __arch_arm64
345 __success __retval(0)
346 __stderr("ERROR: Arena WRITE access at unmapped address 0x{{.*}}")
347 __stderr("CPU: {{[0-9]+}} UID: 0 PID: {{[0-9]+}} Comm: {{.*}}")
348 __stderr("Call trace:\n"
349 "{{([a-zA-Z_][a-zA-Z0-9_]*\\+0x[0-9a-fA-F]+/0x[0-9a-fA-F]+\n"
350 "|[ \t]+[^\n]+\n)*}}")
351 int stream_arena_subprog_fault(void *ctx)
352 {
353 	subprog();
354 	return 0;
355 }
356 
357 static __noinline int timer_cb(void *map, int *key, struct bpf_timer *timer)
358 {
359 	int __arena *addr = (int __arena *)0xdeadbeef;
360 
361 	arena_ptr = &arena;
362 	*addr = 1;
363 	return 0;
364 }
365 
366 SEC("syscall")
367 __arch_x86_64
368 __arch_arm64
369 __success __retval(0)
370 __stderr("ERROR: Arena WRITE access at unmapped address 0x{{.*}}")
371 __stderr("CPU: {{[0-9]+}} UID: 0 PID: {{[0-9]+}} Comm: {{.*}}")
372 __stderr("Call trace:\n"
373 "{{([a-zA-Z_][a-zA-Z0-9_]*\\+0x[0-9a-fA-F]+/0x[0-9a-fA-F]+\n"
374 "|[ \t]+[^\n]+\n)*}}")
375 int stream_arena_callback_fault(void *ctx)
376 {
377 	struct bpf_timer *arr_timer;
378 
379 	arr_timer = bpf_map_lookup_elem(&array, &(int){0});
380 	if (!arr_timer)
381 		return 0;
382 	bpf_timer_init(arr_timer, &array, 1);
383 	bpf_timer_set_callback(arr_timer, timer_cb);
384 	bpf_timer_start(arr_timer, 0, 0);
385 	return 0;
386 }
387 
388 SEC("syscall")
389 __arch_x86_64
390 __arch_arm64
391 __success __retval(0)
392 __stderr("CPU: {{[0-9]+}} UID: 0 PID: {{[0-9]+}} Comm: {{.*}}")
393 __stderr("Call trace:\n"
394 "{{([a-zA-Z_][a-zA-Z0-9_]*\\+0x[0-9a-fA-F]+/0x[0-9a-fA-F]+\n"
395 "|[ \t]+[^\n]+\n)*}}")
396 int stream_print_stack_kfunc(void *ctx)
397 {
398 	return bpf_stream_print_stack(BPF_STDERR);
399 }
400 
401 SEC("syscall")
402 __success __retval(-2)
403 int stream_print_stack_invalid_id(void *ctx)
404 {
405 	/* Try to pass an invalid stream ID. */
406 	return bpf_stream_print_stack((enum bpf_stream_id)0xbadcafe);
407 }
408 
409 SEC("syscall")
410 __arch_x86_64
411 __arch_arm64
412 __success __retval(0)
413 __stdout(_STR)
414 __stderr("CPU: {{[0-9]+}} UID: 0 PID: {{[0-9]+}} Comm: {{.*}}")
415 __stderr("Call trace:\n"
416 "{{([a-zA-Z_][a-zA-Z0-9_]*\\+0x[0-9a-fA-F]+/0x[0-9a-fA-F]+\n"
417 "|[ \t]+[^\n]+\n)*}}")
418 int stream_print_kfuncs_locked(void *ctx)
419 {
420 	int ret;
421 
422 	bpf_spin_lock(&block);
423 
424 	ret = bpf_stream_printk(BPF_STDOUT, _STR);
425 	if (ret)
426 		goto out;
427 
428 	ret = bpf_stream_print_stack(BPF_STDERR);
429 
430 out:
431 	bpf_spin_unlock(&block);
432 
433 	return ret;
434 }
435 
436 
437 char _license[] SEC("license") = "GPL";
438