On Fri, May 2, 2025 at 12:06 PM Mykyta Yatsenko <mykyta.yatsenko5@xxxxxxxxx> wrote: > > From: Mykyta Yatsenko <yatsenko@xxxxxxxx> > > Introduce selftests verifying newly-added dynptr copy kfuncs. > Covering contiguous and non-contiguous memory backed dynptrs. > > Disable test_probe_read_user_str_dynptr that triggers bug in > strncpy_from_user_nofault. Patch to fix the issue [1]. > > [1] https://patchwork.kernel.org/project/linux-mm/patch/20250422131449.57177-1-mykyta.yatsenko5@xxxxxxxxx/ > > Acked-by: Andrii Nakryiko <andrii@xxxxxxxxxx> > Signed-off-by: Mykyta Yatsenko <yatsenko@xxxxxxxx> > --- > tools/testing/selftests/bpf/DENYLIST | 1 + > .../testing/selftests/bpf/prog_tests/dynptr.c | 13 ++ > .../selftests/bpf/progs/dynptr_success.c | 201 ++++++++++++++++++ > 3 files changed, 215 insertions(+) > > diff --git a/tools/testing/selftests/bpf/DENYLIST b/tools/testing/selftests/bpf/DENYLIST > index f748f2c33b22..1789a61d0a9b 100644 > --- a/tools/testing/selftests/bpf/DENYLIST > +++ b/tools/testing/selftests/bpf/DENYLIST > @@ -1,5 +1,6 @@ > # TEMPORARY > # Alphabetical order > +dynptr/test_probe_read_user_str_dynptr # disabled until https://patchwork.kernel.org/project/linux-mm/patch/20250422131449.57177-1-mykyta.yatsenko5@xxxxxxxxx/ makes it into the bpf-next > get_stack_raw_tp # spams with kernel warnings until next bpf -> bpf-next merge > stacktrace_build_id > stacktrace_build_id_nmi > diff --git a/tools/testing/selftests/bpf/prog_tests/dynptr.c b/tools/testing/selftests/bpf/prog_tests/dynptr.c > index e29cc16124c2..62e7ec775f24 100644 > --- a/tools/testing/selftests/bpf/prog_tests/dynptr.c > +++ b/tools/testing/selftests/bpf/prog_tests/dynptr.c > @@ -33,10 +33,19 @@ static struct { > {"test_dynptr_skb_no_buff", SETUP_SKB_PROG}, > {"test_dynptr_skb_strcmp", SETUP_SKB_PROG}, > {"test_dynptr_skb_tp_btf", SETUP_SKB_PROG_TP}, > + {"test_probe_read_user_dynptr", SETUP_XDP_PROG}, > + {"test_probe_read_kernel_dynptr", SETUP_XDP_PROG}, > + {"test_probe_read_user_str_dynptr", SETUP_XDP_PROG}, > + {"test_probe_read_kernel_str_dynptr", SETUP_XDP_PROG}, > + {"test_copy_from_user_dynptr", SETUP_SYSCALL_SLEEP}, > + {"test_copy_from_user_str_dynptr", SETUP_SYSCALL_SLEEP}, > + {"test_copy_from_user_task_dynptr", SETUP_SYSCALL_SLEEP}, > + {"test_copy_from_user_task_str_dynptr", SETUP_SYSCALL_SLEEP}, > }; > > static void verify_success(const char *prog_name, enum test_setup_type setup_type) > { > + char user_data[384] = {[0 ... 382] = 'a', '\0'}; > struct dynptr_success *skel; > struct bpf_program *prog; > struct bpf_link *link; > @@ -58,6 +67,10 @@ static void verify_success(const char *prog_name, enum test_setup_type setup_typ > if (!ASSERT_OK(err, "dynptr_success__load")) > goto cleanup; > > + skel->bss->user_ptr = user_data; > + skel->data->test_len[0] = sizeof(user_data); > + memcpy(skel->bss->expected_str, user_data, sizeof(user_data)); > + > switch (setup_type) { > case SETUP_SYSCALL_SLEEP: > link = bpf_program__attach(prog); > diff --git a/tools/testing/selftests/bpf/progs/dynptr_success.c b/tools/testing/selftests/bpf/progs/dynptr_success.c > index e1fba28e4a86..753d35eb47d9 100644 > --- a/tools/testing/selftests/bpf/progs/dynptr_success.c > +++ b/tools/testing/selftests/bpf/progs/dynptr_success.c > @@ -680,3 +680,204 @@ int test_dynptr_copy_xdp(struct xdp_md *xdp) > bpf_ringbuf_discard_dynptr(&ptr_buf, 0); > return XDP_DROP; > } > + > +void *user_ptr; > +char expected_str[384]; /* Contains the copy of the data pointed by user_ptr */ what so magic about 384? > +__u32 test_len[7] = {0/* placeholder */, 0, 1, 2, 255, 256, 257}; > + > +typedef int (*bpf_read_dynptr_fn_t)(struct bpf_dynptr *dptr, u32 off, > + u32 size, const void *unsafe_ptr); > + > +static __always_inline void test_dynptr_probe(void *ptr, bpf_read_dynptr_fn_t bpf_read_dynptr_fn) More __always_inline in the prog too? Why? > +{ > + char buf[sizeof(expected_str)]; > + struct bpf_dynptr ptr_buf; > + int i; > + > + err = bpf_ringbuf_reserve_dynptr(&ringbuf, sizeof(buf), 0, &ptr_buf); > + > + bpf_for(i, 0, ARRAY_SIZE(test_len)) { > + __u32 len = test_len[i]; > + > + err = err ?: bpf_read_dynptr_fn(&ptr_buf, 0, test_len[i], ptr); > + if (len > sizeof(buf)) > + break; > + err = err ?: bpf_dynptr_read(&buf, len, &ptr_buf, 0, 0); > + > + if (err || bpf_memcmp(expected_str, buf, len)) > + err = 1; > + > + /* Reset buffer and dynptr */ > + __builtin_memset(buf, 0, sizeof(buf)); > + err = err ?: bpf_dynptr_write(&ptr_buf, 0, buf, len, 0); > + } > + bpf_ringbuf_discard_dynptr(&ptr_buf, 0); > +} > + > +static __always_inline void test_dynptr_probe_str(void *ptr, > + bpf_read_dynptr_fn_t bpf_read_dynptr_fn) > +{ > + char buf[sizeof(expected_str)]; > + struct bpf_dynptr ptr_buf; > + __u32 cnt, i; > + > + bpf_ringbuf_reserve_dynptr(&ringbuf, sizeof(buf), 0, &ptr_buf); > + > + bpf_for(i, 0, ARRAY_SIZE(test_len)) { > + __u32 len = test_len[i]; > + > + cnt = bpf_read_dynptr_fn(&ptr_buf, 0, len, ptr); > + if (cnt != len) > + err = 1; > + > + if (len > sizeof(buf)) > + continue; > + err = err ?: bpf_dynptr_read(&buf, len, &ptr_buf, 0, 0); > + if (!len) > + continue; > + if (err || bpf_memcmp(expected_str, buf, len - 1) || buf[len - 1] != '\0') > + err = 1; > + } > + bpf_ringbuf_discard_dynptr(&ptr_buf, 0); > +} > + > +static __always_inline void test_dynptr_probe_xdp(struct xdp_md *xdp, void *ptr, > + bpf_read_dynptr_fn_t bpf_read_dynptr_fn) > +{ > + struct bpf_dynptr ptr_xdp; > + char buf[sizeof(expected_str)]; > + __u32 off, i; > + > + /* Set offset near the seam between buffers */ > + off = 3500; what is 3500 ?