1 // SPDX-License-Identifier: GPL-2.0
2 /* Copyright (c) 2022 Facebook */
7 #include <bpf/bpf_helpers.h>
9 #include "bpf_kfuncs.h"
12 char _license[] SEC("license") = "GPL";
24 __uint(type, BPF_MAP_TYPE_RINGBUF);
25 __uint(max_entries, 4096);
26 } ringbuf SEC(".maps");
29 __uint(type, BPF_MAP_TYPE_ARRAY);
30 __uint(max_entries, 1);
33 } array_map SEC(".maps");
35 SEC("?tp/syscalls/sys_enter_nanosleep")
36 int test_read_write(void *ctx)
38 char write_data[64] = "hello there, world!!";
39 char read_data[64] = {};
40 struct bpf_dynptr ptr;
43 if (bpf_get_current_pid_tgid() >> 32 != pid)
46 bpf_ringbuf_reserve_dynptr(&ringbuf, sizeof(write_data), 0, &ptr);
48 /* Write data into the dynptr */
49 err = bpf_dynptr_write(&ptr, 0, write_data, sizeof(write_data), 0);
51 /* Read the data that was written into the dynptr */
52 err = err ?: bpf_dynptr_read(read_data, sizeof(read_data), &ptr, 0, 0);
54 /* Ensure the data we read matches the data we wrote */
55 for (i = 0; i < sizeof(read_data); i++) {
56 if (read_data[i] != write_data[i]) {
62 bpf_ringbuf_discard_dynptr(&ptr, 0);
66 SEC("?tp/syscalls/sys_enter_nanosleep")
67 int test_dynptr_data(void *ctx)
69 __u32 key = 0, val = 235, *map_val;
70 struct bpf_dynptr ptr;
74 map_val_size = sizeof(*map_val);
76 if (bpf_get_current_pid_tgid() >> 32 != pid)
79 bpf_map_update_elem(&array_map, &key, &val, 0);
81 map_val = bpf_map_lookup_elem(&array_map, &key);
87 bpf_dynptr_from_mem(map_val, map_val_size, 0, &ptr);
89 /* Try getting a data slice that is out of range */
90 data = bpf_dynptr_data(&ptr, map_val_size + 1, 1);
96 /* Try getting more bytes than available */
97 data = bpf_dynptr_data(&ptr, 0, map_val_size + 1);
103 data = bpf_dynptr_data(&ptr, 0, sizeof(__u32));
109 *(__u32 *)data = 999;
111 err = bpf_probe_read_kernel(&val, sizeof(val), data);
115 if (val != *(int *)data)
121 static int ringbuf_callback(__u32 index, void *data)
123 struct sample *sample;
125 struct bpf_dynptr *ptr = (struct bpf_dynptr *)data;
127 sample = bpf_dynptr_data(ptr, 0, sizeof(*sample));
131 sample->pid += index;
136 SEC("?tp/syscalls/sys_enter_nanosleep")
137 int test_ringbuf(void *ctx)
139 struct bpf_dynptr ptr;
140 struct sample *sample;
142 if (bpf_get_current_pid_tgid() >> 32 != pid)
147 /* check that you can reserve a dynamic size reservation */
148 err = bpf_ringbuf_reserve_dynptr(&ringbuf, val, 0, &ptr);
150 sample = err ? NULL : bpf_dynptr_data(&ptr, 0, sizeof(*sample));
158 /* Can pass dynptr to callback functions */
159 bpf_loop(10, ringbuf_callback, &ptr, 0);
161 if (sample->pid != 55)
165 bpf_ringbuf_discard_dynptr(&ptr, 0);
169 SEC("?cgroup_skb/egress")
170 int test_skb_readonly(struct __sk_buff *skb)
172 __u8 write_data[2] = {1, 2};
173 struct bpf_dynptr ptr;
176 if (bpf_dynptr_from_skb(skb, 0, &ptr)) {
181 /* since cgroup skbs are read only, writes should fail */
182 ret = bpf_dynptr_write(&ptr, 0, write_data, sizeof(write_data), 0);
183 if (ret != -EINVAL) {
191 SEC("?cgroup_skb/egress")
192 int test_dynptr_skb_data(struct __sk_buff *skb)
194 struct bpf_dynptr ptr;
197 if (bpf_dynptr_from_skb(skb, 0, &ptr)) {
202 /* This should return NULL. Must use bpf_dynptr_slice API */
203 data = bpf_dynptr_data(&ptr, 0, 1);
212 SEC("tp/syscalls/sys_enter_nanosleep")
213 int test_adjust(void *ctx)
215 struct bpf_dynptr ptr;
220 if (bpf_get_current_pid_tgid() >> 32 != pid)
223 err = bpf_ringbuf_reserve_dynptr(&ringbuf, bytes, 0, &ptr);
229 if (bpf_dynptr_size(&ptr) != bytes) {
234 /* Advance the dynptr by off */
235 err = bpf_dynptr_adjust(&ptr, off, bpf_dynptr_size(&ptr));
241 if (bpf_dynptr_size(&ptr) != bytes - off) {
246 /* Trim the dynptr */
247 err = bpf_dynptr_adjust(&ptr, off, 15);
253 /* Check that the size was adjusted correctly */
254 if (bpf_dynptr_size(&ptr) != trim - off) {
260 bpf_ringbuf_discard_dynptr(&ptr, 0);
264 SEC("tp/syscalls/sys_enter_nanosleep")
265 int test_adjust_err(void *ctx)
267 char write_data[45] = "hello there, world!!";
268 struct bpf_dynptr ptr;
272 if (bpf_get_current_pid_tgid() >> 32 != pid)
275 if (bpf_ringbuf_reserve_dynptr(&ringbuf, size, 0, &ptr)) {
280 /* Check that start can't be greater than end */
281 if (bpf_dynptr_adjust(&ptr, 5, 1) != -EINVAL) {
286 /* Check that start can't be greater than size */
287 if (bpf_dynptr_adjust(&ptr, size + 1, size + 1) != -ERANGE) {
292 /* Check that end can't be greater than size */
293 if (bpf_dynptr_adjust(&ptr, 0, size + 1) != -ERANGE) {
298 if (bpf_dynptr_adjust(&ptr, off, size)) {
303 /* Check that you can't write more bytes than available into the dynptr
304 * after you've adjusted it
306 if (bpf_dynptr_write(&ptr, 0, &write_data, sizeof(write_data), 0) != -E2BIG) {
311 /* Check that even after adjusting, submitting/discarding
312 * a ringbuf dynptr works
314 bpf_ringbuf_submit_dynptr(&ptr, 0);
318 bpf_ringbuf_discard_dynptr(&ptr, 0);
322 SEC("tp/syscalls/sys_enter_nanosleep")
323 int test_zero_size_dynptr(void *ctx)
325 char write_data = 'x', read_data;
326 struct bpf_dynptr ptr;
329 if (bpf_get_current_pid_tgid() >> 32 != pid)
332 if (bpf_ringbuf_reserve_dynptr(&ringbuf, size, 0, &ptr)) {
337 /* After this, the dynptr has a size of 0 */
338 if (bpf_dynptr_adjust(&ptr, size, size)) {
343 /* Test that reading + writing non-zero bytes is not ok */
344 if (bpf_dynptr_read(&read_data, sizeof(read_data), &ptr, 0, 0) != -E2BIG) {
349 if (bpf_dynptr_write(&ptr, 0, &write_data, sizeof(write_data), 0) != -E2BIG) {
354 /* Test that reading + writing 0 bytes from a 0-size dynptr is ok */
355 if (bpf_dynptr_read(&read_data, 0, &ptr, 0, 0)) {
360 if (bpf_dynptr_write(&ptr, 0, &write_data, 0, 0)) {
368 bpf_ringbuf_discard_dynptr(&ptr, 0);
372 SEC("tp/syscalls/sys_enter_nanosleep")
373 int test_dynptr_is_null(void *ctx)
375 struct bpf_dynptr ptr1;
376 struct bpf_dynptr ptr2;
379 if (bpf_get_current_pid_tgid() >> 32 != pid)
382 /* Pass in invalid flags, get back an invalid dynptr */
383 if (bpf_ringbuf_reserve_dynptr(&ringbuf, size, 123, &ptr1) != -EINVAL) {
388 /* Test that the invalid dynptr is null */
389 if (!bpf_dynptr_is_null(&ptr1)) {
394 /* Get a valid dynptr */
395 if (bpf_ringbuf_reserve_dynptr(&ringbuf, size, 0, &ptr2)) {
400 /* Test that the valid dynptr is not null */
401 if (bpf_dynptr_is_null(&ptr2)) {
407 bpf_ringbuf_discard_dynptr(&ptr2, 0);
409 bpf_ringbuf_discard_dynptr(&ptr1, 0);
413 SEC("cgroup_skb/egress")
414 int test_dynptr_is_rdonly(struct __sk_buff *skb)
416 struct bpf_dynptr ptr1;
417 struct bpf_dynptr ptr2;
418 struct bpf_dynptr ptr3;
420 /* Pass in invalid flags, get back an invalid dynptr */
421 if (bpf_dynptr_from_skb(skb, 123, &ptr1) != -EINVAL) {
426 /* Test that an invalid dynptr is_rdonly returns false */
427 if (bpf_dynptr_is_rdonly(&ptr1)) {
432 /* Get a read-only dynptr */
433 if (bpf_dynptr_from_skb(skb, 0, &ptr2)) {
438 /* Test that the dynptr is read-only */
439 if (!bpf_dynptr_is_rdonly(&ptr2)) {
444 /* Get a read-writeable dynptr */
445 if (bpf_ringbuf_reserve_dynptr(&ringbuf, 64, 0, &ptr3)) {
450 /* Test that the dynptr is read-only */
451 if (bpf_dynptr_is_rdonly(&ptr3)) {
457 bpf_ringbuf_discard_dynptr(&ptr3, 0);
461 SEC("cgroup_skb/egress")
462 int test_dynptr_clone(struct __sk_buff *skb)
464 struct bpf_dynptr ptr1;
465 struct bpf_dynptr ptr2;
469 if (bpf_dynptr_from_skb(skb, 0, &ptr1)) {
474 if (bpf_dynptr_adjust(&ptr1, off, bpf_dynptr_size(&ptr1))) {
479 /* Clone the dynptr */
480 if (bpf_dynptr_clone(&ptr1, &ptr2)) {
485 size = bpf_dynptr_size(&ptr1);
487 /* Check that the clone has the same size and rd-only */
488 if (bpf_dynptr_size(&ptr2) != size) {
493 if (bpf_dynptr_is_rdonly(&ptr2) != bpf_dynptr_is_rdonly(&ptr1)) {
498 /* Advance and trim the original dynptr */
499 bpf_dynptr_adjust(&ptr1, 5, 5);
501 /* Check that only original dynptr was affected, and the clone wasn't */
502 if (bpf_dynptr_size(&ptr2) != size) {
510 SEC("?cgroup_skb/egress")
511 int test_dynptr_skb_no_buff(struct __sk_buff *skb)
513 struct bpf_dynptr ptr;
516 if (bpf_dynptr_from_skb(skb, 0, &ptr)) {
521 /* This may return NULL. SKB may require a buffer */
522 data = bpf_dynptr_slice(&ptr, 0, NULL, 1);
527 SEC("?cgroup_skb/egress")
528 int test_dynptr_skb_strcmp(struct __sk_buff *skb)
530 struct bpf_dynptr ptr;
533 if (bpf_dynptr_from_skb(skb, 0, &ptr)) {
538 /* This may return NULL. SKB may require a buffer */
539 data = bpf_dynptr_slice(&ptr, 0, NULL, 10);
541 bpf_strncmp(data, 10, "foo");