xref: /linux/tools/testing/selftests/bpf/progs/verifier_map_in_map.c (revision a3847994b4d20c0701ccc54fe110920ea78e73dc)
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_value_or_null expected=map_ptr")
158 __failure_unpriv
159 __naked void on_the_inner_map_pointer(void)
160 {
161 	asm volatile ("					\
162 	r1 = 0;						\
163 	*(u32*)(r10 - 4) = r1;				\
164 	r2 = r10;					\
165 	r2 += -4;					\
166 	r1 = %[map_in_map] ll;				\
167 	call %[bpf_map_lookup_elem];			\
168 	r1 = 0;						\
169 	*(u32*)(r10 - 4) = r1;				\
170 	r2 = r10;					\
171 	r2 += -4;					\
172 	r1 = r0;					\
173 	call %[bpf_map_lookup_elem];			\
174 	r0 = 0;						\
175 	exit;						\
176 "	:
177 	: __imm(bpf_map_lookup_elem),
178 	  __imm_addr(map_in_map)
179 	: __clobber_all);
180 }
181 
182 SEC("socket")
183 __description("map_ptr is never null")
184 __success
185 __naked void map_ptr_is_never_null(void)
186 {
187 	asm volatile ("					\
188 	r0 = 0;						\
189 	r1 = %[map_in_map] ll;				\
190 	if r1 != 0 goto l0_%=;				\
191 	r10 = 42;					\
192 l0_%=:	exit;						\
193 "	:
194 	: __imm(bpf_map_lookup_elem),
195 	  __imm_addr(map_in_map)
196 	: __clobber_all);
197 }
198 
199 SEC("socket")
200 __description("map_ptr is never null inner")
201 __success
202 __naked void map_ptr_is_never_null_inner(void)
203 {
204 	asm volatile ("					\
205 	r1 = 0;						\
206 	*(u32*)(r10 - 4) = r1;				\
207 	r2 = r10;					\
208 	r2 += -4;					\
209 	r1 = %[map_in_map] ll;				\
210 	call %[bpf_map_lookup_elem];			\
211 	if r0 == 0 goto l0_%=;				\
212 	if r0 != 0 goto l0_%=;				\
213 	r10 = 42;					\
214 l0_%=:  exit;						\
215 "	:
216 	: __imm(bpf_map_lookup_elem),
217 	  __imm_addr(map_in_map)
218 	: __clobber_all);
219 }
220 
221 SEC("socket")
222 __description("map_ptr is never null inner spill fill")
223 __success
224 __naked void map_ptr_is_never_null_inner_spill_fill(void)
225 {
226 	asm volatile ("					\
227 	r1 = 0;						\
228 	*(u32*)(r10 - 4) = r1;				\
229 	r2 = r10;					\
230 	r2 += -4;					\
231 	r1 = %[map_in_map] ll;				\
232 	call %[bpf_map_lookup_elem];			\
233 	if r0 != 0 goto l0_%=;				\
234 	exit;						\
235 l0_%=:	*(u64 *)(r10 -16) = r0;				\
236 	r1 = *(u64 *)(r10 -16);				\
237 	if r1 == 0 goto l1_%=;				\
238 	exit;						\
239 l1_%=:	r10 = 42;					\
240 	exit;						\
241 "	:
242 	: __imm(bpf_map_lookup_elem),
243 	  __imm_addr(map_in_map)
244 	: __clobber_all);
245 }
246 
247 struct {
248 	__uint(type, BPF_MAP_TYPE_ARRAY_OF_MAPS);
249 	__uint(max_entries, 1);
250 	__type(key, int);
251 	__type(value, int);
252 	__array(values, struct {
253 		__uint(type, BPF_MAP_TYPE_RINGBUF);
254 		__uint(max_entries, 64 * 1024);
255 	});
256 } rb_in_map SEC(".maps");
257 
258 struct rb_ctx {
259 	void *rb;
260 	struct bpf_dynptr dptr;
261 };
262 
263 static __always_inline struct rb_ctx __rb_event_reserve(__u32 sz)
264 {
265 	struct rb_ctx rb_ctx = {};
266 	void *rb;
267 	__u32 cpu = bpf_get_smp_processor_id();
268 	__u32 rb_slot = cpu & 1;
269 
270 	rb = bpf_map_lookup_elem(&rb_in_map, &rb_slot);
271 	if (!rb)
272 		return rb_ctx;
273 
274 	rb_ctx.rb = rb;
275 	bpf_ringbuf_reserve_dynptr(rb, sz, 0, &rb_ctx.dptr);
276 
277 	return rb_ctx;
278 }
279 
280 static __noinline void __rb_event_submit(struct rb_ctx *ctx)
281 {
282 	if (!ctx->rb)
283 		return;
284 
285 	/* If the verifier (incorrectly) concludes that ctx->rb can be
286 	 * NULL at this point, we'll get "BPF_EXIT instruction in main
287 	 * prog would lead to reference leak" error
288 	 */
289 	bpf_ringbuf_submit_dynptr(&ctx->dptr, 0);
290 }
291 
292 SEC("socket")
293 int map_ptr_is_never_null_rb(void *ctx)
294 {
295 	struct rb_ctx event_ctx = __rb_event_reserve(256);
296 	__rb_event_submit(&event_ctx);
297 	return 0;
298 }
299 
300 char _license[] SEC("license") = "GPL";
301