Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

selftests/bpf: check operations on untrusted ro pointers to mem

The following cases are tested:
- it is ok to load memory at any offset from rdonly_untrusted_mem;
- rdonly_untrusted_mem offset/bounds are not tracked;
- writes into rdonly_untrusted_mem are forbidden;
- atomic operations on rdonly_untrusted_mem are forbidden;
- rdonly_untrusted_mem can't be passed as a memory argument of a
helper of kfunc;
- it is ok to use PTR_TO_MEM and PTR_TO_BTF_ID in a same load
instruction.

Signed-off-by: Eduard Zingerman <eddyz87@gmail.com>
Link: https://lore.kernel.org/r/20250625182414.30659-4-eddyz87@gmail.com
Signed-off-by: Alexei Starovoitov <ast@kernel.org>

authored by

Eduard Zingerman and committed by
Alexei Starovoitov
12ed81f8 f2362a57

+145
+9
tools/testing/selftests/bpf/prog_tests/mem_rdonly_untrusted.c
··· 1 + // SPDX-License-Identifier: GPL-2.0-only 2 + 3 + #include <test_progs.h> 4 + #include "mem_rdonly_untrusted.skel.h" 5 + 6 + void test_mem_rdonly_untrusted(void) 7 + { 8 + RUN_TESTS(mem_rdonly_untrusted); 9 + }
+136
tools/testing/selftests/bpf/progs/mem_rdonly_untrusted.c
··· 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 "../test_kmods/bpf_testmod_kfunc.h" 7 + 8 + SEC("socket") 9 + __success 10 + __retval(0) 11 + int ldx_is_ok_bad_addr(void *ctx) 12 + { 13 + char *p; 14 + 15 + if (!bpf_core_enum_value_exists(enum bpf_features, BPF_FEAT_RDONLY_CAST_TO_VOID)) 16 + return 42; 17 + 18 + p = bpf_rdonly_cast(0, 0); 19 + return p[0x7fff]; 20 + } 21 + 22 + SEC("socket") 23 + __success 24 + __retval(1) 25 + int ldx_is_ok_good_addr(void *ctx) 26 + { 27 + int v, *p; 28 + 29 + v = 1; 30 + p = bpf_rdonly_cast(&v, 0); 31 + return *p; 32 + } 33 + 34 + SEC("socket") 35 + __success 36 + int offset_not_tracked(void *ctx) 37 + { 38 + int *p, i, s; 39 + 40 + p = bpf_rdonly_cast(0, 0); 41 + s = 0; 42 + bpf_for(i, 0, 1000 * 1000 * 1000) { 43 + p++; 44 + s += *p; 45 + } 46 + return s; 47 + } 48 + 49 + SEC("socket") 50 + __failure 51 + __msg("cannot write into rdonly_untrusted_mem") 52 + int stx_not_ok(void *ctx) 53 + { 54 + int v, *p; 55 + 56 + v = 1; 57 + p = bpf_rdonly_cast(&v, 0); 58 + *p = 1; 59 + return 0; 60 + } 61 + 62 + SEC("socket") 63 + __failure 64 + __msg("cannot write into rdonly_untrusted_mem") 65 + int atomic_not_ok(void *ctx) 66 + { 67 + int v, *p; 68 + 69 + v = 1; 70 + p = bpf_rdonly_cast(&v, 0); 71 + __sync_fetch_and_add(p, 1); 72 + return 0; 73 + } 74 + 75 + SEC("socket") 76 + __failure 77 + __msg("cannot write into rdonly_untrusted_mem") 78 + int atomic_rmw_not_ok(void *ctx) 79 + { 80 + long v, *p; 81 + 82 + v = 1; 83 + p = bpf_rdonly_cast(&v, 0); 84 + return __sync_val_compare_and_swap(p, 0, 42); 85 + } 86 + 87 + SEC("socket") 88 + __failure 89 + __msg("invalid access to memory, mem_size=0 off=0 size=4") 90 + __msg("R1 min value is outside of the allowed memory range") 91 + int kfunc_param_not_ok(void *ctx) 92 + { 93 + int *p; 94 + 95 + p = bpf_rdonly_cast(0, 0); 96 + bpf_kfunc_trusted_num_test(p); 97 + return 0; 98 + } 99 + 100 + SEC("?fentry.s/" SYS_PREFIX "sys_getpgid") 101 + __failure 102 + __msg("R1 type=rdonly_untrusted_mem expected=") 103 + int helper_param_not_ok(void *ctx) 104 + { 105 + char *p; 106 + 107 + p = bpf_rdonly_cast(0, 0); 108 + /* 109 + * Any helper with ARG_CONST_SIZE_OR_ZERO constraint will do, 110 + * the most permissive constraint 111 + */ 112 + bpf_copy_from_user(p, 0, (void *)42); 113 + return 0; 114 + } 115 + 116 + static __noinline u64 *get_some_addr(void) 117 + { 118 + if (bpf_get_prandom_u32()) 119 + return bpf_rdonly_cast(0, bpf_core_type_id_kernel(struct sock)); 120 + else 121 + return bpf_rdonly_cast(0, 0); 122 + } 123 + 124 + SEC("socket") 125 + __success 126 + __retval(0) 127 + int mixed_mem_type(void *ctx) 128 + { 129 + u64 *p; 130 + 131 + /* Try to avoid compiler hoisting load to if branches by using __noinline func. */ 132 + p = get_some_addr(); 133 + return *p; 134 + } 135 + 136 + char _license[] SEC("license") = "GPL";