xref: /linux/tools/testing/selftests/bpf/progs/verifier_map_in_map.c (revision 9d19ca5d0e8b4a3f4b2eaa14e86a25f1c93ff35b)
1 // SPDX-License-Identifier: GPL-2.0
2 /* Converted from tools/testing/selftests/bpf/verifier/map_in_map.c */
3 
4 #include <linux/bpf.h>
5 #include <bpf/bpf_helpers.h>
6 #include "bpf_misc.h"
7 
8 struct {
9 	__uint(type, BPF_MAP_TYPE_ARRAY_OF_MAPS);
10 	__uint(max_entries, 1);
11 	__type(key, int);
12 	__type(value, int);
13 	__array(values, struct {
14 		__uint(type, BPF_MAP_TYPE_ARRAY);
15 		__uint(max_entries, 1);
16 		__type(key, int);
17 		__type(value, int);
18 	});
19 } map_in_map SEC(".maps");
20 
21 struct {
22 	__uint(type, BPF_MAP_TYPE_ARRAY_OF_MAPS);
23 	__uint(max_entries, 1);
24 	__type(key, int);
25 	__type(value, int);
26 	__array(values, struct {
27 		__uint(type, BPF_MAP_TYPE_ARRAY);
28 		__uint(map_flags, BPF_F_INNER_MAP);
29 		__uint(max_entries, 8);
30 		__type(key, int);
31 		__type(value, long);
32 	});
33 } map_in_map_dyn SEC(".maps");
34 
35 SEC("socket")
36 __description("map in map access")
37 __success __success_unpriv __retval(0)
38 __naked void map_in_map_access(void)
39 {
40 	asm volatile ("					\
41 	r1 = 0;						\
42 	*(u32*)(r10 - 4) = r1;				\
43 	r2 = r10;					\
44 	r2 += -4;					\
45 	r1 = %[map_in_map] ll;				\
46 	call %[bpf_map_lookup_elem];			\
47 	if r0 == 0 goto l0_%=;				\
48 	r1 = 0;						\
49 	*(u32*)(r10 - 4) = r1;				\
50 	r2 = r10;					\
51 	r2 += -4;					\
52 	r1 = r0;					\
53 	call %[bpf_map_lookup_elem];			\
54 l0_%=:	r0 = 0;						\
55 	exit;						\
56 "	:
57 	: __imm(bpf_map_lookup_elem),
58 	  __imm_addr(map_in_map)
59 	: __clobber_all);
60 }
61 
62 SEC("socket")
63 __description("map in map dynamic inner array lookup is nullable")
64 __failure __msg("invalid mem access 'map_value_or_null'")
65 __naked void map_in_map_dynamic_inner_array_lookup_is_nullable(void)
66 {
67 	asm volatile ("					\
68 	r1 = 0;						\
69 	*(u32*)(r10 - 4) = r1;				\
70 	r2 = r10;					\
71 	r2 += -4;					\
72 	r1 = %[map_in_map_dyn] ll;			\
73 	call %[bpf_map_lookup_elem];			\
74 	if r0 == 0 goto l0_%=;				\
75 	*(u32*)(r10 - 8) = 4;				\
76 	r2 = r10;					\
77 	r2 += -8;					\
78 	r1 = r0;					\
79 	call %[bpf_map_lookup_elem];			\
80 	r0 = *(u64 *)(r0 + 0);				\
81 l0_%=:	exit;						\
82 "	:
83 	: __imm(bpf_map_lookup_elem),
84 	  __imm_addr(map_in_map_dyn)
85 	: __clobber_all);
86 }
87 
88 SEC("xdp")
89 __description("map in map state pruning")
90 __success __msg("processed 15 insns")
91 __log_level(2) __retval(0) __flag(BPF_F_TEST_STATE_FREQ)
92 __naked void map_in_map_state_pruning(void)
93 {
94 	asm volatile ("					\
95 	r1 = 0;						\
96 	*(u32*)(r10 - 4) = r1;				\
97 	r6 = r10;					\
98 	r6 += -4;					\
99 	r2 = r6;					\
100 	r1 = %[map_in_map] ll;				\
101 	call %[bpf_map_lookup_elem];			\
102 	if r0 != 0 goto l0_%=;				\
103 	exit;						\
104 l0_%=:	r2 = r6;					\
105 	r1 = r0;					\
106 	call %[bpf_map_lookup_elem];			\
107 	if r0 != 0 goto l1_%=;				\
108 	r2 = r6;					\
109 	r1 = %[map_in_map] ll;				\
110 	call %[bpf_map_lookup_elem];			\
111 	if r0 != 0 goto l2_%=;				\
112 	exit;						\
113 l2_%=:	r2 = r6;					\
114 	r1 = r0;					\
115 	call %[bpf_map_lookup_elem];			\
116 	if r0 != 0 goto l1_%=;				\
117 	exit;						\
118 l1_%=:	r0 = *(u32*)(r0 + 0);				\
119 	exit;						\
120 "	:
121 	: __imm(bpf_map_lookup_elem),
122 	  __imm_addr(map_in_map)
123 	: __clobber_all);
124 }
125 
126 SEC("socket")
127 __description("invalid inner map pointer")
128 __failure __msg("R1 pointer arithmetic on map_ptr prohibited")
129 __failure_unpriv
130 __naked void invalid_inner_map_pointer(void)
131 {
132 	asm volatile ("					\
133 	r1 = 0;						\
134 	*(u32*)(r10 - 4) = r1;				\
135 	r2 = r10;					\
136 	r2 += -4;					\
137 	r1 = %[map_in_map] ll;				\
138 	call %[bpf_map_lookup_elem];			\
139 	if r0 == 0 goto l0_%=;				\
140 	r1 = 0;						\
141 	*(u32*)(r10 - 4) = r1;				\
142 	r2 = r10;					\
143 	r2 += -4;					\
144 	r1 = r0;					\
145 	r1 += 8;					\
146 	call %[bpf_map_lookup_elem];			\
147 l0_%=:	r0 = 0;						\
148 	exit;						\
149 "	:
150 	: __imm(bpf_map_lookup_elem),
151 	  __imm_addr(map_in_map)
152 	: __clobber_all);
153 }
154 
155 SEC("socket")
156 __description("forgot null checking on the inner map pointer")
157 __failure __msg("R1 type=map_ptr_or_null expected=map_ptr")
158 __msg("map_ptr_or_null, but this argument accepts map_ptr")
159 __failure_unpriv
160 __naked void on_the_inner_map_pointer(void)
161 {
162 	asm volatile ("					\
163 	r1 = 0;						\
164 	*(u32*)(r10 - 4) = r1;				\
165 	r2 = r10;					\
166 	r2 += -4;					\
167 	r1 = %[map_in_map] ll;				\
168 	call %[bpf_map_lookup_elem];			\
169 	r1 = 0;						\
170 	*(u32*)(r10 - 4) = r1;				\
171 	r2 = r10;					\
172 	r2 += -4;					\
173 	r1 = r0;					\
174 	call %[bpf_map_lookup_elem];			\
175 	r0 = 0;						\
176 	exit;						\
177 "	:
178 	: __imm(bpf_map_lookup_elem),
179 	  __imm_addr(map_in_map)
180 	: __clobber_all);
181 }
182 
183 SEC("socket")
184 __description("map_ptr is never null")
185 __success
186 __naked void map_ptr_is_never_null(void)
187 {
188 	asm volatile ("					\
189 	r0 = 0;						\
190 	r1 = %[map_in_map] ll;				\
191 	if r1 != 0 goto l0_%=;				\
192 	r10 = 42;					\
193 l0_%=:	exit;						\
194 "	:
195 	: __imm(bpf_map_lookup_elem),
196 	  __imm_addr(map_in_map)
197 	: __clobber_all);
198 }
199 
200 SEC("socket")
201 __description("map_ptr is never null inner")
202 __success
203 __naked void map_ptr_is_never_null_inner(void)
204 {
205 	asm volatile ("					\
206 	r1 = 0;						\
207 	*(u32*)(r10 - 4) = r1;				\
208 	r2 = r10;					\
209 	r2 += -4;					\
210 	r1 = %[map_in_map] ll;				\
211 	call %[bpf_map_lookup_elem];			\
212 	if r0 == 0 goto l0_%=;				\
213 	if r0 != 0 goto l0_%=;				\
214 	r10 = 42;					\
215 l0_%=:  exit;						\
216 "	:
217 	: __imm(bpf_map_lookup_elem),
218 	  __imm_addr(map_in_map)
219 	: __clobber_all);
220 }
221 
222 SEC("socket")
223 __description("map_ptr is never null inner spill fill")
224 __success
225 __naked void map_ptr_is_never_null_inner_spill_fill(void)
226 {
227 	asm volatile ("					\
228 	r1 = 0;						\
229 	*(u32*)(r10 - 4) = r1;				\
230 	r2 = r10;					\
231 	r2 += -4;					\
232 	r1 = %[map_in_map] ll;				\
233 	call %[bpf_map_lookup_elem];			\
234 	if r0 != 0 goto l0_%=;				\
235 	exit;						\
236 l0_%=:	*(u64 *)(r10 -16) = r0;				\
237 	r1 = *(u64 *)(r10 -16);				\
238 	if r1 == 0 goto l1_%=;				\
239 	exit;						\
240 l1_%=:	r10 = 42;					\
241 	exit;						\
242 "	:
243 	: __imm(bpf_map_lookup_elem),
244 	  __imm_addr(map_in_map)
245 	: __clobber_all);
246 }
247 
248 struct {
249 	__uint(type, BPF_MAP_TYPE_ARRAY_OF_MAPS);
250 	__uint(max_entries, 1);
251 	__type(key, int);
252 	__type(value, int);
253 	__array(values, struct {
254 		__uint(type, BPF_MAP_TYPE_RINGBUF);
255 		__uint(max_entries, 64 * 1024);
256 	});
257 } rb_in_map SEC(".maps");
258 
259 struct rb_ctx {
260 	void *rb;
261 	struct bpf_dynptr dptr;
262 };
263 
264 static __always_inline struct rb_ctx __rb_event_reserve(__u32 sz)
265 {
266 	struct rb_ctx rb_ctx = {};
267 	void *rb;
268 	__u32 cpu = bpf_get_smp_processor_id();
269 	__u32 rb_slot = cpu & 1;
270 
271 	rb = bpf_map_lookup_elem(&rb_in_map, &rb_slot);
272 	if (!rb)
273 		return rb_ctx;
274 
275 	rb_ctx.rb = rb;
276 	bpf_ringbuf_reserve_dynptr(rb, sz, 0, &rb_ctx.dptr);
277 
278 	return rb_ctx;
279 }
280 
281 static __noinline void __rb_event_submit(struct rb_ctx *ctx)
282 {
283 	if (!ctx->rb)
284 		return;
285 
286 	/* If the verifier (incorrectly) concludes that ctx->rb can be
287 	 * NULL at this point, we'll get "BPF_EXIT instruction in main
288 	 * prog would lead to reference leak" error
289 	 */
290 	bpf_ringbuf_submit_dynptr(&ctx->dptr, 0);
291 }
292 
293 SEC("socket")
294 int map_ptr_is_never_null_rb(void *ctx)
295 {
296 	struct rb_ctx event_ctx = __rb_event_reserve(256);
297 	__rb_event_submit(&event_ctx);
298 	return 0;
299 }
300 
301 char _license[] SEC("license") = "GPL";
302