1 // SPDX-License-Identifier: GPL-2.0 2 /* Converted from tools/testing/selftests/bpf/verifier/value_or_null.c */ 3 4 #include <linux/bpf.h> 5 #include <bpf/bpf_helpers.h> 6 #include "bpf_misc.h" 7 8 #define MAX_ENTRIES 11 9 10 struct test_val { 11 unsigned int index; 12 int foo[MAX_ENTRIES]; 13 }; 14 15 struct { 16 __uint(type, BPF_MAP_TYPE_HASH); 17 __uint(max_entries, 1); 18 __type(key, long long); 19 __type(value, struct test_val); 20 } map_hash_48b SEC(".maps"); 21 22 struct { 23 __uint(type, BPF_MAP_TYPE_HASH); 24 __uint(max_entries, 1); 25 __type(key, long long); 26 __type(value, long long); 27 } map_hash_8b SEC(".maps"); 28 29 SEC("tc") 30 __description("multiple registers share map_lookup_elem result") 31 __success __retval(0) 32 __naked void share_map_lookup_elem_result(void) 33 { 34 asm volatile (" \ 35 r1 = 10; \ 36 *(u64*)(r10 - 8) = r1; \ 37 r2 = r10; \ 38 r2 += -8; \ 39 r1 = %[map_hash_8b] ll; \ 40 call %[bpf_map_lookup_elem]; \ 41 r4 = r0; \ 42 if r0 == 0 goto l0_%=; \ 43 r1 = 0; \ 44 *(u64*)(r4 + 0) = r1; \ 45 l0_%=: exit; \ 46 " : 47 : __imm(bpf_map_lookup_elem), 48 __imm_addr(map_hash_8b) 49 : __clobber_all); 50 } 51 52 SEC("tc") 53 __description("alu ops on ptr_to_map_value_or_null, 1") 54 __failure __msg("R4 pointer arithmetic on map_value_or_null") 55 __naked void map_value_or_null_1(void) 56 { 57 asm volatile (" \ 58 r1 = 10; \ 59 *(u64*)(r10 - 8) = r1; \ 60 r2 = r10; \ 61 r2 += -8; \ 62 r1 = %[map_hash_8b] ll; \ 63 call %[bpf_map_lookup_elem]; \ 64 r4 = r0; \ 65 r4 += -2; \ 66 r4 += 2; \ 67 if r0 == 0 goto l0_%=; \ 68 r1 = 0; \ 69 *(u64*)(r4 + 0) = r1; \ 70 l0_%=: exit; \ 71 " : 72 : __imm(bpf_map_lookup_elem), 73 __imm_addr(map_hash_8b) 74 : __clobber_all); 75 } 76 77 SEC("tc") 78 __description("alu ops on ptr_to_map_value_or_null, 2") 79 __failure __msg("R4 pointer arithmetic on map_value_or_null") 80 __naked void map_value_or_null_2(void) 81 { 82 asm volatile (" \ 83 r1 = 10; \ 84 *(u64*)(r10 - 8) = r1; \ 85 r2 = r10; \ 86 r2 += -8; \ 87 r1 = %[map_hash_8b] ll; \ 88 call %[bpf_map_lookup_elem]; \ 89 r4 = r0; \ 90 r4 &= -1; \ 91 if r0 == 0 goto l0_%=; \ 92 r1 = 0; \ 93 *(u64*)(r4 + 0) = r1; \ 94 l0_%=: exit; \ 95 " : 96 : __imm(bpf_map_lookup_elem), 97 __imm_addr(map_hash_8b) 98 : __clobber_all); 99 } 100 101 SEC("tc") 102 __description("alu ops on ptr_to_map_value_or_null, 3") 103 __failure __msg("R4 pointer arithmetic on map_value_or_null") 104 __naked void map_value_or_null_3(void) 105 { 106 asm volatile (" \ 107 r1 = 10; \ 108 *(u64*)(r10 - 8) = r1; \ 109 r2 = r10; \ 110 r2 += -8; \ 111 r1 = %[map_hash_8b] ll; \ 112 call %[bpf_map_lookup_elem]; \ 113 r4 = r0; \ 114 r4 <<= 1; \ 115 if r0 == 0 goto l0_%=; \ 116 r1 = 0; \ 117 *(u64*)(r4 + 0) = r1; \ 118 l0_%=: exit; \ 119 " : 120 : __imm(bpf_map_lookup_elem), 121 __imm_addr(map_hash_8b) 122 : __clobber_all); 123 } 124 125 SEC("tc") 126 __description("invalid memory access with multiple map_lookup_elem calls") 127 __failure __msg("R4 !read_ok") 128 __naked void multiple_map_lookup_elem_calls(void) 129 { 130 asm volatile (" \ 131 r1 = 10; \ 132 *(u64*)(r10 - 8) = r1; \ 133 r2 = r10; \ 134 r2 += -8; \ 135 r1 = %[map_hash_8b] ll; \ 136 r8 = r1; \ 137 r7 = r2; \ 138 call %[bpf_map_lookup_elem]; \ 139 r4 = r0; \ 140 r1 = r8; \ 141 r2 = r7; \ 142 call %[bpf_map_lookup_elem]; \ 143 if r0 == 0 goto l0_%=; \ 144 r1 = 0; \ 145 *(u64*)(r4 + 0) = r1; \ 146 l0_%=: exit; \ 147 " : 148 : __imm(bpf_map_lookup_elem), 149 __imm_addr(map_hash_8b) 150 : __clobber_all); 151 } 152 153 SEC("tc") 154 __description("valid indirect map_lookup_elem access with 2nd lookup in branch") 155 __success __retval(0) 156 __naked void with_2nd_lookup_in_branch(void) 157 { 158 asm volatile (" \ 159 r1 = 10; \ 160 *(u64*)(r10 - 8) = r1; \ 161 r2 = r10; \ 162 r2 += -8; \ 163 r1 = %[map_hash_8b] ll; \ 164 r8 = r1; \ 165 r7 = r2; \ 166 call %[bpf_map_lookup_elem]; \ 167 r2 = 10; \ 168 if r2 != 0 goto l0_%=; \ 169 r1 = r8; \ 170 r2 = r7; \ 171 call %[bpf_map_lookup_elem]; \ 172 l0_%=: r4 = r0; \ 173 if r0 == 0 goto l1_%=; \ 174 r1 = 0; \ 175 *(u64*)(r4 + 0) = r1; \ 176 l1_%=: exit; \ 177 " : 178 : __imm(bpf_map_lookup_elem), 179 __imm_addr(map_hash_8b) 180 : __clobber_all); 181 } 182 183 SEC("socket") 184 __description("invalid map access from else condition") 185 __failure __msg("R0 unbounded memory access") 186 __failure_unpriv __msg_unpriv("R0 leaks addr") 187 __flag(BPF_F_ANY_ALIGNMENT) 188 __naked void map_access_from_else_condition(void) 189 { 190 asm volatile (" \ 191 r1 = 0; \ 192 *(u64*)(r10 - 8) = r1; \ 193 r2 = r10; \ 194 r2 += -8; \ 195 r1 = %[map_hash_48b] ll; \ 196 call %[bpf_map_lookup_elem]; \ 197 if r0 == 0 goto l0_%=; \ 198 r1 = *(u32*)(r0 + 0); \ 199 if r1 >= %[__imm_0] goto l1_%=; \ 200 r1 += 1; \ 201 l1_%=: r1 <<= 2; \ 202 r0 += r1; \ 203 r1 = %[test_val_foo]; \ 204 *(u64*)(r0 + 0) = r1; \ 205 l0_%=: exit; \ 206 " : 207 : __imm(bpf_map_lookup_elem), 208 __imm_addr(map_hash_48b), 209 __imm_const(__imm_0, MAX_ENTRIES-1), 210 __imm_const(test_val_foo, offsetof(struct test_val, foo)) 211 : __clobber_all); 212 } 213 214 SEC("tc") 215 __description("map lookup and null branch prediction") 216 __success __retval(0) 217 __naked void lookup_and_null_branch_prediction(void) 218 { 219 asm volatile (" \ 220 r1 = 10; \ 221 *(u64*)(r10 - 8) = r1; \ 222 r2 = r10; \ 223 r2 += -8; \ 224 r1 = %[map_hash_8b] ll; \ 225 call %[bpf_map_lookup_elem]; \ 226 r6 = r0; \ 227 if r6 == 0 goto l0_%=; \ 228 if r6 != 0 goto l0_%=; \ 229 r10 += 10; \ 230 l0_%=: exit; \ 231 " : 232 : __imm(bpf_map_lookup_elem), 233 __imm_addr(map_hash_8b) 234 : __clobber_all); 235 } 236 237 SEC("cgroup/skb") 238 __description("MAP_VALUE_OR_NULL check_ids() in regsafe()") 239 __failure __msg("R8 invalid mem access 'map_value_or_null'") 240 __failure_unpriv __msg_unpriv("") 241 __flag(BPF_F_TEST_STATE_FREQ) 242 __naked void null_check_ids_in_regsafe(void) 243 { 244 asm volatile (" \ 245 r1 = 0; \ 246 *(u64*)(r10 - 8) = r1; \ 247 /* r9 = map_lookup_elem(...) */ \ 248 r2 = r10; \ 249 r2 += -8; \ 250 r1 = %[map_hash_8b] ll; \ 251 call %[bpf_map_lookup_elem]; \ 252 r9 = r0; \ 253 /* r8 = map_lookup_elem(...) */ \ 254 r2 = r10; \ 255 r2 += -8; \ 256 r1 = %[map_hash_8b] ll; \ 257 call %[bpf_map_lookup_elem]; \ 258 r8 = r0; \ 259 /* r7 = ktime_get_ns() */ \ 260 call %[bpf_ktime_get_ns]; \ 261 r7 = r0; \ 262 /* r6 = ktime_get_ns() */ \ 263 call %[bpf_ktime_get_ns]; \ 264 r6 = r0; \ 265 /* if r6 > r7 goto +1 ; no new information about the state is derived from\ 266 * ; this check, thus produced verifier states differ\ 267 * ; only in 'insn_idx' \ 268 * r9 = r8 ; optionally share ID between r9 and r8\ 269 */ \ 270 if r6 > r7 goto l0_%=; \ 271 r9 = r8; \ 272 l0_%=: /* if r9 == 0 goto <exit> */ \ 273 if r9 == 0 goto l1_%=; \ 274 /* read map value via r8, this is not always \ 275 * safe because r8 might be not equal to r9. \ 276 */ \ 277 r0 = *(u64*)(r8 + 0); \ 278 l1_%=: /* exit 0 */ \ 279 r0 = 0; \ 280 exit; \ 281 " : 282 : __imm(bpf_ktime_get_ns), 283 __imm(bpf_map_lookup_elem), 284 __imm_addr(map_hash_8b) 285 : __clobber_all); 286 } 287 288 char _license[] SEC("license") = "GPL"; 289