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