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)
map_in_map_access(void)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'")
map_in_map_dynamic_inner_array_lookup_is_nullable(void)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")
__flag(BPF_F_TEST_STATE_FREQ)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
invalid_inner_map_pointer(void)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
on_the_inner_map_pointer(void)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
map_ptr_is_never_null(void)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
map_ptr_is_never_null_inner(void)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
map_ptr_is_never_null_inner_spill_fill(void)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
__rb_event_reserve(__u32 sz)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
__rb_event_submit(struct rb_ctx * ctx)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")
map_ptr_is_never_null_rb(void * ctx)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