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
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("tc")
30 __description("multiple registers share map_lookup_elem result")
31 __success __retval(0)
share_map_lookup_elem_result(void)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; \
45 l0_%=: exit; \
46 " :
47 : __imm(bpf_map_lookup_elem),
48 __imm_addr(map_hash_8b)
49 : __clobber_all);
50 }
51
52 SEC("tc")
53 __description("alu ops on ptr_to_map_value_or_null, 1")
54 __failure __msg("R4 pointer arithmetic on map_value_or_null")
map_value_or_null_1(void)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; \
70 l0_%=: exit; \
71 " :
72 : __imm(bpf_map_lookup_elem),
73 __imm_addr(map_hash_8b)
74 : __clobber_all);
75 }
76
77 SEC("tc")
78 __description("alu ops on ptr_to_map_value_or_null, 2")
79 __failure __msg("R4 pointer arithmetic on map_value_or_null")
map_value_or_null_2(void)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; \
94 l0_%=: exit; \
95 " :
96 : __imm(bpf_map_lookup_elem),
97 __imm_addr(map_hash_8b)
98 : __clobber_all);
99 }
100
101 SEC("tc")
102 __description("alu ops on ptr_to_map_value_or_null, 3")
103 __failure __msg("R4 pointer arithmetic on map_value_or_null")
map_value_or_null_3(void)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; \
118 l0_%=: exit; \
119 " :
120 : __imm(bpf_map_lookup_elem),
121 __imm_addr(map_hash_8b)
122 : __clobber_all);
123 }
124
125 SEC("tc")
126 __description("invalid memory access with multiple map_lookup_elem calls")
127 __failure __msg("R4 !read_ok")
multiple_map_lookup_elem_calls(void)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; \
146 l0_%=: exit; \
147 " :
148 : __imm(bpf_map_lookup_elem),
149 __imm_addr(map_hash_8b)
150 : __clobber_all);
151 }
152
153 SEC("tc")
154 __description("valid indirect map_lookup_elem access with 2nd lookup in branch")
155 __success __retval(0)
with_2nd_lookup_in_branch(void)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]; \
172 l0_%=: r4 = r0; \
173 if r0 == 0 goto l1_%=; \
174 r1 = 0; \
175 *(u64*)(r4 + 0) = r1; \
176 l1_%=: exit; \
177 " :
178 : __imm(bpf_map_lookup_elem),
179 __imm_addr(map_hash_8b)
180 : __clobber_all);
181 }
182
183 SEC("socket")
184 __description("invalid map access from else condition")
185 __failure __msg("R0 unbounded memory access")
186 __failure_unpriv __msg_unpriv("R0 leaks addr")
__flag(BPF_F_ANY_ALIGNMENT)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; \
201 l1_%=: r1 <<= 2; \
202 r0 += r1; \
203 r1 = %[test_val_foo]; \
204 *(u64*)(r0 + 0) = r1; \
205 l0_%=: 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
214 SEC("tc")
215 __description("map lookup and null branch prediction")
216 __success __retval(0)
lookup_and_null_branch_prediction(void)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; \
230 l0_%=: exit; \
231 " :
232 : __imm(bpf_map_lookup_elem),
233 __imm_addr(map_hash_8b)
234 : __clobber_all);
235 }
236
237 SEC("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("")
__flag(BPF_F_TEST_STATE_FREQ)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; \
272 l0_%=: /* 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); \
278 l1_%=: /* 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
288 char _license[] SEC("license") = "GPL";
289