| // SPDX-License-Identifier: GPL-2.0 |
| /* Copyright (c) 2021 Facebook */ |
| #include <vmlinux.h> |
| #include <bpf/bpf_helpers.h> |
| #include "../test_kmods/bpf_testmod_kfunc.h" |
| |
| static struct bpf_spin_lock kfunc_call_lock SEC(".data.A"); |
| |
| SEC("?tc") |
| int kfunc_call_test_spin_lock_unsafe(struct __sk_buff *skb) |
| { |
| bpf_spin_lock(&kfunc_call_lock); |
| bpf_kfunc_trigger_ctx_check(); |
| bpf_spin_unlock(&kfunc_call_lock); |
| |
| return 0; |
| } |
| |
| struct syscall_test_args { |
| __u8 data[16]; |
| size_t size; |
| }; |
| |
| SEC("?syscall") |
| int kfunc_syscall_test_fail(struct syscall_test_args *args) |
| { |
| bpf_kfunc_call_test_mem_len_pass1(&args->data, sizeof(*args) + 1); |
| |
| return 0; |
| } |
| |
| SEC("?syscall") |
| int kfunc_syscall_test_null_fail(struct syscall_test_args *args) |
| { |
| /* Must be called with args as a NULL pointer |
| * we do not check for it to have the verifier consider that |
| * the pointer might not be null, and so we can load it. |
| * |
| * So the following can not be added: |
| * |
| * if (args) |
| * return -22; |
| */ |
| |
| bpf_kfunc_call_test_mem_len_pass1(args, sizeof(*args)); |
| |
| return 0; |
| } |
| |
| SEC("?tc") |
| int kfunc_call_test_get_mem_fail_rdonly(struct __sk_buff *skb) |
| { |
| struct prog_test_ref_kfunc *pt; |
| unsigned long s = 0; |
| int *p = NULL; |
| int ret = 0; |
| |
| pt = bpf_kfunc_call_test_acquire(&s); |
| if (pt) { |
| p = bpf_kfunc_call_test_get_rdonly_mem(pt, 2 * sizeof(int)); |
| if (p) |
| p[0] = 42; /* this is a read-only buffer, so -EACCES */ |
| else |
| ret = -1; |
| |
| bpf_kfunc_call_test_release(pt); |
| } |
| return ret; |
| } |
| |
| SEC("?tc") |
| int kfunc_call_test_get_mem_fail_use_after_free(struct __sk_buff *skb) |
| { |
| struct prog_test_ref_kfunc *pt; |
| unsigned long s = 0; |
| int *p = NULL; |
| int ret = 0; |
| |
| pt = bpf_kfunc_call_test_acquire(&s); |
| if (pt) { |
| p = bpf_kfunc_call_test_get_rdwr_mem(pt, 2 * sizeof(int)); |
| if (p) { |
| p[0] = 42; |
| ret = p[1]; /* 108 */ |
| } else { |
| ret = -1; |
| } |
| |
| bpf_kfunc_call_test_release(pt); |
| } |
| if (p) |
| ret = p[0]; /* p is not valid anymore */ |
| |
| return ret; |
| } |
| |
| SEC("?tc") |
| int kfunc_call_test_get_mem_fail_oob(struct __sk_buff *skb) |
| { |
| struct prog_test_ref_kfunc *pt; |
| unsigned long s = 0; |
| int *p = NULL; |
| int ret = 0; |
| |
| pt = bpf_kfunc_call_test_acquire(&s); |
| if (pt) { |
| p = bpf_kfunc_call_test_get_rdonly_mem(pt, 2 * sizeof(int)); |
| if (p) |
| ret = p[2 * sizeof(int)]; /* oob access, so -EACCES */ |
| else |
| ret = -1; |
| |
| bpf_kfunc_call_test_release(pt); |
| } |
| return ret; |
| } |
| |
| SEC("?tc") |
| int kfunc_call_test_get_mem_fail_zero_size(struct __sk_buff *skb) |
| { |
| struct prog_test_ref_kfunc *pt; |
| unsigned long s = 0; |
| int *p = NULL; |
| int ret = 0; |
| |
| pt = bpf_kfunc_call_test_acquire(&s); |
| if (pt) { |
| /* |
| * An explicit rdwr_buf_size of 0 gives R0 a zero-sized buffer, |
| * so any access is out of bounds, hence -EACCES. Previously the |
| * verifier treated a zero size as "no size argument" and sized |
| * R0 after the pointed-to return type, wrongly allowing the read. |
| */ |
| p = bpf_kfunc_call_test_get_rdwr_mem(pt, 0); |
| if (p) |
| ret = p[0]; |
| else |
| ret = -1; |
| |
| bpf_kfunc_call_test_release(pt); |
| } |
| return ret; |
| } |
| |
| SEC("?tc") |
| int kfunc_call_test_get_mem_fail_oversized(struct __sk_buff *skb) |
| { |
| struct prog_test_ref_kfunc *pt; |
| unsigned long s = 0; |
| int *p = NULL; |
| int ret = 0; |
| |
| pt = bpf_kfunc_call_test_acquire(&s); |
| if (pt) { |
| /* |
| * rdwr_buf_size is a const int, so a C literal is narrowed to |
| * 32 bits before the call. Force the full 64-bit value 2^64 - 192 |
| * (0xffffffffffffff40, > U32_MAX) into the argument register with |
| * a 64-bit immediate load. The verifier records r0_size from the |
| * full register value and must reject it before that value is |
| * truncated into R0's u32 mem_size. |
| */ |
| asm volatile ( |
| "r1 = %[pt];" |
| "r2 = %[oversized] ll;" |
| "call %[get_rdwr_mem];" |
| "%[p] = r0;" |
| : [p] "=r"(p) |
| : [pt] "r"(pt), |
| [oversized] "i"(0xffffffffffffff40LL), |
| [get_rdwr_mem] "i"(bpf_kfunc_call_test_get_rdwr_mem) |
| : "r0", "r1", "r2", "r3", "r4", "r5"); |
| bpf_kfunc_call_test_release(pt); |
| } |
| return ret; |
| } |
| |
| int not_const_size = 2 * sizeof(int); |
| |
| SEC("?tc") |
| int kfunc_call_test_get_mem_fail_not_const(struct __sk_buff *skb) |
| { |
| struct prog_test_ref_kfunc *pt; |
| unsigned long s = 0; |
| int *p = NULL; |
| int ret = 0; |
| |
| pt = bpf_kfunc_call_test_acquire(&s); |
| if (pt) { |
| p = bpf_kfunc_call_test_get_rdonly_mem(pt, not_const_size); /* non const size, -EINVAL */ |
| if (p) |
| ret = p[0]; |
| else |
| ret = -1; |
| |
| bpf_kfunc_call_test_release(pt); |
| } |
| return ret; |
| } |
| |
| SEC("?tc") |
| int kfunc_call_test_mem_acquire_fail(struct __sk_buff *skb) |
| { |
| struct prog_test_ref_kfunc *pt; |
| unsigned long s = 0; |
| int *p = NULL; |
| int ret = 0; |
| |
| pt = bpf_kfunc_call_test_acquire(&s); |
| if (pt) { |
| /* we are failing on this one, because we are not acquiring a PTR_TO_BTF_ID (a struct ptr) */ |
| p = bpf_kfunc_call_test_acq_rdonly_mem(pt, 2 * sizeof(int)); |
| if (p) |
| ret = p[0]; |
| else |
| ret = -1; |
| |
| bpf_kfunc_call_int_mem_release(p); |
| |
| bpf_kfunc_call_test_release(pt); |
| } |
| return ret; |
| } |
| |
| SEC("?tc") |
| int kfunc_call_test_pointer_arg_type_mismatch(struct __sk_buff *skb) |
| { |
| bpf_kfunc_call_test_pass_ctx((void *)10); |
| return 0; |
| } |
| |
| char _license[] SEC("license") = "GPL"; |