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