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