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