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