diff options
Diffstat (limited to 'tools/testing/selftests/bpf/progs/dynptr_success.c')
| -rw-r--r-- | tools/testing/selftests/bpf/progs/dynptr_success.c | 337 | 
1 files changed, 337 insertions, 0 deletions
diff --git a/tools/testing/selftests/bpf/progs/dynptr_success.c b/tools/testing/selftests/bpf/progs/dynptr_success.c index b2fa6c47ecc0..5985920d162e 100644 --- a/tools/testing/selftests/bpf/progs/dynptr_success.c +++ b/tools/testing/selftests/bpf/progs/dynptr_success.c @@ -2,6 +2,7 @@  /* Copyright (c) 2022 Facebook */  #include <string.h> +#include <stdbool.h>  #include <linux/bpf.h>  #include <bpf/bpf_helpers.h>  #include "bpf_misc.h" @@ -207,3 +208,339 @@ int test_dynptr_skb_data(struct __sk_buff *skb)  	return 1;  } + +SEC("tp/syscalls/sys_enter_nanosleep") +int test_adjust(void *ctx) +{ +	struct bpf_dynptr ptr; +	__u32 bytes = 64; +	__u32 off = 10; +	__u32 trim = 15; + +	if (bpf_get_current_pid_tgid() >> 32 != pid) +		return 0; + +	err = bpf_ringbuf_reserve_dynptr(&ringbuf, bytes, 0, &ptr); +	if (err) { +		err = 1; +		goto done; +	} + +	if (bpf_dynptr_size(&ptr) != bytes) { +		err = 2; +		goto done; +	} + +	/* Advance the dynptr by off */ +	err = bpf_dynptr_adjust(&ptr, off, bpf_dynptr_size(&ptr)); +	if (err) { +		err = 3; +		goto done; +	} + +	if (bpf_dynptr_size(&ptr) != bytes - off) { +		err = 4; +		goto done; +	} + +	/* Trim the dynptr */ +	err = bpf_dynptr_adjust(&ptr, off, 15); +	if (err) { +		err = 5; +		goto done; +	} + +	/* Check that the size was adjusted correctly */ +	if (bpf_dynptr_size(&ptr) != trim - off) { +		err = 6; +		goto done; +	} + +done: +	bpf_ringbuf_discard_dynptr(&ptr, 0); +	return 0; +} + +SEC("tp/syscalls/sys_enter_nanosleep") +int test_adjust_err(void *ctx) +{ +	char write_data[45] = "hello there, world!!"; +	struct bpf_dynptr ptr; +	__u32 size = 64; +	__u32 off = 20; + +	if (bpf_get_current_pid_tgid() >> 32 != pid) +		return 0; + +	if (bpf_ringbuf_reserve_dynptr(&ringbuf, size, 0, &ptr)) { +		err = 1; +		goto done; +	} + +	/* Check that start can't be greater than end */ +	if (bpf_dynptr_adjust(&ptr, 5, 1) != -EINVAL) { +		err = 2; +		goto done; +	} + +	/* Check that start can't be greater than size */ +	if (bpf_dynptr_adjust(&ptr, size + 1, size + 1) != -ERANGE) { +		err = 3; +		goto done; +	} + +	/* Check that end can't be greater than size */ +	if (bpf_dynptr_adjust(&ptr, 0, size + 1) != -ERANGE) { +		err = 4; +		goto done; +	} + +	if (bpf_dynptr_adjust(&ptr, off, size)) { +		err = 5; +		goto done; +	} + +	/* Check that you can't write more bytes than available into the dynptr +	 * after you've adjusted it +	 */ +	if (bpf_dynptr_write(&ptr, 0, &write_data, sizeof(write_data), 0) != -E2BIG) { +		err = 6; +		goto done; +	} + +	/* Check that even after adjusting, submitting/discarding +	 * a ringbuf dynptr works +	 */ +	bpf_ringbuf_submit_dynptr(&ptr, 0); +	return 0; + +done: +	bpf_ringbuf_discard_dynptr(&ptr, 0); +	return 0; +} + +SEC("tp/syscalls/sys_enter_nanosleep") +int test_zero_size_dynptr(void *ctx) +{ +	char write_data = 'x', read_data; +	struct bpf_dynptr ptr; +	__u32 size = 64; + +	if (bpf_get_current_pid_tgid() >> 32 != pid) +		return 0; + +	if (bpf_ringbuf_reserve_dynptr(&ringbuf, size, 0, &ptr)) { +		err = 1; +		goto done; +	} + +	/* After this, the dynptr has a size of 0 */ +	if (bpf_dynptr_adjust(&ptr, size, size)) { +		err = 2; +		goto done; +	} + +	/* Test that reading + writing non-zero bytes is not ok */ +	if (bpf_dynptr_read(&read_data, sizeof(read_data), &ptr, 0, 0) != -E2BIG) { +		err = 3; +		goto done; +	} + +	if (bpf_dynptr_write(&ptr, 0, &write_data, sizeof(write_data), 0) != -E2BIG) { +		err = 4; +		goto done; +	} + +	/* Test that reading + writing 0 bytes from a 0-size dynptr is ok */ +	if (bpf_dynptr_read(&read_data, 0, &ptr, 0, 0)) { +		err = 5; +		goto done; +	} + +	if (bpf_dynptr_write(&ptr, 0, &write_data, 0, 0)) { +		err = 6; +		goto done; +	} + +	err = 0; + +done: +	bpf_ringbuf_discard_dynptr(&ptr, 0); +	return 0; +} + +SEC("tp/syscalls/sys_enter_nanosleep") +int test_dynptr_is_null(void *ctx) +{ +	struct bpf_dynptr ptr1; +	struct bpf_dynptr ptr2; +	__u64 size = 4; + +	if (bpf_get_current_pid_tgid() >> 32 != pid) +		return 0; + +	/* Pass in invalid flags, get back an invalid dynptr */ +	if (bpf_ringbuf_reserve_dynptr(&ringbuf, size, 123, &ptr1) != -EINVAL) { +		err = 1; +		goto exit_early; +	} + +	/* Test that the invalid dynptr is null */ +	if (!bpf_dynptr_is_null(&ptr1)) { +		err = 2; +		goto exit_early; +	} + +	/* Get a valid dynptr */ +	if (bpf_ringbuf_reserve_dynptr(&ringbuf, size, 0, &ptr2)) { +		err = 3; +		goto exit; +	} + +	/* Test that the valid dynptr is not null */ +	if (bpf_dynptr_is_null(&ptr2)) { +		err = 4; +		goto exit; +	} + +exit: +	bpf_ringbuf_discard_dynptr(&ptr2, 0); +exit_early: +	bpf_ringbuf_discard_dynptr(&ptr1, 0); +	return 0; +} + +SEC("cgroup_skb/egress") +int test_dynptr_is_rdonly(struct __sk_buff *skb) +{ +	struct bpf_dynptr ptr1; +	struct bpf_dynptr ptr2; +	struct bpf_dynptr ptr3; + +	/* Pass in invalid flags, get back an invalid dynptr */ +	if (bpf_dynptr_from_skb(skb, 123, &ptr1) != -EINVAL) { +		err = 1; +		return 0; +	} + +	/* Test that an invalid dynptr is_rdonly returns false */ +	if (bpf_dynptr_is_rdonly(&ptr1)) { +		err = 2; +		return 0; +	} + +	/* Get a read-only dynptr */ +	if (bpf_dynptr_from_skb(skb, 0, &ptr2)) { +		err = 3; +		return 0; +	} + +	/* Test that the dynptr is read-only */ +	if (!bpf_dynptr_is_rdonly(&ptr2)) { +		err = 4; +		return 0; +	} + +	/* Get a read-writeable dynptr */ +	if (bpf_ringbuf_reserve_dynptr(&ringbuf, 64, 0, &ptr3)) { +		err = 5; +		goto done; +	} + +	/* Test that the dynptr is read-only */ +	if (bpf_dynptr_is_rdonly(&ptr3)) { +		err = 6; +		goto done; +	} + +done: +	bpf_ringbuf_discard_dynptr(&ptr3, 0); +	return 0; +} + +SEC("cgroup_skb/egress") +int test_dynptr_clone(struct __sk_buff *skb) +{ +	struct bpf_dynptr ptr1; +	struct bpf_dynptr ptr2; +	__u32 off = 2, size; + +	/* Get a dynptr */ +	if (bpf_dynptr_from_skb(skb, 0, &ptr1)) { +		err = 1; +		return 0; +	} + +	if (bpf_dynptr_adjust(&ptr1, off, bpf_dynptr_size(&ptr1))) { +		err = 2; +		return 0; +	} + +	/* Clone the dynptr */ +	if (bpf_dynptr_clone(&ptr1, &ptr2)) { +		err = 3; +		return 0; +	} + +	size = bpf_dynptr_size(&ptr1); + +	/* Check that the clone has the same size and rd-only */ +	if (bpf_dynptr_size(&ptr2) != size) { +		err = 4; +		return 0; +	} + +	if (bpf_dynptr_is_rdonly(&ptr2) != bpf_dynptr_is_rdonly(&ptr1)) { +		err = 5; +		return 0; +	} + +	/* Advance and trim the original dynptr */ +	bpf_dynptr_adjust(&ptr1, 5, 5); + +	/* Check that only original dynptr was affected, and the clone wasn't */ +	if (bpf_dynptr_size(&ptr2) != size) { +		err = 6; +		return 0; +	} + +	return 0; +} + +SEC("?cgroup_skb/egress") +int test_dynptr_skb_no_buff(struct __sk_buff *skb) +{ +	struct bpf_dynptr ptr; +	__u64 *data; + +	if (bpf_dynptr_from_skb(skb, 0, &ptr)) { +		err = 1; +		return 1; +	} + +	/* This may return NULL. SKB may require a buffer */ +	data = bpf_dynptr_slice(&ptr, 0, NULL, 1); + +	return !!data; +} + +SEC("?cgroup_skb/egress") +int test_dynptr_skb_strcmp(struct __sk_buff *skb) +{ +	struct bpf_dynptr ptr; +	char *data; + +	if (bpf_dynptr_from_skb(skb, 0, &ptr)) { +		err = 1; +		return 1; +	} + +	/* This may return NULL. SKB may require a buffer */ +	data = bpf_dynptr_slice(&ptr, 0, NULL, 10); +	if (data) { +		bpf_strncmp(data, 10, "foo"); +		return 1; +	} + +	return 1; +}  |