1 | // SPDX-License-Identifier: GPL-2.0 |
2 | /* Converted from tools/testing/selftests/bpf/verifier/search_pruning.c */ |
3 | |
4 | #include <linux/bpf.h> |
5 | #include <bpf/bpf_helpers.h> |
6 | #include "bpf_misc.h" |
7 | |
8 | #define MAX_ENTRIES 11 |
9 | |
10 | struct test_val { |
11 | unsigned int index; |
12 | int foo[MAX_ENTRIES]; |
13 | }; |
14 | |
15 | struct { |
16 | __uint(type, BPF_MAP_TYPE_HASH); |
17 | __uint(max_entries, 1); |
18 | __type(key, long long); |
19 | __type(value, struct test_val); |
20 | } map_hash_48b SEC(".maps" ); |
21 | |
22 | struct { |
23 | __uint(type, BPF_MAP_TYPE_HASH); |
24 | __uint(max_entries, 1); |
25 | __type(key, long long); |
26 | __type(value, long long); |
27 | } map_hash_8b SEC(".maps" ); |
28 | |
29 | SEC("socket" ) |
30 | __description("pointer/scalar confusion in state equality check (way 1)" ) |
31 | __success __failure_unpriv __msg_unpriv("R0 leaks addr as return value" ) |
32 | __retval(POINTER_VALUE) |
33 | __naked void state_equality_check_way_1(void) |
34 | { |
35 | asm volatile (" \ |
36 | r1 = 0; \ |
37 | *(u64*)(r10 - 8) = r1; \ |
38 | r2 = r10; \ |
39 | r2 += -8; \ |
40 | r1 = %[map_hash_8b] ll; \ |
41 | call %[bpf_map_lookup_elem]; \ |
42 | if r0 == 0 goto l0_%=; \ |
43 | r0 = *(u64*)(r0 + 0); \ |
44 | goto l1_%=; \ |
45 | l0_%=: r0 = r10; \ |
46 | l1_%=: goto l2_%=; \ |
47 | l2_%=: exit; \ |
48 | " : |
49 | : __imm(bpf_map_lookup_elem), |
50 | __imm_addr(map_hash_8b) |
51 | : __clobber_all); |
52 | } |
53 | |
54 | SEC("socket" ) |
55 | __description("pointer/scalar confusion in state equality check (way 2)" ) |
56 | __success __failure_unpriv __msg_unpriv("R0 leaks addr as return value" ) |
57 | __retval(POINTER_VALUE) |
58 | __naked void state_equality_check_way_2(void) |
59 | { |
60 | asm volatile (" \ |
61 | r1 = 0; \ |
62 | *(u64*)(r10 - 8) = r1; \ |
63 | r2 = r10; \ |
64 | r2 += -8; \ |
65 | r1 = %[map_hash_8b] ll; \ |
66 | call %[bpf_map_lookup_elem]; \ |
67 | if r0 != 0 goto l0_%=; \ |
68 | r0 = r10; \ |
69 | goto l1_%=; \ |
70 | l0_%=: r0 = *(u64*)(r0 + 0); \ |
71 | l1_%=: exit; \ |
72 | " : |
73 | : __imm(bpf_map_lookup_elem), |
74 | __imm_addr(map_hash_8b) |
75 | : __clobber_all); |
76 | } |
77 | |
78 | SEC("lwt_in" ) |
79 | __description("liveness pruning and write screening" ) |
80 | __failure __msg("R0 !read_ok" ) |
81 | __naked void liveness_pruning_and_write_screening(void) |
82 | { |
83 | asm volatile (" \ |
84 | /* Get an unknown value */ \ |
85 | r2 = *(u32*)(r1 + 0); \ |
86 | /* branch conditions teach us nothing about R2 */\ |
87 | if r2 >= 0 goto l0_%=; \ |
88 | r0 = 0; \ |
89 | l0_%=: if r2 >= 0 goto l1_%=; \ |
90 | r0 = 0; \ |
91 | l1_%=: exit; \ |
92 | " ::: __clobber_all); |
93 | } |
94 | |
95 | SEC("socket" ) |
96 | __description("varlen_map_value_access pruning" ) |
97 | __failure __msg("R0 unbounded memory access" ) |
98 | __failure_unpriv __msg_unpriv("R0 leaks addr" ) |
99 | __flag(BPF_F_ANY_ALIGNMENT) |
100 | __naked void varlen_map_value_access_pruning(void) |
101 | { |
102 | asm volatile (" \ |
103 | r1 = 0; \ |
104 | *(u64*)(r10 - 8) = r1; \ |
105 | r2 = r10; \ |
106 | r2 += -8; \ |
107 | r1 = %[map_hash_48b] ll; \ |
108 | call %[bpf_map_lookup_elem]; \ |
109 | if r0 == 0 goto l0_%=; \ |
110 | r1 = *(u64*)(r0 + 0); \ |
111 | w2 = %[max_entries]; \ |
112 | if r2 s> r1 goto l1_%=; \ |
113 | w1 = 0; \ |
114 | l1_%=: w1 <<= 2; \ |
115 | r0 += r1; \ |
116 | goto l2_%=; \ |
117 | l2_%=: r1 = %[test_val_foo]; \ |
118 | *(u64*)(r0 + 0) = r1; \ |
119 | l0_%=: exit; \ |
120 | " : |
121 | : __imm(bpf_map_lookup_elem), |
122 | __imm_addr(map_hash_48b), |
123 | __imm_const(max_entries, MAX_ENTRIES), |
124 | __imm_const(test_val_foo, offsetof(struct test_val, foo)) |
125 | : __clobber_all); |
126 | } |
127 | |
128 | SEC("tracepoint" ) |
129 | __description("search pruning: all branches should be verified (nop operation)" ) |
130 | __failure __msg("R6 invalid mem access 'scalar'" ) |
131 | __naked void should_be_verified_nop_operation(void) |
132 | { |
133 | asm volatile (" \ |
134 | r2 = r10; \ |
135 | r2 += -8; \ |
136 | r1 = 0; \ |
137 | *(u64*)(r2 + 0) = r1; \ |
138 | r1 = %[map_hash_8b] ll; \ |
139 | call %[bpf_map_lookup_elem]; \ |
140 | if r0 == 0 goto l0_%=; \ |
141 | r3 = *(u64*)(r0 + 0); \ |
142 | if r3 == 0xbeef goto l1_%=; \ |
143 | r4 = 0; \ |
144 | goto l2_%=; \ |
145 | l1_%=: r4 = 1; \ |
146 | l2_%=: *(u64*)(r10 - 16) = r4; \ |
147 | call %[bpf_ktime_get_ns]; \ |
148 | r5 = *(u64*)(r10 - 16); \ |
149 | if r5 == 0 goto l0_%=; \ |
150 | r6 = 0; \ |
151 | r1 = 0xdead; \ |
152 | *(u64*)(r6 + 0) = r1; \ |
153 | l0_%=: exit; \ |
154 | " : |
155 | : __imm(bpf_ktime_get_ns), |
156 | __imm(bpf_map_lookup_elem), |
157 | __imm_addr(map_hash_8b) |
158 | : __clobber_all); |
159 | } |
160 | |
161 | SEC("socket" ) |
162 | __description("search pruning: all branches should be verified (invalid stack access)" ) |
163 | /* in privileged mode reads from uninitialized stack locations are permitted */ |
164 | __success __failure_unpriv |
165 | __msg_unpriv("invalid read from stack off -16+0 size 8" ) |
166 | __retval(0) |
167 | __naked void be_verified_invalid_stack_access(void) |
168 | { |
169 | asm volatile (" \ |
170 | r2 = r10; \ |
171 | r2 += -8; \ |
172 | r1 = 0; \ |
173 | *(u64*)(r2 + 0) = r1; \ |
174 | r1 = %[map_hash_8b] ll; \ |
175 | call %[bpf_map_lookup_elem]; \ |
176 | if r0 == 0 goto l0_%=; \ |
177 | r3 = *(u64*)(r0 + 0); \ |
178 | r4 = 0; \ |
179 | if r3 == 0xbeef goto l1_%=; \ |
180 | *(u64*)(r10 - 16) = r4; \ |
181 | goto l2_%=; \ |
182 | l1_%=: *(u64*)(r10 - 24) = r4; \ |
183 | l2_%=: call %[bpf_ktime_get_ns]; \ |
184 | r5 = *(u64*)(r10 - 16); \ |
185 | l0_%=: exit; \ |
186 | " : |
187 | : __imm(bpf_ktime_get_ns), |
188 | __imm(bpf_map_lookup_elem), |
189 | __imm_addr(map_hash_8b) |
190 | : __clobber_all); |
191 | } |
192 | |
193 | SEC("tracepoint" ) |
194 | __description("precision tracking for u32 spill/fill" ) |
195 | __failure __msg("R0 min value is outside of the allowed memory range" ) |
196 | __naked void tracking_for_u32_spill_fill(void) |
197 | { |
198 | asm volatile (" \ |
199 | r7 = r1; \ |
200 | call %[bpf_get_prandom_u32]; \ |
201 | w6 = 32; \ |
202 | if r0 == 0 goto l0_%=; \ |
203 | w6 = 4; \ |
204 | l0_%=: /* Additional insns to introduce a pruning point. */\ |
205 | call %[bpf_get_prandom_u32]; \ |
206 | r3 = 0; \ |
207 | r3 = 0; \ |
208 | if r0 == 0 goto l1_%=; \ |
209 | r3 = 0; \ |
210 | l1_%=: /* u32 spill/fill */ \ |
211 | *(u32*)(r10 - 8) = r6; \ |
212 | r8 = *(u32*)(r10 - 8); \ |
213 | /* out-of-bound map value access for r6=32 */ \ |
214 | r1 = 0; \ |
215 | *(u64*)(r10 - 16) = r1; \ |
216 | r2 = r10; \ |
217 | r2 += -16; \ |
218 | r1 = %[map_hash_8b] ll; \ |
219 | call %[bpf_map_lookup_elem]; \ |
220 | if r0 == 0 goto l2_%=; \ |
221 | r0 += r8; \ |
222 | r1 = *(u32*)(r0 + 0); \ |
223 | l2_%=: r0 = 0; \ |
224 | exit; \ |
225 | " : |
226 | : __imm(bpf_get_prandom_u32), |
227 | __imm(bpf_map_lookup_elem), |
228 | __imm_addr(map_hash_8b) |
229 | : __clobber_all); |
230 | } |
231 | |
232 | SEC("tracepoint" ) |
233 | __description("precision tracking for u32 spills, u64 fill" ) |
234 | __failure __msg("div by zero" ) |
235 | __naked void for_u32_spills_u64_fill(void) |
236 | { |
237 | asm volatile (" \ |
238 | call %[bpf_get_prandom_u32]; \ |
239 | r6 = r0; \ |
240 | w7 = 0xffffffff; \ |
241 | /* Additional insns to introduce a pruning point. */\ |
242 | r3 = 1; \ |
243 | r3 = 1; \ |
244 | r3 = 1; \ |
245 | r3 = 1; \ |
246 | call %[bpf_get_prandom_u32]; \ |
247 | if r0 == 0 goto l0_%=; \ |
248 | r3 = 1; \ |
249 | l0_%=: w3 /= 0; \ |
250 | /* u32 spills, u64 fill */ \ |
251 | *(u32*)(r10 - 4) = r6; \ |
252 | *(u32*)(r10 - 8) = r7; \ |
253 | r8 = *(u64*)(r10 - 8); \ |
254 | /* if r8 != X goto pc+1 r8 known in fallthrough branch */\ |
255 | if r8 != 0xffffffff goto l1_%=; \ |
256 | r3 = 1; \ |
257 | l1_%=: /* if r8 == X goto pc+1 condition always true on first\ |
258 | * traversal, so starts backtracking to mark r8 as requiring\ |
259 | * precision. r7 marked as needing precision. r6 not marked\ |
260 | * since it's not tracked. \ |
261 | */ \ |
262 | if r8 == 0xffffffff goto l2_%=; \ |
263 | /* fails if r8 correctly marked unknown after fill. */\ |
264 | w3 /= 0; \ |
265 | l2_%=: r0 = 0; \ |
266 | exit; \ |
267 | " : |
268 | : __imm(bpf_get_prandom_u32) |
269 | : __clobber_all); |
270 | } |
271 | |
272 | SEC("socket" ) |
273 | __description("allocated_stack" ) |
274 | __success __msg("processed 15 insns" ) |
275 | __success_unpriv __msg_unpriv("" ) __log_level(1) __retval(0) |
276 | __naked void allocated_stack(void) |
277 | { |
278 | asm volatile (" \ |
279 | r6 = r1; \ |
280 | call %[bpf_get_prandom_u32]; \ |
281 | r7 = r0; \ |
282 | if r0 == 0 goto l0_%=; \ |
283 | r0 = 0; \ |
284 | *(u64*)(r10 - 8) = r6; \ |
285 | r6 = *(u64*)(r10 - 8); \ |
286 | *(u8*)(r10 - 9) = r7; \ |
287 | r7 = *(u8*)(r10 - 9); \ |
288 | l0_%=: if r0 != 0 goto l1_%=; \ |
289 | l1_%=: if r0 != 0 goto l2_%=; \ |
290 | l2_%=: if r0 != 0 goto l3_%=; \ |
291 | l3_%=: if r0 != 0 goto l4_%=; \ |
292 | l4_%=: exit; \ |
293 | " : |
294 | : __imm(bpf_get_prandom_u32) |
295 | : __clobber_all); |
296 | } |
297 | |
298 | /* The test performs a conditional 64-bit write to a stack location |
299 | * fp[-8], this is followed by an unconditional 8-bit write to fp[-8], |
300 | * then data is read from fp[-8]. This sequence is unsafe. |
301 | * |
302 | * The test would be mistakenly marked as safe w/o dst register parent |
303 | * preservation in verifier.c:copy_register_state() function. |
304 | * |
305 | * Note the usage of BPF_F_TEST_STATE_FREQ to force creation of the |
306 | * checkpoint state after conditional 64-bit assignment. |
307 | */ |
308 | |
309 | SEC("socket" ) |
310 | __description("write tracking and register parent chain bug" ) |
311 | /* in privileged mode reads from uninitialized stack locations are permitted */ |
312 | __success __failure_unpriv |
313 | __msg_unpriv("invalid read from stack off -8+1 size 8" ) |
314 | __retval(0) __flag(BPF_F_TEST_STATE_FREQ) |
315 | __naked void and_register_parent_chain_bug(void) |
316 | { |
317 | asm volatile (" \ |
318 | /* r6 = ktime_get_ns() */ \ |
319 | call %[bpf_ktime_get_ns]; \ |
320 | r6 = r0; \ |
321 | /* r0 = ktime_get_ns() */ \ |
322 | call %[bpf_ktime_get_ns]; \ |
323 | /* if r0 > r6 goto +1 */ \ |
324 | if r0 > r6 goto l0_%=; \ |
325 | /* *(u64 *)(r10 - 8) = 0xdeadbeef */ \ |
326 | r0 = 0xdeadbeef; \ |
327 | *(u64*)(r10 - 8) = r0; \ |
328 | l0_%=: r1 = 42; \ |
329 | *(u8*)(r10 - 8) = r1; \ |
330 | r2 = *(u64*)(r10 - 8); \ |
331 | /* exit(0) */ \ |
332 | r0 = 0; \ |
333 | exit; \ |
334 | " : |
335 | : __imm(bpf_ktime_get_ns) |
336 | : __clobber_all); |
337 | } |
338 | |
339 | char _license[] SEC("license" ) = "GPL" ; |
340 | |