1 // SPDX-License-Identifier: GPL-2.0 2 /* Copyright (c) 2021 Facebook */ 3 #include <vmlinux.h> 4 #include <bpf/bpf_helpers.h> 5 #include "../test_kmods/bpf_testmod_kfunc.h" 6 7 static struct bpf_spin_lock kfunc_call_lock SEC(".data.A"); 8 9 SEC("?tc") 10 int kfunc_call_test_spin_lock_unsafe(struct __sk_buff *skb) 11 { 12 bpf_spin_lock(&kfunc_call_lock); 13 bpf_kfunc_trigger_ctx_check(); 14 bpf_spin_unlock(&kfunc_call_lock); 15 16 return 0; 17 } 18 19 struct syscall_test_args { 20 __u8 data[16]; 21 size_t size; 22 }; 23 24 SEC("?syscall") 25 int kfunc_syscall_test_fail(struct syscall_test_args *args) 26 { 27 bpf_kfunc_call_test_mem_len_pass1(&args->data, sizeof(*args) + 1); 28 29 return 0; 30 } 31 32 SEC("?syscall") 33 int kfunc_syscall_test_null_fail(struct syscall_test_args *args) 34 { 35 /* Must be called with args as a NULL pointer 36 * we do not check for it to have the verifier consider that 37 * the pointer might not be null, and so we can load it. 38 * 39 * So the following can not be added: 40 * 41 * if (args) 42 * return -22; 43 */ 44 45 bpf_kfunc_call_test_mem_len_pass1(args, sizeof(*args)); 46 47 return 0; 48 } 49 50 SEC("?tc") 51 int kfunc_call_test_get_mem_fail_rdonly(struct __sk_buff *skb) 52 { 53 struct prog_test_ref_kfunc *pt; 54 unsigned long s = 0; 55 int *p = NULL; 56 int ret = 0; 57 58 pt = bpf_kfunc_call_test_acquire(&s); 59 if (pt) { 60 p = bpf_kfunc_call_test_get_rdonly_mem(pt, 2 * sizeof(int)); 61 if (p) 62 p[0] = 42; /* this is a read-only buffer, so -EACCES */ 63 else 64 ret = -1; 65 66 bpf_kfunc_call_test_release(pt); 67 } 68 return ret; 69 } 70 71 SEC("?tc") 72 int kfunc_call_test_get_mem_fail_use_after_free(struct __sk_buff *skb) 73 { 74 struct prog_test_ref_kfunc *pt; 75 unsigned long s = 0; 76 int *p = NULL; 77 int ret = 0; 78 79 pt = bpf_kfunc_call_test_acquire(&s); 80 if (pt) { 81 p = bpf_kfunc_call_test_get_rdwr_mem(pt, 2 * sizeof(int)); 82 if (p) { 83 p[0] = 42; 84 ret = p[1]; /* 108 */ 85 } else { 86 ret = -1; 87 } 88 89 bpf_kfunc_call_test_release(pt); 90 } 91 if (p) 92 ret = p[0]; /* p is not valid anymore */ 93 94 return ret; 95 } 96 97 SEC("?tc") 98 int kfunc_call_test_get_mem_fail_oob(struct __sk_buff *skb) 99 { 100 struct prog_test_ref_kfunc *pt; 101 unsigned long s = 0; 102 int *p = NULL; 103 int ret = 0; 104 105 pt = bpf_kfunc_call_test_acquire(&s); 106 if (pt) { 107 p = bpf_kfunc_call_test_get_rdonly_mem(pt, 2 * sizeof(int)); 108 if (p) 109 ret = p[2 * sizeof(int)]; /* oob access, so -EACCES */ 110 else 111 ret = -1; 112 113 bpf_kfunc_call_test_release(pt); 114 } 115 return ret; 116 } 117 118 SEC("?tc") 119 int kfunc_call_test_get_mem_fail_zero_size(struct __sk_buff *skb) 120 { 121 struct prog_test_ref_kfunc *pt; 122 unsigned long s = 0; 123 int *p = NULL; 124 int ret = 0; 125 126 pt = bpf_kfunc_call_test_acquire(&s); 127 if (pt) { 128 /* 129 * An explicit rdwr_buf_size of 0 gives R0 a zero-sized buffer, 130 * so any access is out of bounds, hence -EACCES. Previously the 131 * verifier treated a zero size as "no size argument" and sized 132 * R0 after the pointed-to return type, wrongly allowing the read. 133 */ 134 p = bpf_kfunc_call_test_get_rdwr_mem(pt, 0); 135 if (p) 136 ret = p[0]; 137 else 138 ret = -1; 139 140 bpf_kfunc_call_test_release(pt); 141 } 142 return ret; 143 } 144 145 SEC("?tc") 146 int kfunc_call_test_get_mem_fail_oversized(struct __sk_buff *skb) 147 { 148 struct prog_test_ref_kfunc *pt; 149 unsigned long s = 0; 150 int *p = NULL; 151 int ret = 0; 152 153 pt = bpf_kfunc_call_test_acquire(&s); 154 if (pt) { 155 /* 156 * rdwr_buf_size is a const int, so a C literal is narrowed to 157 * 32 bits before the call. Force the full 64-bit value 2^64 - 192 158 * (0xffffffffffffff40, > U32_MAX) into the argument register with 159 * a 64-bit immediate load. The verifier records r0_size from the 160 * full register value and must reject it before that value is 161 * truncated into R0's u32 mem_size. 162 */ 163 asm volatile ( 164 "r1 = %[pt];" 165 "r2 = %[oversized] ll;" 166 "call %[get_rdwr_mem];" 167 "%[p] = r0;" 168 : [p] "=r"(p) 169 : [pt] "r"(pt), 170 [oversized] "i"(0xffffffffffffff40LL), 171 [get_rdwr_mem] "i"(bpf_kfunc_call_test_get_rdwr_mem) 172 : "r0", "r1", "r2", "r3", "r4", "r5"); 173 bpf_kfunc_call_test_release(pt); 174 } 175 return ret; 176 } 177 178 int not_const_size = 2 * sizeof(int); 179 180 SEC("?tc") 181 int kfunc_call_test_get_mem_fail_not_const(struct __sk_buff *skb) 182 { 183 struct prog_test_ref_kfunc *pt; 184 unsigned long s = 0; 185 int *p = NULL; 186 int ret = 0; 187 188 pt = bpf_kfunc_call_test_acquire(&s); 189 if (pt) { 190 p = bpf_kfunc_call_test_get_rdonly_mem(pt, not_const_size); /* non const size, -EINVAL */ 191 if (p) 192 ret = p[0]; 193 else 194 ret = -1; 195 196 bpf_kfunc_call_test_release(pt); 197 } 198 return ret; 199 } 200 201 SEC("?tc") 202 int kfunc_call_test_mem_acquire_fail(struct __sk_buff *skb) 203 { 204 struct prog_test_ref_kfunc *pt; 205 unsigned long s = 0; 206 int *p = NULL; 207 int ret = 0; 208 209 pt = bpf_kfunc_call_test_acquire(&s); 210 if (pt) { 211 /* we are failing on this one, because we are not acquiring a PTR_TO_BTF_ID (a struct ptr) */ 212 p = bpf_kfunc_call_test_acq_rdonly_mem(pt, 2 * sizeof(int)); 213 if (p) 214 ret = p[0]; 215 else 216 ret = -1; 217 218 bpf_kfunc_call_int_mem_release(p); 219 220 bpf_kfunc_call_test_release(pt); 221 } 222 return ret; 223 } 224 225 SEC("?tc") 226 int kfunc_call_test_pointer_arg_type_mismatch(struct __sk_buff *skb) 227 { 228 bpf_kfunc_call_test_pass_ctx((void *)10); 229 return 0; 230 } 231 232 char _license[] SEC("license") = "GPL"; 233