Branch data Line data Source code
1 : : // SPDX-License-Identifier: GPL-2.0-only
2 : : /* Copyright (c) 2017 Facebook
3 : : */
4 : : #include <linux/bpf.h>
5 : : #include <linux/slab.h>
6 : : #include <linux/vmalloc.h>
7 : : #include <linux/etherdevice.h>
8 : : #include <linux/filter.h>
9 : : #include <linux/sched/signal.h>
10 : : #include <net/bpf_sk_storage.h>
11 : : #include <net/sock.h>
12 : : #include <net/tcp.h>
13 : :
14 : : #define CREATE_TRACE_POINTS
15 : : #include <trace/events/bpf_test_run.h>
16 : :
17 : 0 : static int bpf_test_run(struct bpf_prog *prog, void *ctx, u32 repeat,
18 : : u32 *retval, u32 *time)
19 : : {
20 : 0 : struct bpf_cgroup_storage *storage[MAX_BPF_CGROUP_STORAGE_TYPE] = { NULL };
21 : : enum bpf_cgroup_storage_type stype;
22 : 0 : u64 time_start, time_spent = 0;
23 : : int ret = 0;
24 : : u32 i;
25 : :
26 [ # # ]: 0 : for_each_cgroup_storage_type(stype) {
27 : 0 : storage[stype] = bpf_cgroup_storage_alloc(prog, stype);
28 [ # # ]: 0 : if (IS_ERR(storage[stype])) {
29 : 0 : storage[stype] = NULL;
30 [ # # ]: 0 : for_each_cgroup_storage_type(stype)
31 : 0 : bpf_cgroup_storage_free(storage[stype]);
32 : : return -ENOMEM;
33 : : }
34 : : }
35 : :
36 [ # # ]: 0 : if (!repeat)
37 : : repeat = 1;
38 : :
39 : : rcu_read_lock();
40 : 0 : preempt_disable();
41 : : time_start = ktime_get_ns();
42 [ # # ]: 0 : for (i = 0; i < repeat; i++) {
43 : 0 : bpf_cgroup_storage_set(storage);
44 [ # # ]: 0 : *retval = BPF_PROG_RUN(prog, ctx);
45 : :
46 [ # # ]: 0 : if (signal_pending(current)) {
47 : : ret = -EINTR;
48 : : break;
49 : : }
50 : :
51 [ # # ]: 0 : if (need_resched()) {
52 : 0 : time_spent += ktime_get_ns() - time_start;
53 : 0 : preempt_enable();
54 : : rcu_read_unlock();
55 : :
56 : 0 : cond_resched();
57 : :
58 : : rcu_read_lock();
59 : 0 : preempt_disable();
60 : : time_start = ktime_get_ns();
61 : : }
62 : : }
63 : 0 : time_spent += ktime_get_ns() - time_start;
64 : 0 : preempt_enable();
65 : : rcu_read_unlock();
66 : :
67 [ # # # # : 0 : do_div(time_spent, repeat);
# # # # #
# # # # #
# # # # #
# # # # #
# # # # #
# # # # #
# # # # #
# # # # #
# # # # #
# # # # #
# # # # #
# # # # #
# # # # #
# # # # #
# # # # #
# # # # #
# # # # #
# # # # #
# # # # #
# # # # #
# # # # #
# # # # #
# # # # #
# # # # #
# # # # #
# # # # #
# # # # #
# # # # #
# # # # #
# # # # #
# # # # #
# # # # #
# # # # #
# # # # #
# # # # #
# # # # #
# # # # #
# # # # #
# # # # #
# # # # #
# # # # #
# # # # #
# # # # #
# # # # #
# # # # #
# # # # #
# # # # #
# # # # #
# # # # #
# # # # #
# # # # #
# # # # #
# # # # #
# # # # #
# # # # #
# # # # #
# # # # #
# # # # #
# # # # #
# # # # #
# # # # #
# # # # #
# # # # #
# # # # #
# # # # #
# # # # #
# # # # #
# # # # #
# # # # #
# # # # #
# # # # #
# # # # #
# # # # #
# # # # #
# # # # #
# # # # #
# # # # #
# # # # #
# # # # #
# # # # #
# # ]
68 [ # # ]: 0 : *time = time_spent > U32_MAX ? U32_MAX : (u32)time_spent;
69 : :
70 [ # # ]: 0 : for_each_cgroup_storage_type(stype)
71 : 0 : bpf_cgroup_storage_free(storage[stype]);
72 : :
73 : : return ret;
74 : : }
75 : :
76 : 0 : static int bpf_test_finish(const union bpf_attr *kattr,
77 : : union bpf_attr __user *uattr, const void *data,
78 : : u32 size, u32 retval, u32 duration)
79 : : {
80 : 0 : void __user *data_out = u64_to_user_ptr(kattr->test.data_out);
81 : 0 : int err = -EFAULT;
82 : 0 : u32 copy_size = size;
83 : :
84 : : /* Clamp copy if the user has provided a size hint, but copy the full
85 : : * buffer if not to retain old behaviour.
86 : : */
87 [ # # # # ]: 0 : if (kattr->test.data_size_out &&
88 : : copy_size > kattr->test.data_size_out) {
89 : : copy_size = kattr->test.data_size_out;
90 : 0 : err = -ENOSPC;
91 : : }
92 : :
93 [ # # # # ]: 0 : if (data_out && copy_to_user(data_out, data, copy_size))
94 : : goto out;
95 [ # # ]: 0 : if (copy_to_user(&uattr->test.data_size_out, &size, sizeof(size)))
96 : : goto out;
97 [ # # ]: 0 : if (copy_to_user(&uattr->test.retval, &retval, sizeof(retval)))
98 : : goto out;
99 [ # # ]: 0 : if (copy_to_user(&uattr->test.duration, &duration, sizeof(duration)))
100 : : goto out;
101 [ # # ]: 0 : if (err != -ENOSPC)
102 : 0 : err = 0;
103 : : out:
104 : 0 : trace_bpf_test_finish(&err);
105 : 0 : return err;
106 : : }
107 : :
108 : 0 : static void *bpf_test_init(const union bpf_attr *kattr, u32 size,
109 : : u32 headroom, u32 tailroom)
110 : : {
111 : 0 : void __user *data_in = u64_to_user_ptr(kattr->test.data_in);
112 : : void *data;
113 : :
114 [ # # # # ]: 0 : if (size < ETH_HLEN || size > PAGE_SIZE - headroom - tailroom)
115 : : return ERR_PTR(-EINVAL);
116 : :
117 : 0 : data = kzalloc(size + headroom + tailroom, GFP_USER);
118 [ # # ]: 0 : if (!data)
119 : : return ERR_PTR(-ENOMEM);
120 : :
121 [ # # ]: 0 : if (copy_from_user(data + headroom, data_in, size)) {
122 : 0 : kfree(data);
123 : 0 : return ERR_PTR(-EFAULT);
124 : : }
125 : : return data;
126 : : }
127 : :
128 : 0 : static void *bpf_ctx_init(const union bpf_attr *kattr, u32 max_size)
129 : : {
130 : 0 : void __user *data_in = u64_to_user_ptr(kattr->test.ctx_in);
131 : 0 : void __user *data_out = u64_to_user_ptr(kattr->test.ctx_out);
132 : 0 : u32 size = kattr->test.ctx_size_in;
133 : : void *data;
134 : : int err;
135 : :
136 [ # # ]: 0 : if (!data_in && !data_out)
137 : : return NULL;
138 : :
139 : 0 : data = kzalloc(max_size, GFP_USER);
140 [ # # ]: 0 : if (!data)
141 : : return ERR_PTR(-ENOMEM);
142 : :
143 [ # # ]: 0 : if (data_in) {
144 : 0 : err = bpf_check_uarg_tail_zero(data_in, max_size, size);
145 [ # # ]: 0 : if (err) {
146 : 0 : kfree(data);
147 : 0 : return ERR_PTR(err);
148 : : }
149 : :
150 : 0 : size = min_t(u32, max_size, size);
151 [ # # ]: 0 : if (copy_from_user(data, data_in, size)) {
152 : 0 : kfree(data);
153 : 0 : return ERR_PTR(-EFAULT);
154 : : }
155 : : }
156 : 0 : return data;
157 : : }
158 : :
159 : 0 : static int bpf_ctx_finish(const union bpf_attr *kattr,
160 : : union bpf_attr __user *uattr, const void *data,
161 : : u32 size)
162 : : {
163 : 0 : void __user *data_out = u64_to_user_ptr(kattr->test.ctx_out);
164 : : int err = -EFAULT;
165 : 0 : u32 copy_size = size;
166 : :
167 [ # # ]: 0 : if (!data || !data_out)
168 : : return 0;
169 : :
170 [ # # ]: 0 : if (copy_size > kattr->test.ctx_size_out) {
171 : : copy_size = kattr->test.ctx_size_out;
172 : : err = -ENOSPC;
173 : : }
174 : :
175 [ # # ]: 0 : if (copy_to_user(data_out, data, copy_size))
176 : : goto out;
177 [ # # ]: 0 : if (copy_to_user(&uattr->test.ctx_size_out, &size, sizeof(size)))
178 : : goto out;
179 [ # # ]: 0 : if (err != -ENOSPC)
180 : : err = 0;
181 : : out:
182 : 0 : return err;
183 : : }
184 : :
185 : : /**
186 : : * range_is_zero - test whether buffer is initialized
187 : : * @buf: buffer to check
188 : : * @from: check from this position
189 : : * @to: check up until (excluding) this position
190 : : *
191 : : * This function returns true if the there is a non-zero byte
192 : : * in the buf in the range [from,to).
193 : : */
194 : : static inline bool range_is_zero(void *buf, size_t from, size_t to)
195 : : {
196 : 0 : return !memchr_inv((u8 *)buf + from, 0, to - from);
197 : : }
198 : :
199 : 0 : static int convert___skb_to_skb(struct sk_buff *skb, struct __sk_buff *__skb)
200 : : {
201 : : struct qdisc_skb_cb *cb = (struct qdisc_skb_cb *)skb->cb;
202 : :
203 [ # # ]: 0 : if (!__skb)
204 : : return 0;
205 : :
206 : : /* make sure the fields we don't use are zeroed */
207 [ # # ]: 0 : if (!range_is_zero(__skb, 0, offsetof(struct __sk_buff, priority)))
208 : : return -EINVAL;
209 : :
210 : : /* priority is allowed */
211 : :
212 [ # # ]: 0 : if (!range_is_zero(__skb, offsetof(struct __sk_buff, priority) +
213 : : FIELD_SIZEOF(struct __sk_buff, priority),
214 : : offsetof(struct __sk_buff, cb)))
215 : : return -EINVAL;
216 : :
217 : : /* cb is allowed */
218 : :
219 [ # # ]: 0 : if (!range_is_zero(__skb, offsetof(struct __sk_buff, cb) +
220 : : FIELD_SIZEOF(struct __sk_buff, cb),
221 : : sizeof(struct __sk_buff)))
222 : : return -EINVAL;
223 : :
224 : 0 : skb->priority = __skb->priority;
225 : 0 : memcpy(&cb->data, __skb->cb, QDISC_CB_PRIV_LEN);
226 : :
227 : 0 : return 0;
228 : : }
229 : :
230 : : static void convert_skb_to___skb(struct sk_buff *skb, struct __sk_buff *__skb)
231 : : {
232 : : struct qdisc_skb_cb *cb = (struct qdisc_skb_cb *)skb->cb;
233 : :
234 [ # # ]: 0 : if (!__skb)
235 : : return;
236 : :
237 : 0 : __skb->priority = skb->priority;
238 : 0 : memcpy(__skb->cb, &cb->data, QDISC_CB_PRIV_LEN);
239 : : }
240 : :
241 : 0 : int bpf_prog_test_run_skb(struct bpf_prog *prog, const union bpf_attr *kattr,
242 : : union bpf_attr __user *uattr)
243 : : {
244 : : bool is_l2 = false, is_direct_pkt_access = false;
245 : 0 : u32 size = kattr->test.data_size_in;
246 : 0 : u32 repeat = kattr->test.repeat;
247 : : struct __sk_buff *ctx = NULL;
248 : : u32 retval, duration;
249 : : int hh_len = ETH_HLEN;
250 : : struct sk_buff *skb;
251 : : struct sock *sk;
252 : : void *data;
253 : : int ret;
254 : :
255 : 0 : data = bpf_test_init(kattr, size, NET_SKB_PAD + NET_IP_ALIGN,
256 : : SKB_DATA_ALIGN(sizeof(struct skb_shared_info)));
257 [ # # ]: 0 : if (IS_ERR(data))
258 : 0 : return PTR_ERR(data);
259 : :
260 : 0 : ctx = bpf_ctx_init(kattr, sizeof(struct __sk_buff));
261 [ # # ]: 0 : if (IS_ERR(ctx)) {
262 : 0 : kfree(data);
263 : 0 : return PTR_ERR(ctx);
264 : : }
265 : :
266 [ # # # ]: 0 : switch (prog->type) {
267 : : case BPF_PROG_TYPE_SCHED_CLS:
268 : : case BPF_PROG_TYPE_SCHED_ACT:
269 : : is_l2 = true;
270 : : /* fall through */
271 : : case BPF_PROG_TYPE_LWT_IN:
272 : : case BPF_PROG_TYPE_LWT_OUT:
273 : : case BPF_PROG_TYPE_LWT_XMIT:
274 : : is_direct_pkt_access = true;
275 : : break;
276 : : default:
277 : : break;
278 : : }
279 : :
280 : 0 : sk = kzalloc(sizeof(struct sock), GFP_USER);
281 [ # # ]: 0 : if (!sk) {
282 : 0 : kfree(data);
283 : 0 : kfree(ctx);
284 : 0 : return -ENOMEM;
285 : : }
286 : 0 : sock_net_set(sk, current->nsproxy->net_ns);
287 : 0 : sock_init_data(NULL, sk);
288 : :
289 : 0 : skb = build_skb(data, 0);
290 [ # # ]: 0 : if (!skb) {
291 : 0 : kfree(data);
292 : 0 : kfree(ctx);
293 : 0 : kfree(sk);
294 : 0 : return -ENOMEM;
295 : : }
296 : 0 : skb->sk = sk;
297 : :
298 : : skb_reserve(skb, NET_SKB_PAD + NET_IP_ALIGN);
299 : 0 : __skb_put(skb, size);
300 : 0 : skb->protocol = eth_type_trans(skb, current->nsproxy->net_ns->loopback_dev);
301 : : skb_reset_network_header(skb);
302 : :
303 [ # # ]: 0 : if (is_l2)
304 : : __skb_push(skb, hh_len);
305 [ # # ]: 0 : if (is_direct_pkt_access)
306 : : bpf_compute_data_pointers(skb);
307 : 0 : ret = convert___skb_to_skb(skb, ctx);
308 [ # # ]: 0 : if (ret)
309 : : goto out;
310 : 0 : ret = bpf_test_run(prog, skb, repeat, &retval, &duration);
311 [ # # ]: 0 : if (ret)
312 : : goto out;
313 [ # # ]: 0 : if (!is_l2) {
314 [ # # ]: 0 : if (skb_headroom(skb) < hh_len) {
315 : 0 : int nhead = HH_DATA_ALIGN(hh_len - skb_headroom(skb));
316 : :
317 [ # # ]: 0 : if (pskb_expand_head(skb, nhead, 0, GFP_USER)) {
318 : : ret = -ENOMEM;
319 : : goto out;
320 : : }
321 : : }
322 : 0 : memset(__skb_push(skb, hh_len), 0, hh_len);
323 : : }
324 : : convert_skb_to___skb(skb, ctx);
325 : :
326 : 0 : size = skb->len;
327 : : /* bpf program can never convert linear skb to non-linear */
328 [ # # # # : 0 : if (WARN_ON_ONCE(skb_is_nonlinear(skb)))
# # ]
329 : : size = skb_headlen(skb);
330 : 0 : ret = bpf_test_finish(kattr, uattr, skb->data, size, retval, duration);
331 [ # # ]: 0 : if (!ret)
332 : 0 : ret = bpf_ctx_finish(kattr, uattr, ctx,
333 : : sizeof(struct __sk_buff));
334 : : out:
335 : 0 : kfree_skb(skb);
336 : 0 : bpf_sk_storage_free(sk);
337 : 0 : kfree(sk);
338 : 0 : kfree(ctx);
339 : 0 : return ret;
340 : : }
341 : :
342 : 0 : int bpf_prog_test_run_xdp(struct bpf_prog *prog, const union bpf_attr *kattr,
343 : : union bpf_attr __user *uattr)
344 : : {
345 : 0 : u32 size = kattr->test.data_size_in;
346 : 0 : u32 repeat = kattr->test.repeat;
347 : : struct netdev_rx_queue *rxqueue;
348 : 0 : struct xdp_buff xdp = {};
349 : : u32 retval, duration;
350 : : void *data;
351 : : int ret;
352 : :
353 [ # # # # ]: 0 : if (kattr->test.ctx_in || kattr->test.ctx_out)
354 : : return -EINVAL;
355 : :
356 : 0 : data = bpf_test_init(kattr, size, XDP_PACKET_HEADROOM + NET_IP_ALIGN, 0);
357 [ # # ]: 0 : if (IS_ERR(data))
358 : 0 : return PTR_ERR(data);
359 : :
360 : 0 : xdp.data_hard_start = data;
361 : 0 : xdp.data = data + XDP_PACKET_HEADROOM + NET_IP_ALIGN;
362 : 0 : xdp.data_meta = xdp.data;
363 : 0 : xdp.data_end = xdp.data + size;
364 : :
365 : 0 : rxqueue = __netif_get_rx_queue(current->nsproxy->net_ns->loopback_dev, 0);
366 : 0 : xdp.rxq = &rxqueue->xdp_rxq;
367 : :
368 : 0 : ret = bpf_test_run(prog, &xdp, repeat, &retval, &duration);
369 [ # # ]: 0 : if (ret)
370 : : goto out;
371 [ # # # # ]: 0 : if (xdp.data != data + XDP_PACKET_HEADROOM + NET_IP_ALIGN ||
372 : 0 : xdp.data_end != xdp.data + size)
373 : 0 : size = xdp.data_end - xdp.data;
374 : 0 : ret = bpf_test_finish(kattr, uattr, xdp.data, size, retval, duration);
375 : : out:
376 : 0 : kfree(data);
377 : 0 : return ret;
378 : : }
379 : :
380 : 0 : static int verify_user_bpf_flow_keys(struct bpf_flow_keys *ctx)
381 : : {
382 : : /* make sure the fields we don't use are zeroed */
383 [ # # ]: 0 : if (!range_is_zero(ctx, 0, offsetof(struct bpf_flow_keys, flags)))
384 : : return -EINVAL;
385 : :
386 : : /* flags is allowed */
387 : :
388 [ # # ]: 0 : if (!range_is_zero(ctx, offsetof(struct bpf_flow_keys, flags) +
389 : : FIELD_SIZEOF(struct bpf_flow_keys, flags),
390 : : sizeof(struct bpf_flow_keys)))
391 : : return -EINVAL;
392 : :
393 : 0 : return 0;
394 : : }
395 : :
396 : 0 : int bpf_prog_test_run_flow_dissector(struct bpf_prog *prog,
397 : : const union bpf_attr *kattr,
398 : : union bpf_attr __user *uattr)
399 : : {
400 : 0 : u32 size = kattr->test.data_size_in;
401 : 0 : struct bpf_flow_dissector ctx = {};
402 : 0 : u32 repeat = kattr->test.repeat;
403 : : struct bpf_flow_keys *user_ctx;
404 : : struct bpf_flow_keys flow_keys;
405 : 0 : u64 time_start, time_spent = 0;
406 : : const struct ethhdr *eth;
407 : : unsigned int flags = 0;
408 : : u32 retval, duration;
409 : : void *data;
410 : : int ret;
411 : : u32 i;
412 : :
413 [ # # ]: 0 : if (prog->type != BPF_PROG_TYPE_FLOW_DISSECTOR)
414 : : return -EINVAL;
415 : :
416 [ # # ]: 0 : if (size < ETH_HLEN)
417 : : return -EINVAL;
418 : :
419 : 0 : data = bpf_test_init(kattr, size, 0, 0);
420 [ # # ]: 0 : if (IS_ERR(data))
421 : 0 : return PTR_ERR(data);
422 : :
423 : : eth = (struct ethhdr *)data;
424 : :
425 [ # # ]: 0 : if (!repeat)
426 : : repeat = 1;
427 : :
428 : 0 : user_ctx = bpf_ctx_init(kattr, sizeof(struct bpf_flow_keys));
429 [ # # ]: 0 : if (IS_ERR(user_ctx)) {
430 : 0 : kfree(data);
431 : 0 : return PTR_ERR(user_ctx);
432 : : }
433 [ # # ]: 0 : if (user_ctx) {
434 : 0 : ret = verify_user_bpf_flow_keys(user_ctx);
435 [ # # ]: 0 : if (ret)
436 : : goto out;
437 : 0 : flags = user_ctx->flags;
438 : : }
439 : :
440 : 0 : ctx.flow_keys = &flow_keys;
441 : 0 : ctx.data = data;
442 : 0 : ctx.data_end = (__u8 *)data + size;
443 : :
444 : : rcu_read_lock();
445 : 0 : preempt_disable();
446 : : time_start = ktime_get_ns();
447 [ # # ]: 0 : for (i = 0; i < repeat; i++) {
448 : 0 : retval = bpf_flow_dissect(prog, &ctx, eth->h_proto, ETH_HLEN,
449 : : size, flags);
450 : :
451 [ # # ]: 0 : if (signal_pending(current)) {
452 : 0 : preempt_enable();
453 : : rcu_read_unlock();
454 : :
455 : : ret = -EINTR;
456 : 0 : goto out;
457 : : }
458 : :
459 [ # # ]: 0 : if (need_resched()) {
460 : 0 : time_spent += ktime_get_ns() - time_start;
461 : 0 : preempt_enable();
462 : : rcu_read_unlock();
463 : :
464 : 0 : cond_resched();
465 : :
466 : : rcu_read_lock();
467 : 0 : preempt_disable();
468 : : time_start = ktime_get_ns();
469 : : }
470 : : }
471 : 0 : time_spent += ktime_get_ns() - time_start;
472 : 0 : preempt_enable();
473 : : rcu_read_unlock();
474 : :
475 [ # # # # : 0 : do_div(time_spent, repeat);
# # # # #
# # # # #
# # # # #
# # # # #
# # # # #
# # # # #
# # # # #
# # # # #
# # # # #
# # # # #
# # # # #
# # # # #
# # # # #
# # # # #
# # # # #
# # # # #
# # # # #
# # # # #
# # # # #
# # # # #
# # # # #
# # # # #
# # # # #
# # # # #
# # # # #
# # # # #
# # # # #
# # # # #
# # # # #
# # # # #
# # # # #
# # # # #
# # # # #
# # # # #
# # # # #
# # # # #
# # # # #
# # # # #
# # # # #
# # # # #
# # # # #
# # # # #
# # # # #
# # # # #
# # # # #
# # # # #
# # # # #
# # # # #
# # # # #
# # # # #
# # # # #
# # # # #
# # # # #
# # # # #
# # # # #
# # # # #
# # # # #
# # # # #
# # # # #
# # # # #
# # # # #
# # # # #
# # # # #
# # # # #
# # # # #
# # # # #
# # # # #
# # # # #
# # # # #
# # # # #
# # # # #
# # # # #
# # # # #
# # # # #
# # # # #
# # # # #
# # # # #
# # # # #
# # # # #
# # # # #
# # ]
476 [ # # ]: 0 : duration = time_spent > U32_MAX ? U32_MAX : (u32)time_spent;
477 : :
478 : 0 : ret = bpf_test_finish(kattr, uattr, &flow_keys, sizeof(flow_keys),
479 : : retval, duration);
480 [ # # ]: 0 : if (!ret)
481 : 0 : ret = bpf_ctx_finish(kattr, uattr, user_ctx,
482 : : sizeof(struct bpf_flow_keys));
483 : :
484 : : out:
485 : 0 : kfree(user_ctx);
486 : 0 : kfree(data);
487 : 0 : return ret;
488 : : }
|