Skip to content

Commit 034d9ad

Browse files
eddyz87Alexei Starovoitov
authored andcommitted
selftests/bpf: verifier/search_pruning converted to inline assembly
Test verifier/search_pruning automatically converted to use inline assembly. Signed-off-by: Eduard Zingerman <eddyz87@gmail.com> Link: https://lore.kernel.org/r/20230421174234.2391278-19-eddyz87@gmail.com Signed-off-by: Alexei Starovoitov <ast@kernel.org>
1 parent 6522284 commit 034d9ad

3 files changed

Lines changed: 341 additions & 266 deletions

File tree

tools/testing/selftests/bpf/prog_tests/verifier.c

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -49,6 +49,7 @@
4949
#include "verifier_regalloc.skel.h"
5050
#include "verifier_ringbuf.skel.h"
5151
#include "verifier_runtime_jit.skel.h"
52+
#include "verifier_search_pruning.skel.h"
5253
#include "verifier_spill_fill.skel.h"
5354
#include "verifier_stack_ptr.skel.h"
5455
#include "verifier_uninit.skel.h"
@@ -139,6 +140,7 @@ void test_verifier_ref_tracking(void) { RUN(verifier_ref_tracking); }
139140
void test_verifier_regalloc(void) { RUN(verifier_regalloc); }
140141
void test_verifier_ringbuf(void) { RUN(verifier_ringbuf); }
141142
void test_verifier_runtime_jit(void) { RUN(verifier_runtime_jit); }
143+
void test_verifier_search_pruning(void) { RUN(verifier_search_pruning); }
142144
void test_verifier_spill_fill(void) { RUN(verifier_spill_fill); }
143145
void test_verifier_stack_ptr(void) { RUN(verifier_stack_ptr); }
144146
void test_verifier_uninit(void) { RUN(verifier_uninit); }
Lines changed: 339 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,339 @@
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";

0 commit comments

Comments
 (0)