xref: /linux/tools/testing/selftests/bpf/progs/stream.c (revision 69050f8d6d075dc01af7a5f2f550a8067510366f)
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