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