xref: /linux/tools/testing/selftests/bpf/progs/mem_rdonly_untrusted.c (revision 5a8cd539ac19f7a68e68e1d25ef9ca2ff55b8500)
1 // SPDX-License-Identifier: GPL-2.0
2 
3 #include <vmlinux.h>
4 #include <bpf/bpf_core_read.h>
5 #include "bpf_misc.h"
6 #include "bpf_kfuncs.h"
7 #include "../test_kmods/bpf_testmod_kfunc.h"
8 
9 SEC("tp_btf/sys_enter")
10 __success
11 __log_level(2)
12 __msg("r8 = *(u64 *)(r7 +0)          ; R7=ptr_nameidata(imm={{[0-9]+}}) R8=rdonly_untrusted_mem(sz=0)")
13 __msg("r9 = *(u8 *)(r8 +0)           ; R8=rdonly_untrusted_mem(sz=0) R9=scalar")
btf_id_to_ptr_mem(void * ctx)14 int btf_id_to_ptr_mem(void *ctx)
15 {
16 	struct task_struct *task;
17 	struct nameidata *idata;
18 	u64 ret, off;
19 
20 	task = bpf_get_current_task_btf();
21 	idata = task->nameidata;
22 	off = bpf_core_field_offset(struct nameidata, pathname);
23 	/*
24 	 * asm block to have reliable match target for __msg, equivalent of:
25 	 *   ret = task->nameidata->pathname[0];
26 	 */
27 	asm volatile (
28 	"r7 = %[idata];"
29 	"r7 += %[off];"
30 	"r8 = *(u64 *)(r7 + 0);"
31 	"r9 = *(u8 *)(r8 + 0);"
32 	"%[ret] = r9;"
33 	: [ret]"=r"(ret)
34 	: [idata]"r"(idata),
35 	  [off]"r"(off)
36 	: "r7", "r8", "r9");
37 	return ret;
38 }
39 
40 SEC("socket")
41 __success
42 __retval(0)
ldx_is_ok_bad_addr(void * ctx)43 int ldx_is_ok_bad_addr(void *ctx)
44 {
45 	char *p;
46 
47 	if (!bpf_core_enum_value_exists(enum bpf_features, BPF_FEAT_RDONLY_CAST_TO_VOID))
48 		return 42;
49 
50 	p = bpf_rdonly_cast(0, 0);
51 	return p[0x7fff];
52 }
53 
54 SEC("socket")
55 __success
56 __retval(1)
ldx_is_ok_good_addr(void * ctx)57 int ldx_is_ok_good_addr(void *ctx)
58 {
59 	int v, *p;
60 
61 	v = 1;
62 	p = bpf_rdonly_cast(&v, 0);
63 	return *p;
64 }
65 
66 SEC("socket")
67 __success
offset_not_tracked(void * ctx)68 int offset_not_tracked(void *ctx)
69 {
70 	int *p, i, s;
71 
72 	p = bpf_rdonly_cast(0, 0);
73 	s = 0;
74 	bpf_for(i, 0, 1000 * 1000 * 1000) {
75 		p++;
76 		s += *p;
77 	}
78 	return s;
79 }
80 
81 SEC("socket")
82 __failure
83 __msg("cannot write into rdonly_untrusted_mem")
stx_not_ok(void * ctx)84 int stx_not_ok(void *ctx)
85 {
86 	int v, *p;
87 
88 	v = 1;
89 	p = bpf_rdonly_cast(&v, 0);
90 	*p = 1;
91 	return 0;
92 }
93 
94 SEC("socket")
95 __failure
96 __msg("cannot write into rdonly_untrusted_mem")
atomic_not_ok(void * ctx)97 int atomic_not_ok(void *ctx)
98 {
99 	int v, *p;
100 
101 	v = 1;
102 	p = bpf_rdonly_cast(&v, 0);
103 	__sync_fetch_and_add(p, 1);
104 	return 0;
105 }
106 
107 SEC("socket")
108 __failure
109 __msg("cannot write into rdonly_untrusted_mem")
atomic_rmw_not_ok(void * ctx)110 int atomic_rmw_not_ok(void *ctx)
111 {
112 	long v, *p;
113 
114 	v = 1;
115 	p = bpf_rdonly_cast(&v, 0);
116 	return __sync_val_compare_and_swap(p, 0, 42);
117 }
118 
119 SEC("socket")
120 __failure
121 __msg("invalid access to memory, mem_size=0 off=0 size=4")
122 __msg("R1 min value is outside of the allowed memory range")
kfunc_param_not_ok(void * ctx)123 int kfunc_param_not_ok(void *ctx)
124 {
125 	int *p;
126 
127 	p = bpf_rdonly_cast(0, 0);
128 	bpf_kfunc_trusted_num_test(p);
129 	return 0;
130 }
131 
132 SEC("?fentry.s/" SYS_PREFIX "sys_getpgid")
133 __failure
134 __msg("R1 type=rdonly_untrusted_mem expected=")
helper_param_not_ok(void * ctx)135 int helper_param_not_ok(void *ctx)
136 {
137 	char *p;
138 
139 	p = bpf_rdonly_cast(0, 0);
140 	/*
141 	 * Any helper with ARG_MEM_SIZE_OR_ZERO constraint will do,
142 	 * the most permissive constraint
143 	 */
144 	bpf_copy_from_user(p, 0, (void *)42);
145 	return 0;
146 }
147 
get_some_addr(void)148 static __noinline u64 *get_some_addr(void)
149 {
150 	if (bpf_get_prandom_u32())
151 		return bpf_rdonly_cast(0, bpf_core_type_id_kernel(struct sock));
152 	else
153 		return bpf_rdonly_cast(0, 0);
154 }
155 
156 SEC("socket")
157 __success
158 __retval(0)
mixed_mem_type(void * ctx)159 int mixed_mem_type(void *ctx)
160 {
161 	u64 *p;
162 
163 	/* Try to avoid compiler hoisting load to if branches by using __noinline func. */
164 	p = get_some_addr();
165 	return *p;
166 }
167 
168 struct {
169 	__uint(type, BPF_MAP_TYPE_RINGBUF);
170 	__uint(max_entries, 4096);
171 } ringbuf SEC(".maps");
172 
173 struct {
174 	__uint(type, BPF_MAP_TYPE_ARRAY);
175 	__uint(max_entries, 1);
176 	__type(key, u32);
177 	__type(value, u64);
178 } array SEC(".maps");
179 
180 char dynptr_data[8];
181 
182 int zero;
183 
184 SEC("socket")
185 __success
186 __log_level(2)
187 __msg("r8 = *(u64 *)(r7 +0){{.*}}R7=untrusted_ptr_sock")
188 __msg("r8 = *(u64 *)(r7 +0){{.*}}R7=ringbuf_mem")
189 __retval(0)
mixed_mem_untrusted_btf_id_type(void * ctx)190 int mixed_mem_untrusted_btf_id_type(void *ctx)
191 {
192 	u64 *p, *q, v;
193 
194 	p = bpf_ringbuf_reserve(&ringbuf, sizeof(*p), 0);
195 	if (!p)
196 		return 1;
197 	*p = 42;
198 	q = bpf_rdonly_cast(0, bpf_core_type_id_kernel(struct sock));
199 	/*
200 	 * The load below is reached with PTR_TO_MEM | MEM_RINGBUF on one
201 	 * path and with PTR_TO_BTF_ID | PTR_UNTRUSTED on the other. The
202 	 * merged type has to keep the BPF_PROBE_MEM rewrite, otherwise
203 	 * the NULL deref taken at runtime panics the kernel instead of
204 	 * returning 0.
205 	 */
206 	asm volatile (
207 	"r7 = %[p];"
208 	"if %[zero] != 0 goto +1;"
209 	"r7 = %[q];"
210 	"r8 = *(u64 *)(r7 + 0);"
211 	"%[v] = r8;"
212 	: [v]"=r"(v)
213 	: [p]"r"(p),
214 	  [q]"r"(q),
215 	  [zero]"r"(zero)
216 	: "r7", "r8");
217 	bpf_ringbuf_discard(p, 0);
218 	return v;
219 }
220 
221 SEC("socket")
222 __success
223 __log_level(2)
224 __msg("r8 = *(u32 *)(r7 +0){{.*}}R7=ptr_nameidata")
225 __msg("r8 = *(u32 *)(r7 +0){{.*}}R7=ringbuf_mem")
226 __retval(0)
mixed_mem_btf_id_type(void * ctx)227 int mixed_mem_btf_id_type(void *ctx)
228 {
229 	struct task_struct *task;
230 	u32 *p, *q;
231 	u64 v;
232 
233 	p = bpf_ringbuf_reserve(&ringbuf, sizeof(*p), 0);
234 	if (!p)
235 		return 1;
236 	*p = 42;
237 	task = bpf_get_current_task_btf();
238 	/*
239 	 * A plain BTF pointer walk yields a bare PTR_TO_BTF_ID, and
240 	 * task->nameidata is NULL unless the task currently is in the
241 	 * middle of a path lookup.
242 	 */
243 	q = (u32 *)&task->nameidata->flags;
244 	/*
245 	 * Same as above, except that the other path yields a bare
246 	 * PTR_TO_BTF_ID. Merging it with PTR_TO_MEM used to drop the
247 	 * BPF_PROBE_MEM rewrite the bare PTR_TO_BTF_ID would have
248 	 * gotten on its own.
249 	 */
250 	asm volatile (
251 	"r7 = %[p];"
252 	"if %[zero] != 0 goto +1;"
253 	"r7 = %[q];"
254 	"r8 = *(u32 *)(r7 + 0);"
255 	"%[v] = r8;"
256 	: [v]"=r"(v)
257 	: [p]"r"(p),
258 	  [q]"r"(q),
259 	  [zero]"r"(zero)
260 	: "r7", "r8");
261 	bpf_ringbuf_discard(p, 0);
262 	return v;
263 }
264 
265 SEC("socket")
266 __success
267 __log_level(2)
268 __msg("r8 = *(u32 *)(r7 +0){{.*}}R7=ptr_nameidata")
269 __msg("r8 = *(u32 *)(r7 +0){{.*}}R7=rdonly_mem")
270 __retval(0)
mixed_rdonly_mem_btf_id_type(void * ctx)271 int mixed_rdonly_mem_btf_id_type(void *ctx)
272 {
273 	struct task_struct *task;
274 	struct bpf_dynptr dptr;
275 	char buf[sizeof(u32)];
276 	u32 *p, *q;
277 	u64 v;
278 
279 	if (bpf_dynptr_from_mem(dynptr_data, sizeof(dynptr_data), 0, &dptr))
280 		return 1;
281 	p = bpf_dynptr_slice(&dptr, 0, buf, sizeof(buf));
282 	if (!p)
283 		return 1;
284 	task = bpf_get_current_task_btf();
285 	q = (u32 *)&task->nameidata->flags;
286 	/*
287 	 * Same as above, except that the PTR_TO_MEM side already carries
288 	 * MEM_RDONLY. Merging it with a bare PTR_TO_BTF_ID used to yield
289 	 * PTR_TO_MEM | MEM_RDONLY, which is not rewritten either since
290 	 * only its PTR_UNTRUSTED variant is.
291 	 */
292 	asm volatile (
293 	"r7 = %[p];"
294 	"if %[zero] != 0 goto +1;"
295 	"r7 = %[q];"
296 	"r8 = *(u32 *)(r7 + 0);"
297 	"%[v] = r8;"
298 	: [v]"=r"(v)
299 	: [p]"r"(p),
300 	  [q]"r"(q),
301 	  [zero]"r"(zero)
302 	: "r7", "r8");
303 	return v;
304 }
305 
306 SEC("socket")
307 __success
308 __log_level(2)
309 __msg("r8 = *(u64 *)(r7 +0){{.*}}R7=ringbuf_mem")
310 __msg("r8 = *(u64 *)(r7 +0){{.*}}R7=rdonly_untrusted_mem")
311 __retval(0)
mixed_mem_mem_type(void * ctx)312 int mixed_mem_mem_type(void *ctx)
313 {
314 	u64 *p, *q, v;
315 
316 	p = bpf_ringbuf_reserve(&ringbuf, sizeof(*p), 0);
317 	if (!p)
318 		return 1;
319 	*p = 42;
320 	q = bpf_rdonly_cast(0, 0);
321 	/*
322 	 * Both paths are PTR_TO_MEM based, so they used to not trip the
323 	 * type mismatch check and skipped the merge altogether, leaving
324 	 * the insn with the PTR_TO_MEM | MEM_RINGBUF recorded first and
325 	 * hence without the BPF_PROBE_MEM rewrite the other path needs.
326 	 */
327 	asm volatile (
328 	"r7 = %[q];"
329 	"if %[zero] == 0 goto +1;"
330 	"r7 = %[p];"
331 	"r8 = *(u64 *)(r7 + 0);"
332 	"%[v] = r8;"
333 	: [v]"=r"(v)
334 	: [p]"r"(p),
335 	  [q]"r"(q),
336 	  [zero]"r"(zero)
337 	: "r7", "r8");
338 	bpf_ringbuf_discard(p, 0);
339 	return v;
340 }
341 
342 SEC("socket")
343 __failure
344 __msg("same insn cannot be used with different pointers")
mixed_map_value_mem_type(void * ctx)345 int mixed_map_value_mem_type(void *ctx)
346 {
347 	u64 *p, *q, v;
348 	u32 key = 0;
349 
350 	p = bpf_map_lookup_elem(&array, &key);
351 	if (!p)
352 		return 1;
353 	q = bpf_rdonly_cast(0, 0);
354 	/*
355 	 * PTR_TO_MAP_VALUE is neither PTR_TO_MEM nor PTR_TO_BTF_ID based,
356 	 * so it cannot be merged into a type which keeps the BPF_PROBE_MEM
357 	 * rewrite the PTR_TO_MEM | MEM_RDONLY | PTR_UNTRUSTED of the other
358 	 * path needs. Both bases were mismatch ok, hence the load used to be
359 	 * accepted with the PTR_TO_MAP_VALUE recorded and the NULL deref on
360 	 * the second path panicked the kernel.
361 	 */
362 	asm volatile (
363 	"r7 = %[q];"
364 	"if %[zero] == 0 goto +1;"
365 	"r7 = %[p];"
366 	"r8 = *(u64 *)(r7 + 0);"
367 	"%[v] = r8;"
368 	: [v]"=r"(v)
369 	: [p]"r"(p),
370 	  [q]"r"(q),
371 	  [zero]"r"(zero)
372 	: "r7", "r8");
373 	return v;
374 }
375 
376 SEC("socket")
377 __failure
378 __msg("same insn cannot be used with different pointers")
mixed_stack_mem_type(void * ctx)379 int mixed_stack_mem_type(void *ctx)
380 {
381 	u64 *p = bpf_rdonly_cast(0, 0);
382 	u64 s = 42, v;
383 
384 	/*
385 	 * Same as above, but for a PTR_TO_STACK on the other path.
386 	 */
387 	asm volatile (
388 	"r7 = %[p];"
389 	"if %[zero] == 0 goto +1;"
390 	"r7 = %[s];"
391 	"r8 = *(u64 *)(r7 + 0);"
392 	"%[v] = r8;"
393 	: [v]"=r"(v)
394 	: [p]"r"(p),
395 	  [s]"r"(&s),
396 	  [zero]"r"(zero)
397 	: "r7", "r8");
398 	return v;
399 }
400 
401 __attribute__((__aligned__(8)))
402 u8 global[] = {
403 	0x11, 0x22, 0x33, 0x44,
404 	0x55, 0x66, 0x77, 0x88,
405 	0x99
406 };
407 
408 __always_inline
combine(void * p)409 static u64 combine(void *p)
410 {
411 	u64 acc;
412 
413 	acc = 0;
414 #if __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__
415 	acc |= (*(u64 *)p >> 56) << 24;
416 	acc |= (*(u32 *)p >> 24) << 16;
417 	acc |= (*(u16 *)p >> 8)  << 8;
418 	acc |= *(u8 *)p;
419 #else
420 	acc |= (*(u64 *)p & 0xff) << 24;
421 	acc |= (*(u32 *)p & 0xff) << 16;
422 	acc |= (*(u16 *)p & 0xff) << 8;
423 	acc |= *(u8 *)p;
424 #endif
425 	return acc;
426 }
427 
428 SEC("socket")
429 __retval(0x88442211)
diff_size_access(void * ctx)430 int diff_size_access(void *ctx)
431 {
432 	return combine(bpf_rdonly_cast(&global, 0));
433 }
434 
435 SEC("socket")
436 __retval(0x99553322)
misaligned_access(void * ctx)437 int misaligned_access(void *ctx)
438 {
439 	return combine(bpf_rdonly_cast(&global, 0) + 1);
440 }
441 
return_one(void)442 __weak int return_one(void)
443 {
444 	return 1;
445 }
446 
447 SEC("socket")
448 __success
449 __retval(1)
null_check(void * ctx)450 int null_check(void *ctx)
451 {
452 	int *p;
453 
454 	p = bpf_rdonly_cast(0, 0);
455 	if (p == 0)
456 		/* make this a function call to avoid compiler
457 		 * moving r0 assignment before check.
458 		 */
459 		return return_one();
460 	return 0;
461 }
462 
463 SEC("socket")
464 __success
465 __retval(1)
ldx_is_ok_commuted_addr(void * ctx)466 int ldx_is_ok_commuted_addr(void *ctx)
467 {
468 	int v, *p, *derived;
469 
470 	v = 1;
471 	p = bpf_rdonly_cast(&v, 0);
472 	asm volatile ("%[dst] = 0;"
473 		"%[dst] += %[src];"
474 		: [dst]"=&r"(derived)
475 		: [src]"r"(p)
476 		: "memory");
477 	return *derived;
478 }
479 
480 char _license[] SEC("license") = "GPL";
481