1 // SPDX-License-Identifier: GPL-2.0 2 /* Converted from tools/testing/selftests/bpf/verifier/var_off.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_HASH); 10 __uint(max_entries, 1); 11 __type(key, long long); 12 __type(value, long long); 13 } map_hash_8b SEC(".maps"); 14 15 SEC("lwt_in") 16 __description("variable-offset ctx access") 17 __failure __msg("variable ctx access var_off=(0x0; 0x4)") 18 __naked void variable_offset_ctx_access(void) 19 { 20 asm volatile (" \ 21 /* Get an unknown value */ \ 22 r2 = *(u32*)(r1 + 0); \ 23 /* Make it small and 4-byte aligned */ \ 24 r2 &= 4; \ 25 /* add it to skb. We now have either &skb->len or\ 26 * &skb->pkt_type, but we don't know which \ 27 */ \ 28 r1 += r2; \ 29 /* dereference it */ \ 30 r0 = *(u32*)(r1 + 0); \ 31 exit; \ 32 " ::: __clobber_all); 33 } 34 35 SEC("cgroup/skb") 36 __description("variable-offset stack read, priv vs unpriv") 37 __success __failure_unpriv 38 __msg_unpriv("R2 variable stack access prohibited for !root") 39 __retval(0) 40 __naked void stack_read_priv_vs_unpriv(void) 41 { 42 asm volatile (" \ 43 /* Fill the top 8 bytes of the stack */ \ 44 r0 = 0; \ 45 *(u64*)(r10 - 8) = r0; \ 46 /* Get an unknown value */ \ 47 r2 = *(u32*)(r1 + 0); \ 48 /* Make it small and 4-byte aligned */ \ 49 r2 &= 4; \ 50 r2 -= 8; \ 51 /* add it to fp. We now have either fp-4 or fp-8, but\ 52 * we don't know which \ 53 */ \ 54 r2 += r10; \ 55 /* dereference it for a stack read */ \ 56 r0 = *(u32*)(r2 + 0); \ 57 r0 = 0; \ 58 exit; \ 59 " ::: __clobber_all); 60 } 61 62 SEC("cgroup/skb") 63 __description("variable-offset stack read, uninitialized") 64 __success 65 __failure_unpriv __msg_unpriv("R2 variable stack access prohibited for !root") 66 __naked void variable_offset_stack_read_uninitialized(void) 67 { 68 asm volatile (" \ 69 /* Get an unknown value */ \ 70 r2 = *(u32*)(r1 + 0); \ 71 /* Make it small and 4-byte aligned */ \ 72 r2 &= 4; \ 73 r2 -= 8; \ 74 /* add it to fp. We now have either fp-4 or fp-8, but\ 75 * we don't know which \ 76 */ \ 77 r2 += r10; \ 78 /* dereference it for a stack read */ \ 79 r0 = *(u32*)(r2 + 0); \ 80 r0 = 0; \ 81 exit; \ 82 " ::: __clobber_all); 83 } 84 85 SEC("socket") 86 __description("variable-offset stack write, priv vs unpriv") 87 __success 88 /* Check that the maximum stack depth is correctly maintained according to the 89 * maximum possible variable offset. 90 */ 91 __log_level(4) __msg("stack depth 16") 92 __failure_unpriv 93 /* Variable stack access is rejected for unprivileged. 94 */ 95 __msg_unpriv("R2 variable stack access prohibited for !root") 96 __retval(0) 97 __naked void stack_write_priv_vs_unpriv(void) 98 { 99 asm volatile (" \ 100 /* Get an unknown value */ \ 101 r2 = *(u32*)(r1 + 0); \ 102 /* Make it small and 8-byte aligned */ \ 103 r2 &= 8; \ 104 r2 -= 16; \ 105 /* Add it to fp. We now have either fp-8 or \ 106 * fp-16, but we don't know which \ 107 */ \ 108 r2 += r10; \ 109 /* Dereference it for a stack write */ \ 110 r0 = 0; \ 111 *(u64*)(r2 + 0) = r0; \ 112 exit; \ 113 " ::: __clobber_all); 114 } 115 116 /* Similar to the previous test, but this time also perform a read from the 117 * address written to with a variable offset. The read is allowed, showing that, 118 * after a variable-offset write, a priviledged program can read the slots that 119 * were in the range of that write (even if the verifier doesn't actually know if 120 * the slot being read was really written to or not. 121 * 122 * Despite this test being mostly a superset, the previous test is also kept for 123 * the sake of it checking the stack depth in the case where there is no read. 124 */ 125 SEC("socket") 126 __description("variable-offset stack write followed by read") 127 __success 128 /* Check that the maximum stack depth is correctly maintained according to the 129 * maximum possible variable offset. 130 */ 131 __log_level(4) __msg("stack depth 16") 132 __failure_unpriv 133 __msg_unpriv("R2 variable stack access prohibited for !root") 134 __retval(0) 135 __naked void stack_write_followed_by_read(void) 136 { 137 asm volatile (" \ 138 /* Get an unknown value */ \ 139 r2 = *(u32*)(r1 + 0); \ 140 /* Make it small and 8-byte aligned */ \ 141 r2 &= 8; \ 142 r2 -= 16; \ 143 /* Add it to fp. We now have either fp-8 or fp-16, but\ 144 * we don't know which \ 145 */ \ 146 r2 += r10; \ 147 /* Dereference it for a stack write */ \ 148 r0 = 0; \ 149 *(u64*)(r2 + 0) = r0; \ 150 /* Now read from the address we just wrote. */ \ 151 r3 = *(u64*)(r2 + 0); \ 152 r0 = 0; \ 153 exit; \ 154 " ::: __clobber_all); 155 } 156 157 SEC("socket") 158 __description("variable-offset stack write clobbers spilled regs") 159 __failure 160 /* In the priviledged case, dereferencing a spilled-and-then-filled 161 * register is rejected because the previous variable offset stack 162 * write might have overwritten the spilled pointer (i.e. we lose track 163 * of the spilled register when we analyze the write). 164 */ 165 __msg("R2 invalid mem access 'scalar'") 166 __failure_unpriv 167 /* The unprivileged case is not too interesting; variable 168 * stack access is rejected. 169 */ 170 __msg_unpriv("R2 variable stack access prohibited for !root") 171 __naked void stack_write_clobbers_spilled_regs(void) 172 { 173 asm volatile (" \ 174 /* Dummy instruction; needed because we need to patch the next one\ 175 * and we can't patch the first instruction. \ 176 */ \ 177 r6 = 0; \ 178 /* Make R0 a map ptr */ \ 179 r0 = %[map_hash_8b] ll; \ 180 /* Get an unknown value */ \ 181 r2 = *(u32*)(r1 + 0); \ 182 /* Make it small and 8-byte aligned */ \ 183 r2 &= 8; \ 184 r2 -= 16; \ 185 /* Add it to fp. We now have either fp-8 or fp-16, but\ 186 * we don't know which. \ 187 */ \ 188 r2 += r10; \ 189 /* Spill R0(map ptr) into stack */ \ 190 *(u64*)(r10 - 8) = r0; \ 191 /* Dereference the unknown value for a stack write */\ 192 r0 = 0; \ 193 *(u64*)(r2 + 0) = r0; \ 194 /* Fill the register back into R2 */ \ 195 r2 = *(u64*)(r10 - 8); \ 196 /* Try to dereference R2 for a memory load */ \ 197 r0 = *(u64*)(r2 + 8); \ 198 exit; \ 199 " : 200 : __imm_addr(map_hash_8b) 201 : __clobber_all); 202 } 203 204 SEC("sockops") 205 __description("indirect variable-offset stack access, unbounded") 206 __failure __msg("invalid unbounded variable-offset indirect access to stack R4") 207 __naked void variable_offset_stack_access_unbounded(void) 208 { 209 asm volatile (" \ 210 r2 = 6; \ 211 r3 = 28; \ 212 /* Fill the top 16 bytes of the stack. */ \ 213 r4 = 0; \ 214 *(u64*)(r10 - 16) = r4; \ 215 r4 = 0; \ 216 *(u64*)(r10 - 8) = r4; \ 217 /* Get an unknown value. */ \ 218 r4 = *(u64*)(r1 + %[bpf_sock_ops_bytes_received]);\ 219 /* Check the lower bound but don't check the upper one. */\ 220 if r4 s< 0 goto l0_%=; \ 221 /* Point the lower bound to initialized stack. Offset is now in range\ 222 * from fp-16 to fp+0x7fffffffffffffef, i.e. max value is unbounded.\ 223 */ \ 224 r4 -= 16; \ 225 r4 += r10; \ 226 r5 = 8; \ 227 /* Dereference it indirectly. */ \ 228 call %[bpf_getsockopt]; \ 229 l0_%=: r0 = 0; \ 230 exit; \ 231 " : 232 : __imm(bpf_getsockopt), 233 __imm_const(bpf_sock_ops_bytes_received, offsetof(struct bpf_sock_ops, bytes_received)) 234 : __clobber_all); 235 } 236 237 SEC("lwt_in") 238 __description("indirect variable-offset stack access, max out of bound") 239 __failure __msg("invalid variable-offset indirect access to stack R2") 240 __naked void access_max_out_of_bound(void) 241 { 242 asm volatile (" \ 243 /* Fill the top 8 bytes of the stack */ \ 244 r2 = 0; \ 245 *(u64*)(r10 - 8) = r2; \ 246 /* Get an unknown value */ \ 247 r2 = *(u32*)(r1 + 0); \ 248 /* Make it small and 4-byte aligned */ \ 249 r2 &= 4; \ 250 r2 -= 8; \ 251 /* add it to fp. We now have either fp-4 or fp-8, but\ 252 * we don't know which \ 253 */ \ 254 r2 += r10; \ 255 /* dereference it indirectly */ \ 256 r1 = %[map_hash_8b] ll; \ 257 call %[bpf_map_lookup_elem]; \ 258 r0 = 0; \ 259 exit; \ 260 " : 261 : __imm(bpf_map_lookup_elem), 262 __imm_addr(map_hash_8b) 263 : __clobber_all); 264 } 265 266 /* Similar to the test above, but this time check the special case of a 267 * zero-sized stack access. We used to have a bug causing crashes for zero-sized 268 * out-of-bounds accesses. 269 */ 270 SEC("socket") 271 __description("indirect variable-offset stack access, zero-sized, max out of bound") 272 __failure __msg("invalid variable-offset indirect access to stack R1") 273 __naked void zero_sized_access_max_out_of_bound(void) 274 { 275 asm volatile (" \ 276 r0 = 0; \ 277 /* Fill some stack */ \ 278 *(u64*)(r10 - 16) = r0; \ 279 *(u64*)(r10 - 8) = r0; \ 280 /* Get an unknown value */ \ 281 r1 = *(u32*)(r1 + 0); \ 282 r1 &= 63; \ 283 r1 += -16; \ 284 /* r1 is now anywhere in [-16,48) */ \ 285 r1 += r10; \ 286 r2 = 0; \ 287 r3 = 0; \ 288 call %[bpf_probe_read_kernel]; \ 289 exit; \ 290 " : 291 : __imm(bpf_probe_read_kernel) 292 : __clobber_all); 293 } 294 295 SEC("lwt_in") 296 __description("indirect variable-offset stack access, min out of bound") 297 __failure __msg("invalid variable-offset indirect access to stack R2") 298 __naked void access_min_out_of_bound(void) 299 { 300 asm volatile (" \ 301 /* Fill the top 8 bytes of the stack */ \ 302 r2 = 0; \ 303 *(u64*)(r10 - 8) = r2; \ 304 /* Get an unknown value */ \ 305 r2 = *(u32*)(r1 + 0); \ 306 /* Make it small and 4-byte aligned */ \ 307 r2 &= 4; \ 308 r2 -= 516; \ 309 /* add it to fp. We now have either fp-516 or fp-512, but\ 310 * we don't know which \ 311 */ \ 312 r2 += r10; \ 313 /* dereference it indirectly */ \ 314 r1 = %[map_hash_8b] ll; \ 315 call %[bpf_map_lookup_elem]; \ 316 r0 = 0; \ 317 exit; \ 318 " : 319 : __imm(bpf_map_lookup_elem), 320 __imm_addr(map_hash_8b) 321 : __clobber_all); 322 } 323 324 SEC("cgroup/skb") 325 __description("indirect variable-offset stack access, min_off < min_initialized") 326 __success 327 __failure_unpriv __msg_unpriv("R2 variable stack access prohibited for !root") 328 __naked void access_min_off_min_initialized(void) 329 { 330 asm volatile (" \ 331 /* Fill only the top 8 bytes of the stack. */ \ 332 r2 = 0; \ 333 *(u64*)(r10 - 8) = r2; \ 334 /* Get an unknown value */ \ 335 r2 = *(u32*)(r1 + 0); \ 336 /* Make it small and 4-byte aligned. */ \ 337 r2 &= 4; \ 338 r2 -= 16; \ 339 /* Add it to fp. We now have either fp-12 or fp-16, but we don't know\ 340 * which. fp-16 size 8 is partially uninitialized stack.\ 341 */ \ 342 r2 += r10; \ 343 /* Dereference it indirectly. */ \ 344 r1 = %[map_hash_8b] ll; \ 345 call %[bpf_map_lookup_elem]; \ 346 r0 = 0; \ 347 exit; \ 348 " : 349 : __imm(bpf_map_lookup_elem), 350 __imm_addr(map_hash_8b) 351 : __clobber_all); 352 } 353 354 SEC("cgroup/skb") 355 __description("indirect variable-offset stack access, priv vs unpriv") 356 __success __failure_unpriv 357 __msg_unpriv("R2 variable stack access prohibited for !root") 358 __retval(0) 359 __naked void stack_access_priv_vs_unpriv(void) 360 { 361 asm volatile (" \ 362 /* Fill the top 16 bytes of the stack. */ \ 363 r2 = 0; \ 364 *(u64*)(r10 - 16) = r2; \ 365 r2 = 0; \ 366 *(u64*)(r10 - 8) = r2; \ 367 /* Get an unknown value. */ \ 368 r2 = *(u32*)(r1 + 0); \ 369 /* Make it small and 4-byte aligned. */ \ 370 r2 &= 4; \ 371 r2 -= 16; \ 372 /* Add it to fp. We now have either fp-12 or fp-16, we don't know\ 373 * which, but either way it points to initialized stack.\ 374 */ \ 375 r2 += r10; \ 376 /* Dereference it indirectly. */ \ 377 r1 = %[map_hash_8b] ll; \ 378 call %[bpf_map_lookup_elem]; \ 379 r0 = 0; \ 380 exit; \ 381 " : 382 : __imm(bpf_map_lookup_elem), 383 __imm_addr(map_hash_8b) 384 : __clobber_all); 385 } 386 387 SEC("lwt_in") 388 __description("indirect variable-offset stack access, ok") 389 __success __retval(0) 390 __naked void variable_offset_stack_access_ok(void) 391 { 392 asm volatile (" \ 393 /* Fill the top 16 bytes of the stack. */ \ 394 r2 = 0; \ 395 *(u64*)(r10 - 16) = r2; \ 396 r2 = 0; \ 397 *(u64*)(r10 - 8) = r2; \ 398 /* Get an unknown value. */ \ 399 r2 = *(u32*)(r1 + 0); \ 400 /* Make it small and 4-byte aligned. */ \ 401 r2 &= 4; \ 402 r2 -= 16; \ 403 /* Add it to fp. We now have either fp-12 or fp-16, we don't know\ 404 * which, but either way it points to initialized stack.\ 405 */ \ 406 r2 += r10; \ 407 /* Dereference it indirectly. */ \ 408 r1 = %[map_hash_8b] ll; \ 409 call %[bpf_map_lookup_elem]; \ 410 r0 = 0; \ 411 exit; \ 412 " : 413 : __imm(bpf_map_lookup_elem), 414 __imm_addr(map_hash_8b) 415 : __clobber_all); 416 } 417 418 char _license[] SEC("license") = "GPL"; 419