1 // SPDX-License-Identifier: GPL-2.0 2 3 #include <vmlinux.h> 4 #include <bpf/bpf_core_read.h> 5 #include "bpf_misc.h" 6 #include "bpf_kfuncs.h" 7 #include "../test_kmods/bpf_testmod_kfunc.h" 8 9 SEC("tp_btf/sys_enter") 10 __success 11 __log_level(2) 12 __msg("r8 = *(u64 *)(r7 +0) ; R7=ptr_nameidata(imm={{[0-9]+}}) R8=rdonly_untrusted_mem(sz=0)") 13 __msg("r9 = *(u8 *)(r8 +0) ; R8=rdonly_untrusted_mem(sz=0) R9=scalar") 14 int btf_id_to_ptr_mem(void *ctx) 15 { 16 struct task_struct *task; 17 struct nameidata *idata; 18 u64 ret, off; 19 20 task = bpf_get_current_task_btf(); 21 idata = task->nameidata; 22 off = bpf_core_field_offset(struct nameidata, pathname); 23 /* 24 * asm block to have reliable match target for __msg, equivalent of: 25 * ret = task->nameidata->pathname[0]; 26 */ 27 asm volatile ( 28 "r7 = %[idata];" 29 "r7 += %[off];" 30 "r8 = *(u64 *)(r7 + 0);" 31 "r9 = *(u8 *)(r8 + 0);" 32 "%[ret] = r9;" 33 : [ret]"=r"(ret) 34 : [idata]"r"(idata), 35 [off]"r"(off) 36 : "r7", "r8", "r9"); 37 return ret; 38 } 39 40 SEC("socket") 41 __success 42 __retval(0) 43 int ldx_is_ok_bad_addr(void *ctx) 44 { 45 char *p; 46 47 if (!bpf_core_enum_value_exists(enum bpf_features, BPF_FEAT_RDONLY_CAST_TO_VOID)) 48 return 42; 49 50 p = bpf_rdonly_cast(0, 0); 51 return p[0x7fff]; 52 } 53 54 SEC("socket") 55 __success 56 __retval(1) 57 int ldx_is_ok_good_addr(void *ctx) 58 { 59 int v, *p; 60 61 v = 1; 62 p = bpf_rdonly_cast(&v, 0); 63 return *p; 64 } 65 66 SEC("socket") 67 __success 68 int offset_not_tracked(void *ctx) 69 { 70 int *p, i, s; 71 72 p = bpf_rdonly_cast(0, 0); 73 s = 0; 74 bpf_for(i, 0, 1000 * 1000 * 1000) { 75 p++; 76 s += *p; 77 } 78 return s; 79 } 80 81 SEC("socket") 82 __failure 83 __msg("cannot write into rdonly_untrusted_mem") 84 int stx_not_ok(void *ctx) 85 { 86 int v, *p; 87 88 v = 1; 89 p = bpf_rdonly_cast(&v, 0); 90 *p = 1; 91 return 0; 92 } 93 94 SEC("socket") 95 __failure 96 __msg("cannot write into rdonly_untrusted_mem") 97 int atomic_not_ok(void *ctx) 98 { 99 int v, *p; 100 101 v = 1; 102 p = bpf_rdonly_cast(&v, 0); 103 __sync_fetch_and_add(p, 1); 104 return 0; 105 } 106 107 SEC("socket") 108 __failure 109 __msg("cannot write into rdonly_untrusted_mem") 110 int atomic_rmw_not_ok(void *ctx) 111 { 112 long v, *p; 113 114 v = 1; 115 p = bpf_rdonly_cast(&v, 0); 116 return __sync_val_compare_and_swap(p, 0, 42); 117 } 118 119 SEC("socket") 120 __failure 121 __msg("R1 type=rdonly_untrusted_mem expected=fp") 122 int kfunc_param_not_ok(void *ctx) 123 { 124 int *p; 125 126 p = bpf_rdonly_cast(0, 0); 127 bpf_kfunc_trusted_num_test(p); 128 return 0; 129 } 130 131 SEC("?fentry.s/" SYS_PREFIX "sys_getpgid") 132 __failure 133 __msg("R1 type=rdonly_untrusted_mem expected=") 134 int helper_param_not_ok(void *ctx) 135 { 136 char *p; 137 138 p = bpf_rdonly_cast(0, 0); 139 /* 140 * Any helper with ARG_MEM_SIZE_OR_ZERO constraint will do, 141 * the most permissive constraint 142 */ 143 bpf_copy_from_user(p, 0, (void *)42); 144 return 0; 145 } 146 147 static __noinline u64 *get_some_addr(void) 148 { 149 if (bpf_get_prandom_u32()) 150 return bpf_rdonly_cast(0, bpf_core_type_id_kernel(struct sock)); 151 else 152 return bpf_rdonly_cast(0, 0); 153 } 154 155 SEC("socket") 156 __success 157 __retval(0) 158 int mixed_mem_type(void *ctx) 159 { 160 u64 *p; 161 162 /* Try to avoid compiler hoisting load to if branches by using __noinline func. */ 163 p = get_some_addr(); 164 return *p; 165 } 166 167 struct { 168 __uint(type, BPF_MAP_TYPE_RINGBUF); 169 __uint(max_entries, 4096); 170 } ringbuf SEC(".maps"); 171 172 struct { 173 __uint(type, BPF_MAP_TYPE_ARRAY); 174 __uint(max_entries, 1); 175 __type(key, u32); 176 __type(value, u64); 177 } array SEC(".maps"); 178 179 char dynptr_data[8]; 180 181 int zero; 182 183 SEC("socket") 184 __success 185 __log_level(2) 186 __msg("r8 = *(u64 *)(r7 +0){{.*}}R7=untrusted_ptr_sock") 187 __msg("r8 = *(u64 *)(r7 +0){{.*}}R7=ringbuf_mem") 188 __retval(0) 189 int mixed_mem_untrusted_btf_id_type(void *ctx) 190 { 191 u64 *p, *q, v; 192 193 p = bpf_ringbuf_reserve(&ringbuf, sizeof(*p), 0); 194 if (!p) 195 return 1; 196 *p = 42; 197 q = bpf_rdonly_cast(0, bpf_core_type_id_kernel(struct sock)); 198 /* 199 * The load below is reached with PTR_TO_MEM | MEM_RINGBUF on one 200 * path and with PTR_TO_BTF_ID | PTR_UNTRUSTED on the other. The 201 * merged type has to keep the BPF_PROBE_MEM rewrite, otherwise 202 * the NULL deref taken at runtime panics the kernel instead of 203 * returning 0. 204 */ 205 asm volatile ( 206 "r7 = %[p];" 207 "if %[zero] != 0 goto +1;" 208 "r7 = %[q];" 209 "r8 = *(u64 *)(r7 + 0);" 210 "%[v] = r8;" 211 : [v]"=r"(v) 212 : [p]"r"(p), 213 [q]"r"(q), 214 [zero]"r"(zero) 215 : "r7", "r8"); 216 bpf_ringbuf_discard(p, 0); 217 return v; 218 } 219 220 SEC("socket") 221 __success 222 __log_level(2) 223 __msg("r8 = *(u32 *)(r7 +0){{.*}}R7=ptr_nameidata") 224 __msg("r8 = *(u32 *)(r7 +0){{.*}}R7=ringbuf_mem") 225 __retval(0) 226 int mixed_mem_btf_id_type(void *ctx) 227 { 228 struct task_struct *task; 229 u32 *p, *q; 230 u64 v; 231 232 p = bpf_ringbuf_reserve(&ringbuf, sizeof(*p), 0); 233 if (!p) 234 return 1; 235 *p = 42; 236 task = bpf_get_current_task_btf(); 237 /* 238 * A plain BTF pointer walk yields a bare PTR_TO_BTF_ID, and 239 * task->nameidata is NULL unless the task currently is in the 240 * middle of a path lookup. 241 */ 242 q = (u32 *)&task->nameidata->flags; 243 /* 244 * Same as above, except that the other path yields a bare 245 * PTR_TO_BTF_ID. Merging it with PTR_TO_MEM used to drop the 246 * BPF_PROBE_MEM rewrite the bare PTR_TO_BTF_ID would have 247 * gotten on its own. 248 */ 249 asm volatile ( 250 "r7 = %[p];" 251 "if %[zero] != 0 goto +1;" 252 "r7 = %[q];" 253 "r8 = *(u32 *)(r7 + 0);" 254 "%[v] = r8;" 255 : [v]"=r"(v) 256 : [p]"r"(p), 257 [q]"r"(q), 258 [zero]"r"(zero) 259 : "r7", "r8"); 260 bpf_ringbuf_discard(p, 0); 261 return v; 262 } 263 264 SEC("socket") 265 __success 266 __log_level(2) 267 __msg("r8 = *(u32 *)(r7 +0){{.*}}R7=ptr_nameidata") 268 __msg("r8 = *(u32 *)(r7 +0){{.*}}R7=rdonly_mem") 269 __retval(0) 270 int mixed_rdonly_mem_btf_id_type(void *ctx) 271 { 272 struct task_struct *task; 273 struct bpf_dynptr dptr; 274 char buf[sizeof(u32)]; 275 u32 *p, *q; 276 u64 v; 277 278 if (bpf_dynptr_from_mem(dynptr_data, sizeof(dynptr_data), 0, &dptr)) 279 return 1; 280 p = bpf_dynptr_slice(&dptr, 0, buf, sizeof(buf)); 281 if (!p) 282 return 1; 283 task = bpf_get_current_task_btf(); 284 q = (u32 *)&task->nameidata->flags; 285 /* 286 * Same as above, except that the PTR_TO_MEM side already carries 287 * MEM_RDONLY. Merging it with a bare PTR_TO_BTF_ID used to yield 288 * PTR_TO_MEM | MEM_RDONLY, which is not rewritten either since 289 * only its PTR_UNTRUSTED variant is. 290 */ 291 asm volatile ( 292 "r7 = %[p];" 293 "if %[zero] != 0 goto +1;" 294 "r7 = %[q];" 295 "r8 = *(u32 *)(r7 + 0);" 296 "%[v] = r8;" 297 : [v]"=r"(v) 298 : [p]"r"(p), 299 [q]"r"(q), 300 [zero]"r"(zero) 301 : "r7", "r8"); 302 return v; 303 } 304 305 SEC("socket") 306 __success 307 __log_level(2) 308 __msg("r8 = *(u64 *)(r7 +0){{.*}}R7=ringbuf_mem") 309 __msg("r8 = *(u64 *)(r7 +0){{.*}}R7=rdonly_untrusted_mem") 310 __retval(0) 311 int mixed_mem_mem_type(void *ctx) 312 { 313 u64 *p, *q, v; 314 315 p = bpf_ringbuf_reserve(&ringbuf, sizeof(*p), 0); 316 if (!p) 317 return 1; 318 *p = 42; 319 q = bpf_rdonly_cast(0, 0); 320 /* 321 * Both paths are PTR_TO_MEM based, so they used to not trip the 322 * type mismatch check and skipped the merge altogether, leaving 323 * the insn with the PTR_TO_MEM | MEM_RINGBUF recorded first and 324 * hence without the BPF_PROBE_MEM rewrite the other path needs. 325 */ 326 asm volatile ( 327 "r7 = %[q];" 328 "if %[zero] == 0 goto +1;" 329 "r7 = %[p];" 330 "r8 = *(u64 *)(r7 + 0);" 331 "%[v] = r8;" 332 : [v]"=r"(v) 333 : [p]"r"(p), 334 [q]"r"(q), 335 [zero]"r"(zero) 336 : "r7", "r8"); 337 bpf_ringbuf_discard(p, 0); 338 return v; 339 } 340 341 SEC("socket") 342 __failure 343 __msg("same insn cannot be used with different pointers") 344 int mixed_map_value_mem_type(void *ctx) 345 { 346 u64 *p, *q, v; 347 u32 key = 0; 348 349 p = bpf_map_lookup_elem(&array, &key); 350 if (!p) 351 return 1; 352 q = bpf_rdonly_cast(0, 0); 353 /* 354 * PTR_TO_MAP_VALUE is neither PTR_TO_MEM nor PTR_TO_BTF_ID based, 355 * so it cannot be merged into a type which keeps the BPF_PROBE_MEM 356 * rewrite the PTR_TO_MEM | MEM_RDONLY | PTR_UNTRUSTED of the other 357 * path needs. Both bases were mismatch ok, hence the load used to be 358 * accepted with the PTR_TO_MAP_VALUE recorded and the NULL deref on 359 * the second path panicked the kernel. 360 */ 361 asm volatile ( 362 "r7 = %[q];" 363 "if %[zero] == 0 goto +1;" 364 "r7 = %[p];" 365 "r8 = *(u64 *)(r7 + 0);" 366 "%[v] = r8;" 367 : [v]"=r"(v) 368 : [p]"r"(p), 369 [q]"r"(q), 370 [zero]"r"(zero) 371 : "r7", "r8"); 372 return v; 373 } 374 375 SEC("socket") 376 __failure 377 __msg("same insn cannot be used with different pointers") 378 int mixed_stack_mem_type(void *ctx) 379 { 380 u64 *p = bpf_rdonly_cast(0, 0); 381 u64 s = 42, v; 382 383 /* 384 * Same as above, but for a PTR_TO_STACK on the other path. 385 */ 386 asm volatile ( 387 "r7 = %[p];" 388 "if %[zero] == 0 goto +1;" 389 "r7 = %[s];" 390 "r8 = *(u64 *)(r7 + 0);" 391 "%[v] = r8;" 392 : [v]"=r"(v) 393 : [p]"r"(p), 394 [s]"r"(&s), 395 [zero]"r"(zero) 396 : "r7", "r8"); 397 return v; 398 } 399 400 __attribute__((__aligned__(8))) 401 u8 global[] = { 402 0x11, 0x22, 0x33, 0x44, 403 0x55, 0x66, 0x77, 0x88, 404 0x99 405 }; 406 407 __always_inline 408 static u64 combine(void *p) 409 { 410 u64 acc; 411 412 acc = 0; 413 #if __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__ 414 acc |= (*(u64 *)p >> 56) << 24; 415 acc |= (*(u32 *)p >> 24) << 16; 416 acc |= (*(u16 *)p >> 8) << 8; 417 acc |= *(u8 *)p; 418 #else 419 acc |= (*(u64 *)p & 0xff) << 24; 420 acc |= (*(u32 *)p & 0xff) << 16; 421 acc |= (*(u16 *)p & 0xff) << 8; 422 acc |= *(u8 *)p; 423 #endif 424 return acc; 425 } 426 427 SEC("socket") 428 __retval(0x88442211) 429 int diff_size_access(void *ctx) 430 { 431 return combine(bpf_rdonly_cast(&global, 0)); 432 } 433 434 SEC("socket") 435 __retval(0x99553322) 436 int misaligned_access(void *ctx) 437 { 438 return combine(bpf_rdonly_cast(&global, 0) + 1); 439 } 440 441 __weak int return_one(void) 442 { 443 return 1; 444 } 445 446 SEC("socket") 447 __success 448 __retval(1) 449 int null_check(void *ctx) 450 { 451 int *p; 452 453 p = bpf_rdonly_cast(0, 0); 454 if (p == 0) 455 /* make this a function call to avoid compiler 456 * moving r0 assignment before check. 457 */ 458 return return_one(); 459 return 0; 460 } 461 462 SEC("socket") 463 __success 464 __retval(1) 465 int ldx_is_ok_commuted_addr(void *ctx) 466 { 467 int v, *p, *derived; 468 469 v = 1; 470 p = bpf_rdonly_cast(&v, 0); 471 asm volatile ("%[dst] = 0;" 472 "%[dst] += %[src];" 473 : [dst]"=&r"(derived) 474 : [src]"r"(p) 475 : "memory"); 476 return *derived; 477 } 478 479 char _license[] SEC("license") = "GPL"; 480