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