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

selftests/bpf: Add tests for kfunc returning a memory pointer

We add 2 new kfuncs that are following the RET_PTR_TO_MEM
capability from the previous commit.
Then we test them in selftests:
the first tests are testing valid case, and are not failing,
and the later ones are actually preventing the program to be loaded
because they are wrong.

To work around that, we mark the failing ones as not autoloaded
(with SEC("?tc")), and we manually enable them one by one, ensuring
the verifier rejects them.

Signed-off-by: Benjamin Tissoires <benjamin.tissoires@redhat.com>
Link: https://lore.kernel.org/r/20220906151303.2780789-8-benjamin.tissoires@redhat.com
Signed-off-by: Alexei Starovoitov <ast@kernel.org>

authored by

Benjamin Tissoires and committed by
Alexei Starovoitov
22ed8d5a eb1f7f71

+197
+36
net/bpf/test_run.c
··· 606 606 WARN_ON_ONCE(1); 607 607 } 608 608 609 + static int *__bpf_kfunc_call_test_get_mem(struct prog_test_ref_kfunc *p, const int size) 610 + { 611 + if (size > 2 * sizeof(int)) 612 + return NULL; 613 + 614 + return (int *)p; 615 + } 616 + 617 + noinline int *bpf_kfunc_call_test_get_rdwr_mem(struct prog_test_ref_kfunc *p, const int rdwr_buf_size) 618 + { 619 + return __bpf_kfunc_call_test_get_mem(p, rdwr_buf_size); 620 + } 621 + 622 + noinline int *bpf_kfunc_call_test_get_rdonly_mem(struct prog_test_ref_kfunc *p, const int rdonly_buf_size) 623 + { 624 + return __bpf_kfunc_call_test_get_mem(p, rdonly_buf_size); 625 + } 626 + 627 + /* the next 2 ones can't be really used for testing expect to ensure 628 + * that the verifier rejects the call. 629 + * Acquire functions must return struct pointers, so these ones are 630 + * failing. 631 + */ 632 + noinline int *bpf_kfunc_call_test_acq_rdonly_mem(struct prog_test_ref_kfunc *p, const int rdonly_buf_size) 633 + { 634 + return __bpf_kfunc_call_test_get_mem(p, rdonly_buf_size); 635 + } 636 + 637 + noinline void bpf_kfunc_call_int_mem_release(int *p) 638 + { 639 + } 640 + 609 641 noinline struct prog_test_ref_kfunc * 610 642 bpf_kfunc_call_test_kptr_get(struct prog_test_ref_kfunc **pp, int a, int b) 611 643 { ··· 744 712 BTF_ID_FLAGS(func, bpf_kfunc_call_test_release, KF_RELEASE) 745 713 BTF_ID_FLAGS(func, bpf_kfunc_call_memb_release, KF_RELEASE) 746 714 BTF_ID_FLAGS(func, bpf_kfunc_call_memb1_release, KF_RELEASE) 715 + BTF_ID_FLAGS(func, bpf_kfunc_call_test_get_rdwr_mem, KF_RET_NULL) 716 + BTF_ID_FLAGS(func, bpf_kfunc_call_test_get_rdonly_mem, KF_RET_NULL) 717 + BTF_ID_FLAGS(func, bpf_kfunc_call_test_acq_rdonly_mem, KF_ACQUIRE | KF_RET_NULL) 718 + BTF_ID_FLAGS(func, bpf_kfunc_call_int_mem_release, KF_RELEASE) 747 719 BTF_ID_FLAGS(func, bpf_kfunc_call_test_kptr_get, KF_ACQUIRE | KF_RET_NULL | KF_KPTR_GET) 748 720 BTF_ID_FLAGS(func, bpf_kfunc_call_test_pass_ctx) 749 721 BTF_ID_FLAGS(func, bpf_kfunc_call_test_pass1)
+7
tools/testing/selftests/bpf/prog_tests/kfunc_call.c
··· 50 50 #define SYSCALL_TEST(name, retval) __BPF_TEST_SUCCESS(name, retval, syscall_test) 51 51 #define SYSCALL_NULL_CTX_TEST(name, retval) __BPF_TEST_SUCCESS(name, retval, syscall_null_ctx_test) 52 52 53 + #define TC_FAIL(name, retval, error_msg) __BPF_TEST_FAIL(name, retval, tc_test, error_msg) 53 54 #define SYSCALL_NULL_CTX_FAIL(name, retval, error_msg) \ 54 55 __BPF_TEST_FAIL(name, retval, syscall_null_ctx_test, error_msg) 55 56 ··· 63 62 */ 64 63 SYSCALL_NULL_CTX_FAIL(kfunc_syscall_test_fail, -EINVAL, "processed 4 insns"), 65 64 SYSCALL_NULL_CTX_FAIL(kfunc_syscall_test_null_fail, -EINVAL, "processed 4 insns"), 65 + TC_FAIL(kfunc_call_test_get_mem_fail_rdonly, 0, "R0 cannot write into rdonly_mem"), 66 + TC_FAIL(kfunc_call_test_get_mem_fail_use_after_free, 0, "invalid mem access 'scalar'"), 67 + TC_FAIL(kfunc_call_test_get_mem_fail_oob, 0, "min value is outside of the allowed memory range"), 68 + TC_FAIL(kfunc_call_test_get_mem_fail_not_const, 0, "is not a const"), 69 + TC_FAIL(kfunc_call_test_mem_acquire_fail, 0, "acquire kernel function does not return PTR_TO_BTF_ID"), 66 70 67 71 /* success cases */ 68 72 TC_TEST(kfunc_call_test1, 12), 69 73 TC_TEST(kfunc_call_test2, 3), 70 74 TC_TEST(kfunc_call_test_ref_btf_id, 0), 75 + TC_TEST(kfunc_call_test_get_mem, 42), 71 76 SYSCALL_TEST(kfunc_syscall_test, 0), 72 77 SYSCALL_NULL_CTX_TEST(kfunc_syscall_test_null, 0), 73 78 };
+121
tools/testing/selftests/bpf/progs/kfunc_call_fail.c
··· 3 3 #include <vmlinux.h> 4 4 #include <bpf/bpf_helpers.h> 5 5 6 + extern struct prog_test_ref_kfunc *bpf_kfunc_call_test_acquire(unsigned long *sp) __ksym; 7 + extern void bpf_kfunc_call_test_release(struct prog_test_ref_kfunc *p) __ksym; 6 8 extern void bpf_kfunc_call_test_mem_len_pass1(void *mem, int len) __ksym; 9 + extern int *bpf_kfunc_call_test_get_rdwr_mem(struct prog_test_ref_kfunc *p, const int rdwr_buf_size) __ksym; 10 + extern int *bpf_kfunc_call_test_get_rdonly_mem(struct prog_test_ref_kfunc *p, const int rdonly_buf_size) __ksym; 11 + extern int *bpf_kfunc_call_test_acq_rdonly_mem(struct prog_test_ref_kfunc *p, const int rdonly_buf_size) __ksym; 12 + extern void bpf_kfunc_call_int_mem_release(int *p) __ksym; 7 13 8 14 struct syscall_test_args { 9 15 __u8 data[16]; ··· 40 34 bpf_kfunc_call_test_mem_len_pass1(args, sizeof(*args)); 41 35 42 36 return 0; 37 + } 38 + 39 + SEC("?tc") 40 + int kfunc_call_test_get_mem_fail_rdonly(struct __sk_buff *skb) 41 + { 42 + struct prog_test_ref_kfunc *pt; 43 + unsigned long s = 0; 44 + int *p = NULL; 45 + int ret = 0; 46 + 47 + pt = bpf_kfunc_call_test_acquire(&s); 48 + if (pt) { 49 + p = bpf_kfunc_call_test_get_rdonly_mem(pt, 2 * sizeof(int)); 50 + if (p) 51 + p[0] = 42; /* this is a read-only buffer, so -EACCES */ 52 + else 53 + ret = -1; 54 + 55 + bpf_kfunc_call_test_release(pt); 56 + } 57 + return ret; 58 + } 59 + 60 + SEC("?tc") 61 + int kfunc_call_test_get_mem_fail_use_after_free(struct __sk_buff *skb) 62 + { 63 + struct prog_test_ref_kfunc *pt; 64 + unsigned long s = 0; 65 + int *p = NULL; 66 + int ret = 0; 67 + 68 + pt = bpf_kfunc_call_test_acquire(&s); 69 + if (pt) { 70 + p = bpf_kfunc_call_test_get_rdwr_mem(pt, 2 * sizeof(int)); 71 + if (p) { 72 + p[0] = 42; 73 + ret = p[1]; /* 108 */ 74 + } else { 75 + ret = -1; 76 + } 77 + 78 + bpf_kfunc_call_test_release(pt); 79 + } 80 + if (p) 81 + ret = p[0]; /* p is not valid anymore */ 82 + 83 + return ret; 84 + } 85 + 86 + SEC("?tc") 87 + int kfunc_call_test_get_mem_fail_oob(struct __sk_buff *skb) 88 + { 89 + struct prog_test_ref_kfunc *pt; 90 + unsigned long s = 0; 91 + int *p = NULL; 92 + int ret = 0; 93 + 94 + pt = bpf_kfunc_call_test_acquire(&s); 95 + if (pt) { 96 + p = bpf_kfunc_call_test_get_rdonly_mem(pt, 2 * sizeof(int)); 97 + if (p) 98 + ret = p[2 * sizeof(int)]; /* oob access, so -EACCES */ 99 + else 100 + ret = -1; 101 + 102 + bpf_kfunc_call_test_release(pt); 103 + } 104 + return ret; 105 + } 106 + 107 + int not_const_size = 2 * sizeof(int); 108 + 109 + SEC("?tc") 110 + int kfunc_call_test_get_mem_fail_not_const(struct __sk_buff *skb) 111 + { 112 + struct prog_test_ref_kfunc *pt; 113 + unsigned long s = 0; 114 + int *p = NULL; 115 + int ret = 0; 116 + 117 + pt = bpf_kfunc_call_test_acquire(&s); 118 + if (pt) { 119 + p = bpf_kfunc_call_test_get_rdonly_mem(pt, not_const_size); /* non const size, -EINVAL */ 120 + if (p) 121 + ret = p[0]; 122 + else 123 + ret = -1; 124 + 125 + bpf_kfunc_call_test_release(pt); 126 + } 127 + return ret; 128 + } 129 + 130 + SEC("?tc") 131 + int kfunc_call_test_mem_acquire_fail(struct __sk_buff *skb) 132 + { 133 + struct prog_test_ref_kfunc *pt; 134 + unsigned long s = 0; 135 + int *p = NULL; 136 + int ret = 0; 137 + 138 + pt = bpf_kfunc_call_test_acquire(&s); 139 + if (pt) { 140 + /* we are failing on this one, because we are not acquiring a PTR_TO_BTF_ID (a struct ptr) */ 141 + p = bpf_kfunc_call_test_acq_rdonly_mem(pt, 2 * sizeof(int)); 142 + if (p) 143 + ret = p[0]; 144 + else 145 + ret = -1; 146 + 147 + bpf_kfunc_call_int_mem_release(p); 148 + 149 + bpf_kfunc_call_test_release(pt); 150 + } 151 + return ret; 43 152 } 44 153 45 154 char _license[] SEC("license") = "GPL";
+33
tools/testing/selftests/bpf/progs/kfunc_call_test.c
··· 14 14 extern void bpf_kfunc_call_test_pass2(struct prog_test_pass2 *p) __ksym; 15 15 extern void bpf_kfunc_call_test_mem_len_pass1(void *mem, int len) __ksym; 16 16 extern void bpf_kfunc_call_test_mem_len_fail2(__u64 *mem, int len) __ksym; 17 + extern int *bpf_kfunc_call_test_get_rdwr_mem(struct prog_test_ref_kfunc *p, const int rdwr_buf_size) __ksym; 18 + extern int *bpf_kfunc_call_test_get_rdonly_mem(struct prog_test_ref_kfunc *p, const int rdonly_buf_size) __ksym; 17 19 18 20 SEC("tc") 19 21 int kfunc_call_test2(struct __sk_buff *skb) ··· 130 128 bpf_kfunc_call_test_mem_len_pass1(args, 0); 131 129 132 130 return 0; 131 + } 132 + 133 + SEC("tc") 134 + int kfunc_call_test_get_mem(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 + p = bpf_kfunc_call_test_get_rdwr_mem(pt, 2 * sizeof(int)); 144 + if (p) { 145 + p[0] = 42; 146 + ret = p[1]; /* 108 */ 147 + } else { 148 + ret = -1; 149 + } 150 + 151 + if (ret >= 0) { 152 + p = bpf_kfunc_call_test_get_rdonly_mem(pt, 2 * sizeof(int)); 153 + if (p) 154 + ret = p[0]; /* 42 */ 155 + else 156 + ret = -1; 157 + } 158 + 159 + bpf_kfunc_call_test_release(pt); 160 + } 161 + return ret; 133 162 } 134 163 135 164 char _license[] SEC("license") = "GPL";