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")
kfunc_call_test_spin_lock_unsafe(struct __sk_buff * skb)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")
kfunc_syscall_test_fail(struct syscall_test_args * args)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")
kfunc_syscall_test_null_fail(struct syscall_test_args * args)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")
kfunc_call_test_get_mem_fail_rdonly(struct __sk_buff * skb)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")
kfunc_call_test_get_mem_fail_use_after_free(struct __sk_buff * skb)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")
kfunc_call_test_get_mem_fail_oob(struct __sk_buff * skb)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")
kfunc_call_test_get_mem_fail_zero_size(struct __sk_buff * skb)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")
kfunc_call_test_get_mem_fail_oversized(struct __sk_buff * skb)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")
kfunc_call_test_get_mem_fail_not_const(struct __sk_buff * skb)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")
kfunc_call_test_mem_acquire_fail(struct __sk_buff * skb)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")
kfunc_call_test_pointer_arg_type_mismatch(struct __sk_buff * skb)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