1// SPDX-License-Identifier: GPL-2.0
2/* Converted from tools/testing/selftests/bpf/verifier/value_or_null.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
10struct test_val {
11 unsigned int index;
12 int foo[MAX_ENTRIES];
13};
14
15struct {
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
22struct {
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
29SEC("tc")
30__description("multiple registers share map_lookup_elem result")
31__success __retval(0)
32__naked void share_map_lookup_elem_result(void)
33{
34 asm volatile (" \
35 r1 = 10; \
36 *(u64*)(r10 - 8) = r1; \
37 r2 = r10; \
38 r2 += -8; \
39 r1 = %[map_hash_8b] ll; \
40 call %[bpf_map_lookup_elem]; \
41 r4 = r0; \
42 if r0 == 0 goto l0_%=; \
43 r1 = 0; \
44 *(u64*)(r4 + 0) = r1; \
45l0_%=: exit; \
46" :
47 : __imm(bpf_map_lookup_elem),
48 __imm_addr(map_hash_8b)
49 : __clobber_all);
50}
51
52SEC("tc")
53__description("alu ops on ptr_to_map_value_or_null, 1")
54__failure __msg("R4 pointer arithmetic on map_value_or_null")
55__naked void map_value_or_null_1(void)
56{
57 asm volatile (" \
58 r1 = 10; \
59 *(u64*)(r10 - 8) = r1; \
60 r2 = r10; \
61 r2 += -8; \
62 r1 = %[map_hash_8b] ll; \
63 call %[bpf_map_lookup_elem]; \
64 r4 = r0; \
65 r4 += -2; \
66 r4 += 2; \
67 if r0 == 0 goto l0_%=; \
68 r1 = 0; \
69 *(u64*)(r4 + 0) = r1; \
70l0_%=: exit; \
71" :
72 : __imm(bpf_map_lookup_elem),
73 __imm_addr(map_hash_8b)
74 : __clobber_all);
75}
76
77SEC("tc")
78__description("alu ops on ptr_to_map_value_or_null, 2")
79__failure __msg("R4 pointer arithmetic on map_value_or_null")
80__naked void map_value_or_null_2(void)
81{
82 asm volatile (" \
83 r1 = 10; \
84 *(u64*)(r10 - 8) = r1; \
85 r2 = r10; \
86 r2 += -8; \
87 r1 = %[map_hash_8b] ll; \
88 call %[bpf_map_lookup_elem]; \
89 r4 = r0; \
90 r4 &= -1; \
91 if r0 == 0 goto l0_%=; \
92 r1 = 0; \
93 *(u64*)(r4 + 0) = r1; \
94l0_%=: exit; \
95" :
96 : __imm(bpf_map_lookup_elem),
97 __imm_addr(map_hash_8b)
98 : __clobber_all);
99}
100
101SEC("tc")
102__description("alu ops on ptr_to_map_value_or_null, 3")
103__failure __msg("R4 pointer arithmetic on map_value_or_null")
104__naked void map_value_or_null_3(void)
105{
106 asm volatile (" \
107 r1 = 10; \
108 *(u64*)(r10 - 8) = r1; \
109 r2 = r10; \
110 r2 += -8; \
111 r1 = %[map_hash_8b] ll; \
112 call %[bpf_map_lookup_elem]; \
113 r4 = r0; \
114 r4 <<= 1; \
115 if r0 == 0 goto l0_%=; \
116 r1 = 0; \
117 *(u64*)(r4 + 0) = r1; \
118l0_%=: exit; \
119" :
120 : __imm(bpf_map_lookup_elem),
121 __imm_addr(map_hash_8b)
122 : __clobber_all);
123}
124
125SEC("tc")
126__description("invalid memory access with multiple map_lookup_elem calls")
127__failure __msg("R4 !read_ok")
128__naked void multiple_map_lookup_elem_calls(void)
129{
130 asm volatile (" \
131 r1 = 10; \
132 *(u64*)(r10 - 8) = r1; \
133 r2 = r10; \
134 r2 += -8; \
135 r1 = %[map_hash_8b] ll; \
136 r8 = r1; \
137 r7 = r2; \
138 call %[bpf_map_lookup_elem]; \
139 r4 = r0; \
140 r1 = r8; \
141 r2 = r7; \
142 call %[bpf_map_lookup_elem]; \
143 if r0 == 0 goto l0_%=; \
144 r1 = 0; \
145 *(u64*)(r4 + 0) = r1; \
146l0_%=: exit; \
147" :
148 : __imm(bpf_map_lookup_elem),
149 __imm_addr(map_hash_8b)
150 : __clobber_all);
151}
152
153SEC("tc")
154__description("valid indirect map_lookup_elem access with 2nd lookup in branch")
155__success __retval(0)
156__naked void with_2nd_lookup_in_branch(void)
157{
158 asm volatile (" \
159 r1 = 10; \
160 *(u64*)(r10 - 8) = r1; \
161 r2 = r10; \
162 r2 += -8; \
163 r1 = %[map_hash_8b] ll; \
164 r8 = r1; \
165 r7 = r2; \
166 call %[bpf_map_lookup_elem]; \
167 r2 = 10; \
168 if r2 != 0 goto l0_%=; \
169 r1 = r8; \
170 r2 = r7; \
171 call %[bpf_map_lookup_elem]; \
172l0_%=: r4 = r0; \
173 if r0 == 0 goto l1_%=; \
174 r1 = 0; \
175 *(u64*)(r4 + 0) = r1; \
176l1_%=: exit; \
177" :
178 : __imm(bpf_map_lookup_elem),
179 __imm_addr(map_hash_8b)
180 : __clobber_all);
181}
182
183SEC("socket")
184__description("invalid map access from else condition")
185__failure __msg("R0 unbounded memory access")
186__failure_unpriv __msg_unpriv("R0 leaks addr")
187__flag(BPF_F_ANY_ALIGNMENT)
188__naked void map_access_from_else_condition(void)
189{
190 asm volatile (" \
191 r1 = 0; \
192 *(u64*)(r10 - 8) = r1; \
193 r2 = r10; \
194 r2 += -8; \
195 r1 = %[map_hash_48b] ll; \
196 call %[bpf_map_lookup_elem]; \
197 if r0 == 0 goto l0_%=; \
198 r1 = *(u32*)(r0 + 0); \
199 if r1 >= %[__imm_0] goto l1_%=; \
200 r1 += 1; \
201l1_%=: r1 <<= 2; \
202 r0 += r1; \
203 r1 = %[test_val_foo]; \
204 *(u64*)(r0 + 0) = r1; \
205l0_%=: exit; \
206" :
207 : __imm(bpf_map_lookup_elem),
208 __imm_addr(map_hash_48b),
209 __imm_const(__imm_0, MAX_ENTRIES-1),
210 __imm_const(test_val_foo, offsetof(struct test_val, foo))
211 : __clobber_all);
212}
213
214SEC("tc")
215__description("map lookup and null branch prediction")
216__success __retval(0)
217__naked void lookup_and_null_branch_prediction(void)
218{
219 asm volatile (" \
220 r1 = 10; \
221 *(u64*)(r10 - 8) = r1; \
222 r2 = r10; \
223 r2 += -8; \
224 r1 = %[map_hash_8b] ll; \
225 call %[bpf_map_lookup_elem]; \
226 r6 = r0; \
227 if r6 == 0 goto l0_%=; \
228 if r6 != 0 goto l0_%=; \
229 r10 += 10; \
230l0_%=: exit; \
231" :
232 : __imm(bpf_map_lookup_elem),
233 __imm_addr(map_hash_8b)
234 : __clobber_all);
235}
236
237SEC("cgroup/skb")
238__description("MAP_VALUE_OR_NULL check_ids() in regsafe()")
239__failure __msg("R8 invalid mem access 'map_value_or_null'")
240__failure_unpriv __msg_unpriv("")
241__flag(BPF_F_TEST_STATE_FREQ)
242__naked void null_check_ids_in_regsafe(void)
243{
244 asm volatile (" \
245 r1 = 0; \
246 *(u64*)(r10 - 8) = r1; \
247 /* r9 = map_lookup_elem(...) */ \
248 r2 = r10; \
249 r2 += -8; \
250 r1 = %[map_hash_8b] ll; \
251 call %[bpf_map_lookup_elem]; \
252 r9 = r0; \
253 /* r8 = map_lookup_elem(...) */ \
254 r2 = r10; \
255 r2 += -8; \
256 r1 = %[map_hash_8b] ll; \
257 call %[bpf_map_lookup_elem]; \
258 r8 = r0; \
259 /* r7 = ktime_get_ns() */ \
260 call %[bpf_ktime_get_ns]; \
261 r7 = r0; \
262 /* r6 = ktime_get_ns() */ \
263 call %[bpf_ktime_get_ns]; \
264 r6 = r0; \
265 /* if r6 > r7 goto +1 ; no new information about the state is derived from\
266 * ; this check, thus produced verifier states differ\
267 * ; only in 'insn_idx' \
268 * r9 = r8 ; optionally share ID between r9 and r8\
269 */ \
270 if r6 > r7 goto l0_%=; \
271 r9 = r8; \
272l0_%=: /* if r9 == 0 goto <exit> */ \
273 if r9 == 0 goto l1_%=; \
274 /* read map value via r8, this is not always \
275 * safe because r8 might be not equal to r9. \
276 */ \
277 r0 = *(u64*)(r8 + 0); \
278l1_%=: /* exit 0 */ \
279 r0 = 0; \
280 exit; \
281" :
282 : __imm(bpf_ktime_get_ns),
283 __imm(bpf_map_lookup_elem),
284 __imm_addr(map_hash_8b)
285 : __clobber_all);
286}
287
288char _license[] SEC("license") = "GPL";
289

source code of linux/tools/testing/selftests/bpf/progs/verifier_value_or_null.c