xref: /linux/tools/testing/selftests/bpf/progs/verifier_unpriv.c (revision 913a5466dbfc28c82ebe8dd85fec0ddc1407a362)
1 // SPDX-License-Identifier: GPL-2.0
2 /* Converted from tools/testing/selftests/bpf/verifier/unpriv.c */
3 
4 #include <linux/bpf.h>
5 #include <bpf/bpf_helpers.h>
6 #include "../../../include/linux/filter.h"
7 #include "bpf_misc.h"
8 
9 extern const int bpf_prog_active __ksym;
10 
11 #define BPF_SK_LOOKUP(func) \
12 	/* struct bpf_sock_tuple tuple = {} */ \
13 	"r2 = 0;"			\
14 	"*(u32*)(r10 - 8) = r2;"	\
15 	"*(u64*)(r10 - 16) = r2;"	\
16 	"*(u64*)(r10 - 24) = r2;"	\
17 	"*(u64*)(r10 - 32) = r2;"	\
18 	"*(u64*)(r10 - 40) = r2;"	\
19 	"*(u64*)(r10 - 48) = r2;"	\
20 	/* sk = func(ctx, &tuple, sizeof tuple, 0, 0) */ \
21 	"r2 = r10;"			\
22 	"r2 += -48;"			\
23 	"r3 = %[sizeof_bpf_sock_tuple];"\
24 	"r4 = 0;"			\
25 	"r5 = 0;"			\
26 	"call %[" #func "];"
27 
28 struct {
29 	__uint(type, BPF_MAP_TYPE_HASH);
30 	__uint(max_entries, 1);
31 	__type(key, long long);
32 	__type(value, long long);
33 } map_hash_8b SEC(".maps");
34 
35 void dummy_prog_42_socket(void);
36 void dummy_prog_24_socket(void);
37 void dummy_prog_loop1_socket(void);
38 
39 struct {
40 	__uint(type, BPF_MAP_TYPE_PROG_ARRAY);
41 	__uint(max_entries, 4);
42 	__uint(key_size, sizeof(int));
43 	__array(values, void (void));
44 } map_prog1_socket SEC(".maps") = {
45 	.values = {
46 		[0] = (void *)&dummy_prog_42_socket,
47 		[1] = (void *)&dummy_prog_loop1_socket,
48 		[2] = (void *)&dummy_prog_24_socket,
49 	},
50 };
51 
52 SEC("socket")
53 __auxiliary __auxiliary_unpriv
54 __naked void dummy_prog_42_socket(void)
55 {
56 	asm volatile ("r0 = 42; exit;");
57 }
58 
59 SEC("socket")
60 __auxiliary __auxiliary_unpriv
61 __naked void dummy_prog_24_socket(void)
62 {
63 	asm volatile ("r0 = 24; exit;");
64 }
65 
66 SEC("socket")
67 __auxiliary __auxiliary_unpriv
68 __naked void dummy_prog_loop1_socket(void)
69 {
70 	asm volatile ("			\
71 	r3 = 1;				\
72 	r2 = %[map_prog1_socket] ll;	\
73 	call %[bpf_tail_call];		\
74 	r0 = 41;			\
75 	exit;				\
76 "	:
77 	: __imm(bpf_tail_call),
78 	  __imm_addr(map_prog1_socket)
79 	: __clobber_all);
80 }
81 
82 SEC("socket")
83 __description("unpriv: pseudo btf id log masks address")
84 __success_unpriv
85 __msg_unpriv("0: (18) r1 = 0x0")
86 __not_msg_unpriv("0: (18) r1 = 0x{{[1-9a-f][0-9a-f]*}}")
87 __retval_unpriv(0)
88 __log_level(2)
89 __naked void pseudo_btf_id_log_masks_address(void)
90 {
91 	asm volatile ("r1 = %[bpf_prog_active] ll;"
92 		      "r0 = 0;"
93 		      "exit;"
94 	:
95 	: __imm_addr(bpf_prog_active)
96 	: __clobber_all);
97 }
98 
99 static int pseudo_func_callback(__u32 index, void *ctx)
100 {
101 	return 0;
102 }
103 
104 SEC("socket")
105 __description("unpriv: pseudo function policy diagnostic")
106 __success __failure_unpriv
107 __msg_unpriv("loading/calling other bpf or kernel functions")
108 __not_msg_unpriv("BPF-to-BPF function call")
109 __msg_unpriv("policy check failed for BPF function reference")
110 __msg_unpriv("avoid BPF function references in unprivileged")
111 int unpriv_pseudo_func_policy(void *ctx)
112 {
113 	bpf_loop(1, pseudo_func_callback, NULL, 0);
114 	return 0;
115 }
116 
117 SEC("socket")
118 __description("unpriv: return pointer")
119 __success __failure_unpriv __msg_unpriv("R0 leaks addr")
120 __retval(POINTER_VALUE)
121 __naked void unpriv_return_pointer(void)
122 {
123 	asm volatile ("					\
124 	r0 = r10;					\
125 	exit;						\
126 "	::: __clobber_all);
127 }
128 
129 SEC("socket")
130 __description("unpriv: add const to pointer")
131 __success __success_unpriv __retval(0)
132 __naked void unpriv_add_const_to_pointer(void)
133 {
134 	asm volatile ("					\
135 	r1 += 8;					\
136 	r0 = 0;						\
137 	exit;						\
138 "	::: __clobber_all);
139 }
140 
141 SEC("socket")
142 __description("unpriv: add pointer to pointer")
143 __failure __msg("R1 pointer += pointer")
144 __failure_unpriv
145 __naked void unpriv_add_pointer_to_pointer(void)
146 {
147 	asm volatile ("					\
148 	r1 += r10;					\
149 	r0 = 0;						\
150 	exit;						\
151 "	::: __clobber_all);
152 }
153 
154 SEC("socket")
155 __description("unpriv: neg pointer")
156 __success __failure_unpriv __msg_unpriv("R1 pointer arithmetic")
157 __retval(0)
158 __naked void unpriv_neg_pointer(void)
159 {
160 	asm volatile ("					\
161 	r1 = -r1;					\
162 	r0 = 0;						\
163 	exit;						\
164 "	::: __clobber_all);
165 }
166 
167 SEC("socket")
168 __description("unpriv: cmp pointer with const")
169 __success __failure_unpriv __msg_unpriv("R1 pointer comparison")
170 __retval(0)
171 __naked void unpriv_cmp_pointer_with_const(void)
172 {
173 	asm volatile ("					\
174 	if r1 == 0 goto l0_%=;				\
175 l0_%=:	r0 = 0;						\
176 	exit;						\
177 "	::: __clobber_all);
178 }
179 
180 SEC("socket")
181 __description("unpriv: cmp pointer with pointer")
182 __success __failure_unpriv __msg_unpriv("R10 pointer comparison")
183 __retval(0)
184 __naked void unpriv_cmp_pointer_with_pointer(void)
185 {
186 	asm volatile ("					\
187 	if r1 == r10 goto l0_%=;			\
188 l0_%=:	r0 = 0;						\
189 	exit;						\
190 "	::: __clobber_all);
191 }
192 
193 SEC("tracepoint")
194 __description("unpriv: check that printk is disallowed")
195 __success
196 __naked void check_that_printk_is_disallowed(void)
197 {
198 	asm volatile ("					\
199 	r1 = 0;						\
200 	*(u64*)(r10 - 8) = r1;				\
201 	r1 = r10;					\
202 	r1 += -8;					\
203 	r2 = 8;						\
204 	r3 = r1;					\
205 	call %[bpf_trace_printk];			\
206 	r0 = 0;						\
207 	exit;						\
208 "	:
209 	: __imm(bpf_trace_printk)
210 	: __clobber_all);
211 }
212 
213 SEC("socket")
214 __description("unpriv: pass pointer to helper function")
215 __success __failure_unpriv __msg_unpriv("R4 leaks addr")
216 __retval(0)
217 __naked void pass_pointer_to_helper_function(void)
218 {
219 	asm volatile ("					\
220 	r1 = 0;						\
221 	*(u64*)(r10 - 8) = r1;				\
222 	r2 = r10;					\
223 	r2 += -8;					\
224 	r1 = %[map_hash_8b] ll;				\
225 	r3 = r2;					\
226 	r4 = r2;					\
227 	call %[bpf_map_update_elem];			\
228 	r0 = 0;						\
229 	exit;						\
230 "	:
231 	: __imm(bpf_map_update_elem),
232 	  __imm_addr(map_hash_8b)
233 	: __clobber_all);
234 }
235 
236 SEC("socket")
237 __description("unpriv: indirectly pass pointer on stack to helper function")
238 __success __failure_unpriv
239 __msg_unpriv("invalid read from stack R2 off -8+0 size 8")
240 __retval(0)
241 __naked void on_stack_to_helper_function(void)
242 {
243 	asm volatile ("					\
244 	*(u64*)(r10 - 8) = r10;				\
245 	r2 = r10;					\
246 	r2 += -8;					\
247 	r1 = %[map_hash_8b] ll;				\
248 	call %[bpf_map_lookup_elem];			\
249 	r0 = 0;						\
250 	exit;						\
251 "	:
252 	: __imm(bpf_map_lookup_elem),
253 	  __imm_addr(map_hash_8b)
254 	: __clobber_all);
255 }
256 
257 SEC("socket")
258 __description("unpriv: mangle pointer on stack 1")
259 __success __failure_unpriv __msg_unpriv("attempt to corrupt spilled")
260 __retval(0)
261 __naked void mangle_pointer_on_stack_1(void)
262 {
263 	asm volatile ("					\
264 	*(u64*)(r10 - 8) = r10;				\
265 	r0 = 0;						\
266 	*(u32*)(r10 - 8) = r0;				\
267 	r0 = 0;						\
268 	exit;						\
269 "	::: __clobber_all);
270 }
271 
272 SEC("socket")
273 __description("unpriv: mangle pointer on stack 2")
274 __success __failure_unpriv __msg_unpriv("attempt to corrupt spilled")
275 __retval(0)
276 __naked void mangle_pointer_on_stack_2(void)
277 {
278 	asm volatile ("					\
279 	*(u64*)(r10 - 8) = r10;				\
280 	r0 = 0;						\
281 	*(u8*)(r10 - 1) = r0;				\
282 	r0 = 0;						\
283 	exit;						\
284 "	::: __clobber_all);
285 }
286 
287 SEC("socket")
288 __description("unpriv: read pointer from stack in small chunks")
289 __failure __msg("invalid size")
290 __failure_unpriv
291 __naked void from_stack_in_small_chunks(void)
292 {
293 	asm volatile ("					\
294 	*(u64*)(r10 - 8) = r10;				\
295 	r0 = *(u32*)(r10 - 8);				\
296 	r0 = 0;						\
297 	exit;						\
298 "	::: __clobber_all);
299 }
300 
301 SEC("socket")
302 __description("unpriv: write pointer into ctx")
303 __failure __msg("invalid bpf_context access")
304 __failure_unpriv __msg_unpriv("R1 leaks addr")
305 __naked void unpriv_write_pointer_into_ctx(void)
306 {
307 	asm volatile ("					\
308 	*(u64*)(r1 + 0) = r1;				\
309 	r0 = 0;						\
310 	exit;						\
311 "	::: __clobber_all);
312 }
313 
314 SEC("socket")
315 __description("unpriv: spill/fill of ctx")
316 __success __success_unpriv __retval(0)
317 __naked void unpriv_spill_fill_of_ctx(void)
318 {
319 	asm volatile ("					\
320 	r6 = r10;					\
321 	r6 += -8;					\
322 	*(u64*)(r6 + 0) = r1;				\
323 	r1 = *(u64*)(r6 + 0);				\
324 	r0 = 0;						\
325 	exit;						\
326 "	::: __clobber_all);
327 }
328 
329 SEC("tc")
330 __description("unpriv: spill/fill of ctx 2")
331 __success __retval(0)
332 __naked void spill_fill_of_ctx_2(void)
333 {
334 	asm volatile ("					\
335 	r6 = r10;					\
336 	r6 += -8;					\
337 	*(u64*)(r6 + 0) = r1;				\
338 	r1 = *(u64*)(r6 + 0);				\
339 	call %[bpf_get_hash_recalc];			\
340 	r0 = 0;						\
341 	exit;						\
342 "	:
343 	: __imm(bpf_get_hash_recalc)
344 	: __clobber_all);
345 }
346 
347 SEC("tc")
348 __description("unpriv: spill/fill of ctx 3")
349 __failure __msg("R1 type=fp expected=ctx")
350 __naked void spill_fill_of_ctx_3(void)
351 {
352 	asm volatile ("					\
353 	r6 = r10;					\
354 	r6 += -8;					\
355 	*(u64*)(r6 + 0) = r1;				\
356 	*(u64*)(r6 + 0) = r10;				\
357 	r1 = *(u64*)(r6 + 0);				\
358 	call %[bpf_get_hash_recalc];			\
359 	exit;						\
360 "	:
361 	: __imm(bpf_get_hash_recalc)
362 	: __clobber_all);
363 }
364 
365 SEC("tc")
366 __description("unpriv: spill/fill of ctx 4")
367 __failure __msg("R1 type=scalar expected=ctx")
368 __naked void spill_fill_of_ctx_4(void)
369 {
370 	asm volatile ("					\
371 	r6 = r10;					\
372 	r6 += -8;					\
373 	*(u64*)(r6 + 0) = r1;				\
374 	r0 = 1;						\
375 	lock *(u64 *)(r10 - 8) += r0;			\
376 	r1 = *(u64*)(r6 + 0);				\
377 	call %[bpf_get_hash_recalc];			\
378 	exit;						\
379 "	:
380 	: __imm(bpf_get_hash_recalc)
381 	: __clobber_all);
382 }
383 
384 SEC("tc")
385 __description("unpriv: spill/fill of different pointers stx")
386 __failure __msg("same insn cannot be used with different pointers")
387 __naked void fill_of_different_pointers_stx(void)
388 {
389 	asm volatile ("					\
390 	r3 = 42;					\
391 	r6 = r10;					\
392 	r6 += -8;					\
393 	if r1 == 0 goto l0_%=;				\
394 	r2 = r10;					\
395 	r2 += -16;					\
396 	*(u64*)(r6 + 0) = r2;				\
397 l0_%=:	if r1 != 0 goto l1_%=;				\
398 	*(u64*)(r6 + 0) = r1;				\
399 l1_%=:	r1 = *(u64*)(r6 + 0);				\
400 	*(u32*)(r1 + %[__sk_buff_mark]) = r3;		\
401 	r0 = 0;						\
402 	exit;						\
403 "	:
404 	: __imm_const(__sk_buff_mark, offsetof(struct __sk_buff, mark))
405 	: __clobber_all);
406 }
407 
408 /* Same as above, but use BPF_ST_MEM to save 42
409  * instead of BPF_STX_MEM.
410  */
411 SEC("tc")
412 __description("unpriv: spill/fill of different pointers st")
413 __failure __msg("same insn cannot be used with different pointers")
414 __naked void fill_of_different_pointers_st(void)
415 {
416 	asm volatile ("					\
417 	r6 = r10;					\
418 	r6 += -8;					\
419 	if r1 == 0 goto l0_%=;				\
420 	r2 = r10;					\
421 	r2 += -16;					\
422 	*(u64*)(r6 + 0) = r2;				\
423 l0_%=:	if r1 != 0 goto l1_%=;				\
424 	*(u64*)(r6 + 0) = r1;				\
425 l1_%=:	r1 = *(u64*)(r6 + 0);				\
426 	.8byte %[st_mem];				\
427 	r0 = 0;						\
428 	exit;						\
429 "	:
430 	: __imm_const(__sk_buff_mark, offsetof(struct __sk_buff, mark)),
431 	  __imm_insn(st_mem,
432 		     BPF_ST_MEM(BPF_W, BPF_REG_1, offsetof(struct __sk_buff, mark), 42))
433 	: __clobber_all);
434 }
435 
436 SEC("tc")
437 __description("unpriv: spill/fill of different pointers stx - ctx and sock")
438 __failure __msg("type=ctx expected=sock")
439 __naked void pointers_stx_ctx_and_sock(void)
440 {
441 	asm volatile ("					\
442 	r8 = r1;					\
443 	/* struct bpf_sock *sock = bpf_sock_lookup(...); */\
444 "	BPF_SK_LOOKUP(bpf_sk_lookup_tcp)
445 "	r2 = r0;					\
446 	/* u64 foo; */					\
447 	/* void *target = &foo; */			\
448 	r6 = r10;					\
449 	r6 += -8;					\
450 	r1 = r8;					\
451 	/* if (skb == NULL) *target = sock; */		\
452 	if r1 == 0 goto l0_%=;				\
453 	*(u64*)(r6 + 0) = r2;				\
454 l0_%=:	/* else *target = skb; */			\
455 	if r1 != 0 goto l1_%=;				\
456 	*(u64*)(r6 + 0) = r1;				\
457 l1_%=:	/* struct __sk_buff *skb = *target; */		\
458 	r1 = *(u64*)(r6 + 0);				\
459 	/* skb->mark = 42; */				\
460 	r3 = 42;					\
461 	*(u32*)(r1 + %[__sk_buff_mark]) = r3;		\
462 	/* if (sk) bpf_sk_release(sk) */		\
463 	if r1 == 0 goto l2_%=;				\
464 	call %[bpf_sk_release];				\
465 l2_%=:	r0 = 0;						\
466 	exit;						\
467 "	:
468 	: __imm(bpf_sk_lookup_tcp),
469 	  __imm(bpf_sk_release),
470 	  __imm_const(__sk_buff_mark, offsetof(struct __sk_buff, mark)),
471 	  __imm_const(sizeof_bpf_sock_tuple, sizeof(struct bpf_sock_tuple))
472 	: __clobber_all);
473 }
474 
475 SEC("tc")
476 __description("unpriv: spill/fill of different pointers stx - leak sock")
477 __failure
478 //.errstr = "same insn cannot be used with different pointers",
479 __msg("Unreleased reference")
480 __naked void different_pointers_stx_leak_sock(void)
481 {
482 	asm volatile ("					\
483 	r8 = r1;					\
484 	/* struct bpf_sock *sock = bpf_sock_lookup(...); */\
485 "	BPF_SK_LOOKUP(bpf_sk_lookup_tcp)
486 "	r2 = r0;					\
487 	/* u64 foo; */					\
488 	/* void *target = &foo; */			\
489 	r6 = r10;					\
490 	r6 += -8;					\
491 	r1 = r8;					\
492 	/* if (skb == NULL) *target = sock; */		\
493 	if r1 == 0 goto l0_%=;				\
494 	*(u64*)(r6 + 0) = r2;				\
495 l0_%=:	/* else *target = skb; */			\
496 	if r1 != 0 goto l1_%=;				\
497 	*(u64*)(r6 + 0) = r1;				\
498 l1_%=:	/* struct __sk_buff *skb = *target; */		\
499 	r1 = *(u64*)(r6 + 0);				\
500 	/* skb->mark = 42; */				\
501 	r3 = 42;					\
502 	*(u32*)(r1 + %[__sk_buff_mark]) = r3;		\
503 	exit;						\
504 "	:
505 	: __imm(bpf_sk_lookup_tcp),
506 	  __imm_const(__sk_buff_mark, offsetof(struct __sk_buff, mark)),
507 	  __imm_const(sizeof_bpf_sock_tuple, sizeof(struct bpf_sock_tuple))
508 	: __clobber_all);
509 }
510 
511 SEC("tc")
512 __description("unpriv: spill/fill of different pointers stx - sock and ctx (read)")
513 __failure __msg("same insn cannot be used with different pointers")
514 __naked void stx_sock_and_ctx_read(void)
515 {
516 	asm volatile ("					\
517 	r8 = r1;					\
518 	/* struct bpf_sock *sock = bpf_sock_lookup(...); */\
519 "	BPF_SK_LOOKUP(bpf_sk_lookup_tcp)
520 "	r2 = r0;					\
521 	/* u64 foo; */					\
522 	/* void *target = &foo; */			\
523 	r6 = r10;					\
524 	r6 += -8;					\
525 	r1 = r8;					\
526 	/* if (skb) *target = skb */			\
527 	if r1 == 0 goto l0_%=;				\
528 	*(u64*)(r6 + 0) = r1;				\
529 l0_%=:	/* else *target = sock */			\
530 	if r1 != 0 goto l1_%=;				\
531 	*(u64*)(r6 + 0) = r2;				\
532 l1_%=:	/* struct bpf_sock *sk = *target; */		\
533 	r1 = *(u64*)(r6 + 0);				\
534 	/* if (sk) u32 foo = sk->mark; bpf_sk_release(sk); */\
535 	if r1 == 0 goto l2_%=;				\
536 	r3 = *(u32*)(r1 + %[bpf_sock_mark]);		\
537 	call %[bpf_sk_release];				\
538 l2_%=:	r0 = 0;						\
539 	exit;						\
540 "	:
541 	: __imm(bpf_sk_lookup_tcp),
542 	  __imm(bpf_sk_release),
543 	  __imm_const(bpf_sock_mark, offsetof(struct bpf_sock, mark)),
544 	  __imm_const(sizeof_bpf_sock_tuple, sizeof(struct bpf_sock_tuple))
545 	: __clobber_all);
546 }
547 
548 SEC("tc")
549 __description("unpriv: spill/fill of different pointers stx - sock and ctx (write)")
550 __failure
551 //.errstr = "same insn cannot be used with different pointers",
552 __msg("cannot write into sock")
553 __naked void stx_sock_and_ctx_write(void)
554 {
555 	asm volatile ("					\
556 	r8 = r1;					\
557 	/* struct bpf_sock *sock = bpf_sock_lookup(...); */\
558 "	BPF_SK_LOOKUP(bpf_sk_lookup_tcp)
559 "	r2 = r0;					\
560 	/* u64 foo; */					\
561 	/* void *target = &foo; */			\
562 	r6 = r10;					\
563 	r6 += -8;					\
564 	r1 = r8;					\
565 	/* if (skb) *target = skb */			\
566 	if r1 == 0 goto l0_%=;				\
567 	*(u64*)(r6 + 0) = r1;				\
568 l0_%=:	/* else *target = sock */			\
569 	if r1 != 0 goto l1_%=;				\
570 	*(u64*)(r6 + 0) = r2;				\
571 l1_%=:	/* struct bpf_sock *sk = *target; */		\
572 	r1 = *(u64*)(r6 + 0);				\
573 	/* if (sk) sk->mark = 42; bpf_sk_release(sk); */\
574 	if r1 == 0 goto l2_%=;				\
575 	r3 = 42;					\
576 	*(u32*)(r1 + %[bpf_sock_mark]) = r3;		\
577 	call %[bpf_sk_release];				\
578 l2_%=:	r0 = 0;						\
579 	exit;						\
580 "	:
581 	: __imm(bpf_sk_lookup_tcp),
582 	  __imm(bpf_sk_release),
583 	  __imm_const(bpf_sock_mark, offsetof(struct bpf_sock, mark)),
584 	  __imm_const(sizeof_bpf_sock_tuple, sizeof(struct bpf_sock_tuple))
585 	: __clobber_all);
586 }
587 
588 SEC("socket")
589 __description("unpriv: write pointer into map elem value")
590 __success __failure_unpriv __msg_unpriv("R0 leaks addr")
591 __retval(0)
592 __naked void pointer_into_map_elem_value(void)
593 {
594 	asm volatile ("					\
595 	r1 = 0;						\
596 	*(u64*)(r10 - 8) = r1;				\
597 	r2 = r10;					\
598 	r2 += -8;					\
599 	r1 = %[map_hash_8b] ll;				\
600 	call %[bpf_map_lookup_elem];			\
601 	if r0 == 0 goto l0_%=;				\
602 	*(u64*)(r0 + 0) = r0;				\
603 l0_%=:	exit;						\
604 "	:
605 	: __imm(bpf_map_lookup_elem),
606 	  __imm_addr(map_hash_8b)
607 	: __clobber_all);
608 }
609 
610 SEC("socket")
611 __description("alu32: mov u32 const")
612 __success __success_unpriv
613 __retval(0)
614 #ifdef SPEC_V1
615 __xlated_unpriv("if r0 == 0x0 goto pc+2")
616 __xlated_unpriv("nospec") /* inserted to prevent `R7 invalid mem access 'scalar'` */
617 __xlated_unpriv("goto pc-1") /* sanitized dead code */
618 __xlated_unpriv("exit")
619 #endif
620 __naked void alu32_mov_u32_const(void)
621 {
622 	asm volatile ("					\
623 	w7 = 0;						\
624 	w7 ^= w7;					\
625 	w0 = w7;					\
626 	if r0 == 0 goto l0_%=;				\
627 	r0 = *(u64*)(r7 + 0);				\
628 l0_%=:	exit;						\
629 "	::: __clobber_all);
630 }
631 
632 SEC("socket")
633 __description("unpriv: partial copy of pointer")
634 __success __failure_unpriv __msg_unpriv("R10 partial copy")
635 __retval(0)
636 __naked void unpriv_partial_copy_of_pointer(void)
637 {
638 	asm volatile ("					\
639 	w1 = w10;					\
640 	r0 = 0;						\
641 	exit;						\
642 "	::: __clobber_all);
643 }
644 
645 SEC("socket")
646 __description("unpriv: pass pointer to tail_call")
647 __success __failure_unpriv __msg_unpriv("R3 leaks addr into helper")
648 __retval(0)
649 __naked void pass_pointer_to_tail_call(void)
650 {
651 	asm volatile ("					\
652 	r3 = r1;					\
653 	r2 = %[map_prog1_socket] ll;			\
654 	call %[bpf_tail_call];				\
655 	r0 = 0;						\
656 	exit;						\
657 "	:
658 	: __imm(bpf_tail_call),
659 	  __imm_addr(map_prog1_socket)
660 	: __clobber_all);
661 }
662 
663 SEC("socket")
664 __description("unpriv: cmp map pointer with zero")
665 __success __success_unpriv
666 __retval(0)
667 __naked void cmp_map_pointer_with_zero(void)
668 {
669 	asm volatile ("					\
670 	r1 = %[map_hash_8b] ll;				\
671 	if r1 == 0 goto l0_%=;				\
672 l0_%=:	r0 = 0;						\
673 	exit;						\
674 "	:
675 	: __imm_addr(map_hash_8b)
676 	: __clobber_all);
677 }
678 
679 SEC("socket")
680 __description("unpriv: cmp map pointer with const")
681 __success __failure_unpriv __msg_unpriv("R1 pointer comparison prohibited")
682 __retval(0)
683 __naked void cmp_map_pointer_with_const(void)
684 {
685 	asm volatile ("					\
686 	r1 = %[map_hash_8b] ll;				\
687 	if r1 == 0x0000beef goto l0_%=;			\
688 l0_%=:	r0 = 0;						\
689 	exit;						\
690 "	:
691 	: __imm_addr(map_hash_8b)
692 	: __clobber_all);
693 }
694 
695 SEC("socket")
696 __description("unpriv: write into frame pointer")
697 __failure __msg("frame pointer is read only")
698 __failure_unpriv
699 __naked void unpriv_write_into_frame_pointer(void)
700 {
701 	asm volatile ("					\
702 	r10 = r1;					\
703 	r0 = 0;						\
704 	exit;						\
705 "	::: __clobber_all);
706 }
707 
708 SEC("socket")
709 __description("unpriv: spill/fill frame pointer")
710 __failure __msg("frame pointer is read only")
711 __failure_unpriv
712 __naked void unpriv_spill_fill_frame_pointer(void)
713 {
714 	asm volatile ("					\
715 	r6 = r10;					\
716 	r6 += -8;					\
717 	*(u64*)(r6 + 0) = r10;				\
718 	r10 = *(u64*)(r6 + 0);				\
719 	r0 = 0;						\
720 	exit;						\
721 "	::: __clobber_all);
722 }
723 
724 SEC("socket")
725 __description("unpriv: cmp of frame pointer")
726 __success __failure_unpriv __msg_unpriv("R10 pointer comparison")
727 __retval(0)
728 __naked void unpriv_cmp_of_frame_pointer(void)
729 {
730 	asm volatile ("					\
731 	if r10 == 0 goto l0_%=;				\
732 l0_%=:	r0 = 0;						\
733 	exit;						\
734 "	::: __clobber_all);
735 }
736 
737 SEC("socket")
738 __description("unpriv: adding of fp, reg")
739 __success __failure_unpriv
740 __msg_unpriv("R1 stack pointer arithmetic goes out of range")
741 __retval(0)
742 __naked void unpriv_adding_of_fp_reg(void)
743 {
744 	asm volatile ("					\
745 	r0 = 0;						\
746 	r1 = 0;						\
747 	r1 += r10;					\
748 	*(u64*)(r1 - 8) = r0;				\
749 	exit;						\
750 "	::: __clobber_all);
751 }
752 
753 SEC("socket")
754 __description("unpriv: adding of fp, imm")
755 __success __failure_unpriv
756 __msg_unpriv("R1 stack pointer arithmetic goes out of range")
757 __retval(0)
758 __naked void unpriv_adding_of_fp_imm(void)
759 {
760 	asm volatile ("					\
761 	r0 = 0;						\
762 	r1 = r10;					\
763 	r1 += 0;					\
764 	*(u64*)(r1 - 8) = r0;				\
765 	exit;						\
766 "	::: __clobber_all);
767 }
768 
769 SEC("socket")
770 __description("unpriv: cmp of stack pointer")
771 __success __failure_unpriv __msg_unpriv("R2 pointer comparison")
772 __retval(0)
773 __naked void unpriv_cmp_of_stack_pointer(void)
774 {
775 	asm volatile ("					\
776 	r2 = r10;					\
777 	r2 += -8;					\
778 	if r2 == 0 goto l0_%=;				\
779 l0_%=:	r0 = 0;						\
780 	exit;						\
781 "	::: __clobber_all);
782 }
783 
784 SEC("socket")
785 __description("unpriv: Spectre v1 path-based type confusion of scalar as stack-ptr")
786 __success __success_unpriv __retval(0)
787 #ifdef SPEC_V1
788 __xlated_unpriv("if r0 != 0x1 goto pc+2")
789 /* This nospec prevents the exploit because it forces the mispredicted (not
790  * taken) `if r0 != 0x0 goto l0_%=` to resolve before using r6 as a pointer.
791  * This causes the CPU to realize that `r6 = r9` should have never executed. It
792  * ensures that r6 always contains a readable stack slot ptr when the insn after
793  * the nospec executes.
794  */
795 __xlated_unpriv("nospec")
796 __xlated_unpriv("r9 = *(u8 *)(r6 +0)")
797 #endif
798 __naked void unpriv_spec_v1_type_confusion(void)
799 {
800 	asm volatile ("					\
801 	r1 = 0;						\
802 	*(u64*)(r10 - 8) = r1;				\
803 	r2 = r10;					\
804 	r2 += -8;					\
805 	r1 = %[map_hash_8b] ll;				\
806 	call %[bpf_map_lookup_elem];			\
807 	if r0 == 0 goto l2_%=;				\
808 	/* r0: pointer to a map array entry */		\
809 	r2 = r10;					\
810 	r2 += -8;					\
811 	r1 = %[map_hash_8b] ll;				\
812 	/* r1, r2: prepared call args */		\
813 	r6 = r10;					\
814 	r6 += -8;					\
815 	/* r6: pointer to readable stack slot */	\
816 	r9 = 0xffffc900;				\
817 	r9 <<= 32;					\
818 	/* r9: scalar controlled by attacker */		\
819 	r0 = *(u64 *)(r0 + 0); /* cache miss */		\
820 	if r0 != 0x0 goto l0_%=;			\
821 	r6 = r9;					\
822 l0_%=:	if r0 != 0x1 goto l1_%=;			\
823 	r9 = *(u8 *)(r6 + 0);				\
824 l1_%=:  /* leak r9 */					\
825 	r9 &= 1;					\
826 	r9 <<= 9;					\
827 	*(u64*)(r10 - 8) = r9;				\
828 	call %[bpf_map_lookup_elem];			\
829 	if r0 == 0 goto l2_%=;				\
830 	/* leak secret into is_cached(map[0|512]): */	\
831 	r0 = *(u64 *)(r0 + 0);				\
832 l2_%=:							\
833 	r0 = 0;						\
834 	exit;						\
835 "	:
836 	: __imm(bpf_map_lookup_elem),
837 	  __imm_addr(map_hash_8b)
838 	: __clobber_all);
839 }
840 
841 SEC("socket")
842 __description("unpriv: ldimm64 before Spectre v4 barrier")
843 __success __success_unpriv
844 __retval(0)
845 #ifdef SPEC_V4
846 __xlated_unpriv("r1 = 0x2020200005642020") /* should not matter */
847 __xlated_unpriv("*(u64 *)(r10 -8) = r1")
848 __xlated_unpriv("nospec")
849 #endif
850 __naked void unpriv_ldimm64_spectre_v4(void)
851 {
852 	asm volatile ("					\
853 	r1 = 0x2020200005642020 ll;			\
854 	*(u64 *)(r10 -8) = r1;				\
855 	r0 = 0;						\
856 	exit;						\
857 "	::: __clobber_all);
858 }
859 
860 SEC("socket")
861 __description("unpriv: Spectre v1 and v4 barrier")
862 __success __success_unpriv
863 __retval(0)
864 #ifdef SPEC_V1
865 #ifdef SPEC_V4
866 /* starts with r0 == r8 == r9 == 0 */
867 __xlated_unpriv("if r8 != 0x0 goto pc+1")
868 __xlated_unpriv("goto pc+2")
869 __xlated_unpriv("if r9 == 0x0 goto pc+4")
870 __xlated_unpriv("r2 = r0")
871 /* Following nospec required to prevent following dangerous `*(u64 *)(NOT_FP -64)
872  * = r1` iff `if r9 == 0 goto pc+4` was mispredicted because of Spectre v1. The
873  * test therefore ensures the Spectre-v4--induced nospec does not prevent the
874  * Spectre-v1--induced speculative path from being fully analyzed.
875  */
876 __xlated_unpriv("nospec") /* Spectre v1 */
877 __xlated_unpriv("*(u64 *)(r2 -64) = r1") /* could be used to leak r2 */
878 __xlated_unpriv("nospec") /* Spectre v4 */
879 #endif
880 #endif
881 __naked void unpriv_spectre_v1_and_v4(void)
882 {
883 	asm volatile ("					\
884 	r1 = 0;						\
885 	*(u64*)(r10 - 8) = r1;				\
886 	r2 = r10;					\
887 	r2 += -8;					\
888 	r1 = %[map_hash_8b] ll;				\
889 	call %[bpf_map_lookup_elem];			\
890 	r8 = r0;					\
891 	r2 = r10;					\
892 	r2 += -8;					\
893 	r1 = %[map_hash_8b] ll;				\
894 	call %[bpf_map_lookup_elem];			\
895 	r9 = r0;					\
896 	r0 = r10;					\
897 	r1 = 0;						\
898 	r2 = r10;					\
899 	if r8 != 0 goto l0_%=;				\
900 	if r9 != 0 goto l0_%=;				\
901 	r0 = 0;						\
902 l0_%=:	if r8 != 0 goto l1_%=;				\
903 	goto l2_%=;					\
904 l1_%=:	if r9 == 0 goto l3_%=;				\
905 	r2 = r0;					\
906 l2_%=:	*(u64 *)(r2 -64) = r1;				\
907 l3_%=:	r0 = 0;						\
908 	exit;						\
909 "	:
910 	: __imm(bpf_map_lookup_elem),
911 	  __imm_addr(map_hash_8b)
912 	: __clobber_all);
913 }
914 
915 SEC("socket")
916 __description("unpriv: Spectre v1 and v4 barrier (simple)")
917 __success __success_unpriv
918 __retval(0)
919 #ifdef SPEC_V1
920 #ifdef SPEC_V4
921 __xlated_unpriv("if r8 != 0x0 goto pc+1")
922 __xlated_unpriv("goto pc+2")
923 __xlated_unpriv("goto pc-1") /* if r9 == 0 goto l3_%= */
924 __xlated_unpriv("goto pc-1") /* r2 = r0 */
925 __xlated_unpriv("nospec")
926 __xlated_unpriv("*(u64 *)(r2 -64) = r1")
927 __xlated_unpriv("nospec")
928 #endif
929 #endif
930 __naked void unpriv_spectre_v1_and_v4_simple(void)
931 {
932 	asm volatile ("					\
933 	r8 = 0;						\
934 	r8 ^= r8;					\
935 	r9 = 0;						\
936 	r9 ^= r9;					\
937 	r0 = r10;					\
938 	r1 = 0;						\
939 	r2 = r10;					\
940 	if r8 != 0 goto l0_%=;				\
941 	if r9 != 0 goto l0_%=;				\
942 	r0 = 0;						\
943 l0_%=:	if r8 != 0 goto l1_%=;				\
944 	goto l2_%=;					\
945 l1_%=:	if r9 == 0 goto l3_%=;				\
946 	r2 = r0;					\
947 l2_%=:	*(u64 *)(r2 -64) = r1;				\
948 l3_%=:	r0 = 0;						\
949 	exit;						\
950 "	::: __clobber_all);
951 }
952 
953 SEC("socket")
954 __description("unpriv: ldimm64 before Spectre v1 and v4 barrier (simple)")
955 __success __success_unpriv
956 __retval(0)
957 #ifdef SPEC_V1
958 #ifdef SPEC_V4
959 __xlated_unpriv("if r8 != 0x0 goto pc+1")
960 __xlated_unpriv("goto pc+4")
961 __xlated_unpriv("goto pc-1") /* if r9 == 0 goto l3_%= */
962 __xlated_unpriv("goto pc-1") /* r2 = r0 */
963 __xlated_unpriv("goto pc-1") /* r1 = 0x2020200005642020 ll */
964 __xlated_unpriv("goto pc-1") /* second part of ldimm64 */
965 __xlated_unpriv("nospec")
966 __xlated_unpriv("*(u64 *)(r2 -64) = r1")
967 __xlated_unpriv("nospec")
968 #endif
969 #endif
970 __naked void unpriv_ldimm64_spectre_v1_and_v4_simple(void)
971 {
972 	asm volatile ("					\
973 	r8 = 0;						\
974 	r8 ^= r8;					\
975 	r9 = 0;						\
976 	r9 ^= r9;					\
977 	r0 = r10;					\
978 	r1 = 0;						\
979 	r2 = r10;					\
980 	if r8 != 0 goto l0_%=;				\
981 	if r9 != 0 goto l0_%=;				\
982 	r0 = 0;						\
983 l0_%=:	if r8 != 0 goto l1_%=;				\
984 	goto l2_%=;					\
985 l1_%=:	if r9 == 0 goto l3_%=;				\
986 	r2 = r0;					\
987 	r1 = 0x2020200005642020 ll;			\
988 l2_%=:	*(u64 *)(r2 -64) = r1;				\
989 l3_%=:	r0 = 0;						\
990 	exit;						\
991 "	::: __clobber_all);
992 }
993 
994 SEC("socket")
995 __description("unpriv: nospec after dead stack write in helper")
996 __success __success_unpriv
997 __retval(0)
998 /* Dead code sanitizer rewrites the call to `goto -1`. */
999 __naked void unpriv_dead_helper_stack_write_nospec_result(void)
1000 {
1001 	asm volatile ("					\
1002 	r0 = 0;						\
1003 	if r0 != 1 goto l0_%=;				\
1004 	r2 = 0;						\
1005 	r3 = r10;					\
1006 	r3 += -16;					\
1007 	r4 = 4;						\
1008 	r5 = 0;						\
1009 	call %[bpf_skb_load_bytes_relative];		\
1010 l0_%=:	exit;						\
1011 "	:
1012 	: __imm(bpf_skb_load_bytes_relative)
1013 	: __clobber_all);
1014 }
1015 
1016 SEC("socket")
1017 __description("unpriv: Spectre v4 stack write slot index")
1018 __success __success_unpriv
1019 __retval(0)
1020 #ifdef SPEC_V4
1021 __xlated_unpriv("r0 = 0")
1022 __xlated_unpriv("*(u32 *)(r10 -4) = r0")
1023 __xlated_unpriv("nospec")
1024 __xlated_unpriv("*(u32 *)(r10 -8) = r0")
1025 __xlated_unpriv("nospec")
1026 __xlated_unpriv("exit")
1027 #endif
1028 __naked void stack_write_nospec_slot_index(void)
1029 {
1030 	asm volatile ("					\
1031 	r0 = 0;					\
1032 	*(u32 *)(r10 - 4) = r0;			\
1033 	*(u32 *)(r10 - 8) = r0;			\
1034 	exit;					\
1035 "	::: __clobber_all);
1036 }
1037 
1038 __noinline int nospec_global_func(int x)
1039 {
1040 	return x + 1;
1041 }
1042 
1043 __used __naked static void nospec_call_subprog(void)
1044 {
1045 	asm volatile ("					\
1046 	call %[bpf_get_prandom_u32];			\
1047 	r0 &= 0xff;					\
1048 	r1 = 0;						\
1049 	if r0 < 0x100 goto l0_%=;			\
1050 	/* executed only speculatively */		\
1051 	r1 = r10;					\
1052 l0_%=:	call nospec_global_func;			\
1053 	exit;						\
1054 "	:
1055 	: __imm(bpf_get_prandom_u32)
1056 	: __clobber_all);
1057 }
1058 
1059 /* nospec_global_func is placed in front of nospec_call_subprog */
1060 SEC("socket")
1061 __description("unpriv: nospec in front of a call")
1062 __success __success_unpriv
1063 __caps_unpriv(CAP_BPF)
1064 __retval(1)
1065 __naked void nospec_backward_call(void)
1066 {
1067 	asm volatile ("					\
1068 	r1 = 0;						\
1069 	call nospec_global_func;			\
1070 	call nospec_call_subprog;			\
1071 	exit;						\
1072 "	::: __clobber_all);
1073 }
1074 
1075 char _license[] SEC("license") = "GPL";
1076