1// SPDX-License-Identifier: GPL-2.0
2/* Converted from tools/testing/selftests/bpf/verifier/var_off.c */
3
4#include <linux/bpf.h>
5#include <bpf/bpf_helpers.h>
6#include "bpf_misc.h"
7
8struct {
9	__uint(type, BPF_MAP_TYPE_HASH);
10	__uint(max_entries, 1);
11	__type(key, long long);
12	__type(value, long long);
13} map_hash_8b SEC(".maps");
14
15SEC("lwt_in")
16__description("variable-offset ctx access")
17__failure __msg("variable ctx access var_off=(0x0; 0x4)")
18__naked void variable_offset_ctx_access(void)
19{
20	asm volatile ("					\
21	/* Get an unknown value */			\
22	r2 = *(u32*)(r1 + 0);				\
23	/* Make it small and 4-byte aligned */		\
24	r2 &= 4;					\
25	/* add it to skb.  We now have either &skb->len or\
26	 * &skb->pkt_type, but we don't know which	\
27	 */						\
28	r1 += r2;					\
29	/* dereference it */				\
30	r0 = *(u32*)(r1 + 0);				\
31	exit;						\
32"	::: __clobber_all);
33}
34
35SEC("cgroup/skb")
36__description("variable-offset stack read, priv vs unpriv")
37__success __failure_unpriv
38__msg_unpriv("R2 variable stack access prohibited for !root")
39__retval(0)
40__naked void stack_read_priv_vs_unpriv(void)
41{
42	asm volatile ("					\
43	/* Fill the top 8 bytes of the stack */		\
44	r0 = 0;						\
45	*(u64*)(r10 - 8) = r0;				\
46	/* Get an unknown value */			\
47	r2 = *(u32*)(r1 + 0);				\
48	/* Make it small and 4-byte aligned */		\
49	r2 &= 4;					\
50	r2 -= 8;					\
51	/* add it to fp.  We now have either fp-4 or fp-8, but\
52	 * we don't know which				\
53	 */						\
54	r2 += r10;					\
55	/* dereference it for a stack read */		\
56	r0 = *(u32*)(r2 + 0);				\
57	r0 = 0;						\
58	exit;						\
59"	::: __clobber_all);
60}
61
62SEC("cgroup/skb")
63__description("variable-offset stack read, uninitialized")
64__success
65__failure_unpriv __msg_unpriv("R2 variable stack access prohibited for !root")
66__naked void variable_offset_stack_read_uninitialized(void)
67{
68	asm volatile ("					\
69	/* Get an unknown value */			\
70	r2 = *(u32*)(r1 + 0);				\
71	/* Make it small and 4-byte aligned */		\
72	r2 &= 4;					\
73	r2 -= 8;					\
74	/* add it to fp.  We now have either fp-4 or fp-8, but\
75	 * we don't know which				\
76	 */						\
77	r2 += r10;					\
78	/* dereference it for a stack read */		\
79	r0 = *(u32*)(r2 + 0);				\
80	r0 = 0;						\
81	exit;						\
82"	::: __clobber_all);
83}
84
85SEC("socket")
86__description("variable-offset stack write, priv vs unpriv")
87__success
88/* Check that the maximum stack depth is correctly maintained according to the
89 * maximum possible variable offset.
90 */
91__log_level(4) __msg("stack depth 16")
92__failure_unpriv
93/* Variable stack access is rejected for unprivileged.
94 */
95__msg_unpriv("R2 variable stack access prohibited for !root")
96__retval(0)
97__naked void stack_write_priv_vs_unpriv(void)
98{
99	asm volatile ("                               \
100	/* Get an unknown value */                    \
101	r2 = *(u32*)(r1 + 0);                         \
102	/* Make it small and 8-byte aligned */        \
103	r2 &= 8;                                      \
104	r2 -= 16;                                     \
105	/* Add it to fp. We now have either fp-8 or   \
106	 * fp-16, but we don't know which             \
107	 */                                           \
108	r2 += r10;                                    \
109	/* Dereference it for a stack write */        \
110	r0 = 0;                                       \
111	*(u64*)(r2 + 0) = r0;                         \
112	exit;                                         \
113"	::: __clobber_all);
114}
115
116/* Similar to the previous test, but this time also perform a read from the
117 * address written to with a variable offset. The read is allowed, showing that,
118 * after a variable-offset write, a priviledged program can read the slots that
119 * were in the range of that write (even if the verifier doesn't actually know if
120 * the slot being read was really written to or not.
121 *
122 * Despite this test being mostly a superset, the previous test is also kept for
123 * the sake of it checking the stack depth in the case where there is no read.
124 */
125SEC("socket")
126__description("variable-offset stack write followed by read")
127__success
128/* Check that the maximum stack depth is correctly maintained according to the
129 * maximum possible variable offset.
130 */
131__log_level(4) __msg("stack depth 16")
132__failure_unpriv
133__msg_unpriv("R2 variable stack access prohibited for !root")
134__retval(0)
135__naked void stack_write_followed_by_read(void)
136{
137	asm volatile ("					\
138	/* Get an unknown value */			\
139	r2 = *(u32*)(r1 + 0);				\
140	/* Make it small and 8-byte aligned */		\
141	r2 &= 8;					\
142	r2 -= 16;					\
143	/* Add it to fp.  We now have either fp-8 or fp-16, but\
144	 * we don't know which				\
145	 */						\
146	r2 += r10;					\
147	/* Dereference it for a stack write */		\
148	r0 = 0;						\
149	*(u64*)(r2 + 0) = r0;				\
150	/* Now read from the address we just wrote. */ \
151	r3 = *(u64*)(r2 + 0);				\
152	r0 = 0;						\
153	exit;						\
154"	::: __clobber_all);
155}
156
157SEC("socket")
158__description("variable-offset stack write clobbers spilled regs")
159__failure
160/* In the priviledged case, dereferencing a spilled-and-then-filled
161 * register is rejected because the previous variable offset stack
162 * write might have overwritten the spilled pointer (i.e. we lose track
163 * of the spilled register when we analyze the write).
164 */
165__msg("R2 invalid mem access 'scalar'")
166__failure_unpriv
167/* The unprivileged case is not too interesting; variable
168 * stack access is rejected.
169 */
170__msg_unpriv("R2 variable stack access prohibited for !root")
171__naked void stack_write_clobbers_spilled_regs(void)
172{
173	asm volatile ("					\
174	/* Dummy instruction; needed because we need to patch the next one\
175	 * and we can't patch the first instruction.	\
176	 */						\
177	r6 = 0;						\
178	/* Make R0 a map ptr */				\
179	r0 = %[map_hash_8b] ll;				\
180	/* Get an unknown value */			\
181	r2 = *(u32*)(r1 + 0);				\
182	/* Make it small and 8-byte aligned */		\
183	r2 &= 8;					\
184	r2 -= 16;					\
185	/* Add it to fp. We now have either fp-8 or fp-16, but\
186	 * we don't know which.				\
187	 */						\
188	r2 += r10;					\
189	/* Spill R0(map ptr) into stack */		\
190	*(u64*)(r10 - 8) = r0;				\
191	/* Dereference the unknown value for a stack write */\
192	r0 = 0;						\
193	*(u64*)(r2 + 0) = r0;				\
194	/* Fill the register back into R2 */		\
195	r2 = *(u64*)(r10 - 8);				\
196	/* Try to dereference R2 for a memory load */	\
197	r0 = *(u64*)(r2 + 8);				\
198	exit;						\
199"	:
200	: __imm_addr(map_hash_8b)
201	: __clobber_all);
202}
203
204SEC("sockops")
205__description("indirect variable-offset stack access, unbounded")
206__failure __msg("invalid unbounded variable-offset indirect access to stack R4")
207__naked void variable_offset_stack_access_unbounded(void)
208{
209	asm volatile ("					\
210	r2 = 6;						\
211	r3 = 28;					\
212	/* Fill the top 16 bytes of the stack. */	\
213	r4 = 0;						\
214	*(u64*)(r10 - 16) = r4;				\
215	r4 = 0;						\
216	*(u64*)(r10 - 8) = r4;				\
217	/* Get an unknown value. */			\
218	r4 = *(u64*)(r1 + %[bpf_sock_ops_bytes_received]);\
219	/* Check the lower bound but don't check the upper one. */\
220	if r4 s< 0 goto l0_%=;				\
221	/* Point the lower bound to initialized stack. Offset is now in range\
222	 * from fp-16 to fp+0x7fffffffffffffef, i.e. max value is unbounded.\
223	 */						\
224	r4 -= 16;					\
225	r4 += r10;					\
226	r5 = 8;						\
227	/* Dereference it indirectly. */		\
228	call %[bpf_getsockopt];				\
229l0_%=:	r0 = 0;						\
230	exit;						\
231"	:
232	: __imm(bpf_getsockopt),
233	  __imm_const(bpf_sock_ops_bytes_received, offsetof(struct bpf_sock_ops, bytes_received))
234	: __clobber_all);
235}
236
237SEC("lwt_in")
238__description("indirect variable-offset stack access, max out of bound")
239__failure __msg("invalid variable-offset indirect access to stack R2")
240__naked void access_max_out_of_bound(void)
241{
242	asm volatile ("					\
243	/* Fill the top 8 bytes of the stack */		\
244	r2 = 0;						\
245	*(u64*)(r10 - 8) = r2;				\
246	/* Get an unknown value */			\
247	r2 = *(u32*)(r1 + 0);				\
248	/* Make it small and 4-byte aligned */		\
249	r2 &= 4;					\
250	r2 -= 8;					\
251	/* add it to fp.  We now have either fp-4 or fp-8, but\
252	 * we don't know which				\
253	 */						\
254	r2 += r10;					\
255	/* dereference it indirectly */			\
256	r1 = %[map_hash_8b] ll;				\
257	call %[bpf_map_lookup_elem];			\
258	r0 = 0;						\
259	exit;						\
260"	:
261	: __imm(bpf_map_lookup_elem),
262	  __imm_addr(map_hash_8b)
263	: __clobber_all);
264}
265
266/* Similar to the test above, but this time check the special case of a
267 * zero-sized stack access. We used to have a bug causing crashes for zero-sized
268 * out-of-bounds accesses.
269 */
270SEC("socket")
271__description("indirect variable-offset stack access, zero-sized, max out of bound")
272__failure __msg("invalid variable-offset indirect access to stack R1")
273__naked void zero_sized_access_max_out_of_bound(void)
274{
275	asm volatile ("                      \
276	r0 = 0;                              \
277	/* Fill some stack */                \
278	*(u64*)(r10 - 16) = r0;              \
279	*(u64*)(r10 - 8) = r0;               \
280	/* Get an unknown value */           \
281	r1 = *(u32*)(r1 + 0);                \
282	r1 &= 63;                            \
283	r1 += -16;                           \
284	/* r1 is now anywhere in [-16,48) */ \
285	r1 += r10;                           \
286	r2 = 0;                              \
287	r3 = 0;                              \
288	call %[bpf_probe_read_kernel];       \
289	exit;                                \
290"	:
291	: __imm(bpf_probe_read_kernel)
292	: __clobber_all);
293}
294
295SEC("lwt_in")
296__description("indirect variable-offset stack access, min out of bound")
297__failure __msg("invalid variable-offset indirect access to stack R2")
298__naked void access_min_out_of_bound(void)
299{
300	asm volatile ("					\
301	/* Fill the top 8 bytes of the stack */		\
302	r2 = 0;						\
303	*(u64*)(r10 - 8) = r2;				\
304	/* Get an unknown value */			\
305	r2 = *(u32*)(r1 + 0);				\
306	/* Make it small and 4-byte aligned */		\
307	r2 &= 4;					\
308	r2 -= 516;					\
309	/* add it to fp.  We now have either fp-516 or fp-512, but\
310	 * we don't know which				\
311	 */						\
312	r2 += r10;					\
313	/* dereference it indirectly */			\
314	r1 = %[map_hash_8b] ll;				\
315	call %[bpf_map_lookup_elem];			\
316	r0 = 0;						\
317	exit;						\
318"	:
319	: __imm(bpf_map_lookup_elem),
320	  __imm_addr(map_hash_8b)
321	: __clobber_all);
322}
323
324SEC("cgroup/skb")
325__description("indirect variable-offset stack access, min_off < min_initialized")
326__success
327__failure_unpriv __msg_unpriv("R2 variable stack access prohibited for !root")
328__naked void access_min_off_min_initialized(void)
329{
330	asm volatile ("					\
331	/* Fill only the top 8 bytes of the stack. */	\
332	r2 = 0;						\
333	*(u64*)(r10 - 8) = r2;				\
334	/* Get an unknown value */			\
335	r2 = *(u32*)(r1 + 0);				\
336	/* Make it small and 4-byte aligned. */		\
337	r2 &= 4;					\
338	r2 -= 16;					\
339	/* Add it to fp.  We now have either fp-12 or fp-16, but we don't know\
340	 * which. fp-16 size 8 is partially uninitialized stack.\
341	 */						\
342	r2 += r10;					\
343	/* Dereference it indirectly. */		\
344	r1 = %[map_hash_8b] ll;				\
345	call %[bpf_map_lookup_elem];			\
346	r0 = 0;						\
347	exit;						\
348"	:
349	: __imm(bpf_map_lookup_elem),
350	  __imm_addr(map_hash_8b)
351	: __clobber_all);
352}
353
354SEC("cgroup/skb")
355__description("indirect variable-offset stack access, priv vs unpriv")
356__success __failure_unpriv
357__msg_unpriv("R2 variable stack access prohibited for !root")
358__retval(0)
359__naked void stack_access_priv_vs_unpriv(void)
360{
361	asm volatile ("					\
362	/* Fill the top 16 bytes of the stack. */	\
363	r2 = 0;						\
364	*(u64*)(r10 - 16) = r2;				\
365	r2 = 0;						\
366	*(u64*)(r10 - 8) = r2;				\
367	/* Get an unknown value. */			\
368	r2 = *(u32*)(r1 + 0);				\
369	/* Make it small and 4-byte aligned. */		\
370	r2 &= 4;					\
371	r2 -= 16;					\
372	/* Add it to fp.  We now have either fp-12 or fp-16, we don't know\
373	 * which, but either way it points to initialized stack.\
374	 */						\
375	r2 += r10;					\
376	/* Dereference it indirectly. */		\
377	r1 = %[map_hash_8b] ll;				\
378	call %[bpf_map_lookup_elem];			\
379	r0 = 0;						\
380	exit;						\
381"	:
382	: __imm(bpf_map_lookup_elem),
383	  __imm_addr(map_hash_8b)
384	: __clobber_all);
385}
386
387SEC("lwt_in")
388__description("indirect variable-offset stack access, ok")
389__success __retval(0)
390__naked void variable_offset_stack_access_ok(void)
391{
392	asm volatile ("					\
393	/* Fill the top 16 bytes of the stack. */	\
394	r2 = 0;						\
395	*(u64*)(r10 - 16) = r2;				\
396	r2 = 0;						\
397	*(u64*)(r10 - 8) = r2;				\
398	/* Get an unknown value. */			\
399	r2 = *(u32*)(r1 + 0);				\
400	/* Make it small and 4-byte aligned. */		\
401	r2 &= 4;					\
402	r2 -= 16;					\
403	/* Add it to fp.  We now have either fp-12 or fp-16, we don't know\
404	 * which, but either way it points to initialized stack.\
405	 */						\
406	r2 += r10;					\
407	/* Dereference it indirectly. */		\
408	r1 = %[map_hash_8b] ll;				\
409	call %[bpf_map_lookup_elem];			\
410	r0 = 0;						\
411	exit;						\
412"	:
413	: __imm(bpf_map_lookup_elem),
414	  __imm_addr(map_hash_8b)
415	: __clobber_all);
416}
417
418char _license[] SEC("license") = "GPL";
419