xref: /linux/tools/testing/selftests/bpf/progs/mem_rdonly_untrusted.c (revision 0a91336e287ca2557fead5221d2c79e0effd034e)
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 "../test_kmods/bpf_testmod_kfunc.h"
7 
8 SEC("tp_btf/sys_enter")
9 __success
10 __log_level(2)
11 __msg("r8 = *(u64 *)(r7 +0)          ; R7_w=ptr_nameidata(off={{[0-9]+}}) R8_w=rdonly_untrusted_mem(sz=0)")
12 __msg("r9 = *(u8 *)(r8 +0)           ; R8_w=rdonly_untrusted_mem(sz=0) R9_w=scalar")
btf_id_to_ptr_mem(void * ctx)13 int btf_id_to_ptr_mem(void *ctx)
14 {
15 	struct task_struct *task;
16 	struct nameidata *idata;
17 	u64 ret, off;
18 
19 	task = bpf_get_current_task_btf();
20 	idata = task->nameidata;
21 	off = bpf_core_field_offset(struct nameidata, pathname);
22 	/*
23 	 * asm block to have reliable match target for __msg, equivalent of:
24 	 *   ret = task->nameidata->pathname[0];
25 	 */
26 	asm volatile (
27 	"r7 = %[idata];"
28 	"r7 += %[off];"
29 	"r8 = *(u64 *)(r7 + 0);"
30 	"r9 = *(u8 *)(r8 + 0);"
31 	"%[ret] = r9;"
32 	: [ret]"=r"(ret)
33 	: [idata]"r"(idata),
34 	  [off]"r"(off)
35 	: "r7", "r8", "r9");
36 	return ret;
37 }
38 
39 SEC("socket")
40 __success
41 __retval(0)
ldx_is_ok_bad_addr(void * ctx)42 int ldx_is_ok_bad_addr(void *ctx)
43 {
44 	char *p;
45 
46 	if (!bpf_core_enum_value_exists(enum bpf_features, BPF_FEAT_RDONLY_CAST_TO_VOID))
47 		return 42;
48 
49 	p = bpf_rdonly_cast(0, 0);
50 	return p[0x7fff];
51 }
52 
53 SEC("socket")
54 __success
55 __retval(1)
ldx_is_ok_good_addr(void * ctx)56 int ldx_is_ok_good_addr(void *ctx)
57 {
58 	int v, *p;
59 
60 	v = 1;
61 	p = bpf_rdonly_cast(&v, 0);
62 	return *p;
63 }
64 
65 SEC("socket")
66 __success
offset_not_tracked(void * ctx)67 int offset_not_tracked(void *ctx)
68 {
69 	int *p, i, s;
70 
71 	p = bpf_rdonly_cast(0, 0);
72 	s = 0;
73 	bpf_for(i, 0, 1000 * 1000 * 1000) {
74 		p++;
75 		s += *p;
76 	}
77 	return s;
78 }
79 
80 SEC("socket")
81 __failure
82 __msg("cannot write into rdonly_untrusted_mem")
stx_not_ok(void * ctx)83 int stx_not_ok(void *ctx)
84 {
85 	int v, *p;
86 
87 	v = 1;
88 	p = bpf_rdonly_cast(&v, 0);
89 	*p = 1;
90 	return 0;
91 }
92 
93 SEC("socket")
94 __failure
95 __msg("cannot write into rdonly_untrusted_mem")
atomic_not_ok(void * ctx)96 int atomic_not_ok(void *ctx)
97 {
98 	int v, *p;
99 
100 	v = 1;
101 	p = bpf_rdonly_cast(&v, 0);
102 	__sync_fetch_and_add(p, 1);
103 	return 0;
104 }
105 
106 SEC("socket")
107 __failure
108 __msg("cannot write into rdonly_untrusted_mem")
atomic_rmw_not_ok(void * ctx)109 int atomic_rmw_not_ok(void *ctx)
110 {
111 	long v, *p;
112 
113 	v = 1;
114 	p = bpf_rdonly_cast(&v, 0);
115 	return __sync_val_compare_and_swap(p, 0, 42);
116 }
117 
118 SEC("socket")
119 __failure
120 __msg("invalid access to memory, mem_size=0 off=0 size=4")
121 __msg("R1 min value is outside of the allowed memory range")
kfunc_param_not_ok(void * ctx)122 int kfunc_param_not_ok(void *ctx)
123 {
124 	int *p;
125 
126 	p = bpf_rdonly_cast(0, 0);
127 	bpf_kfunc_trusted_num_test(p);
128 	return 0;
129 }
130 
131 SEC("?fentry.s/" SYS_PREFIX "sys_getpgid")
132 __failure
133 __msg("R1 type=rdonly_untrusted_mem expected=")
helper_param_not_ok(void * ctx)134 int helper_param_not_ok(void *ctx)
135 {
136 	char *p;
137 
138 	p = bpf_rdonly_cast(0, 0);
139 	/*
140 	 * Any helper with ARG_CONST_SIZE_OR_ZERO constraint will do,
141 	 * the most permissive constraint
142 	 */
143 	bpf_copy_from_user(p, 0, (void *)42);
144 	return 0;
145 }
146 
get_some_addr(void)147 static __noinline u64 *get_some_addr(void)
148 {
149 	if (bpf_get_prandom_u32())
150 		return bpf_rdonly_cast(0, bpf_core_type_id_kernel(struct sock));
151 	else
152 		return bpf_rdonly_cast(0, 0);
153 }
154 
155 SEC("socket")
156 __success
157 __retval(0)
mixed_mem_type(void * ctx)158 int mixed_mem_type(void *ctx)
159 {
160 	u64 *p;
161 
162 	/* Try to avoid compiler hoisting load to if branches by using __noinline func. */
163 	p = get_some_addr();
164 	return *p;
165 }
166 
167 __attribute__((__aligned__(8)))
168 u8 global[] = {
169 	0x11, 0x22, 0x33, 0x44,
170 	0x55, 0x66, 0x77, 0x88,
171 	0x99
172 };
173 
174 __always_inline
combine(void * p)175 static u64 combine(void *p)
176 {
177 	u64 acc;
178 
179 	acc = 0;
180 #if __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__
181 	acc |= (*(u64 *)p >> 56) << 24;
182 	acc |= (*(u32 *)p >> 24) << 16;
183 	acc |= (*(u16 *)p >> 8)  << 8;
184 	acc |= *(u8 *)p;
185 #else
186 	acc |= (*(u64 *)p & 0xff) << 24;
187 	acc |= (*(u32 *)p & 0xff) << 16;
188 	acc |= (*(u16 *)p & 0xff) << 8;
189 	acc |= *(u8 *)p;
190 #endif
191 	return acc;
192 }
193 
194 SEC("socket")
195 __retval(0x88442211)
diff_size_access(void * ctx)196 int diff_size_access(void *ctx)
197 {
198 	return combine(bpf_rdonly_cast(&global, 0));
199 }
200 
201 SEC("socket")
202 __retval(0x99553322)
misaligned_access(void * ctx)203 int misaligned_access(void *ctx)
204 {
205 	return combine(bpf_rdonly_cast(&global, 0) + 1);
206 }
207 
return_one(void)208 __weak int return_one(void)
209 {
210 	return 1;
211 }
212 
213 SEC("socket")
214 __success
215 __retval(1)
null_check(void * ctx)216 int null_check(void *ctx)
217 {
218 	int *p;
219 
220 	p = bpf_rdonly_cast(0, 0);
221 	if (p == 0)
222 		/* make this a function call to avoid compiler
223 		 * moving r0 assignment before check.
224 		 */
225 		return return_one();
226 	return 0;
227 }
228 
229 char _license[] SEC("license") = "GPL";
230