On Mon, Jan 6, 2025 at 6:20 PM Jordan Rome <linux@xxxxxxxxxxxxxx> wrote: > > This adds tests for both the happy path and the > error path (with and without the BPF_F_PAD_ZEROS flag). > > Signed-off-by: Jordan Rome <linux@xxxxxxxxxxxxxx> > --- > .../selftests/bpf/prog_tests/bpf_iter.c | 7 +++ > .../selftests/bpf/prog_tests/read_vsyscall.c | 1 + > .../selftests/bpf/progs/bpf_iter_tasks.c | 55 +++++++++++++++++++ > .../selftests/bpf/progs/read_vsyscall.c | 6 +- > 4 files changed, 67 insertions(+), 2 deletions(-) > > diff --git a/tools/testing/selftests/bpf/prog_tests/bpf_iter.c b/tools/testing/selftests/bpf/prog_tests/bpf_iter.c > index 6f1bfacd7375..8ed864793bd1 100644 > --- a/tools/testing/selftests/bpf/prog_tests/bpf_iter.c > +++ b/tools/testing/selftests/bpf/prog_tests/bpf_iter.c > @@ -34,6 +34,8 @@ > #include "bpf_iter_ksym.skel.h" > #include "bpf_iter_sockmap.skel.h" > > +static char test_data[] = "test_data"; > + > static void test_btf_id_or_null(void) > { > struct bpf_iter_test_kern3 *skel; > @@ -328,12 +330,17 @@ static void test_task_sleepable(void) > if (!ASSERT_OK_PTR(skel, "bpf_iter_tasks__open_and_load")) > return; > > + skel->bss->user_ptr = test_data; > do_dummy_read(skel->progs.dump_task_sleepable); > > ASSERT_GT(skel->bss->num_expected_failure_copy_from_user_task, 0, > "num_expected_failure_copy_from_user_task"); > ASSERT_GT(skel->bss->num_success_copy_from_user_task, 0, > "num_success_copy_from_user_task"); > + ASSERT_GT(skel->bss->num_expected_failure_copy_from_user_task_str, 0, > + "num_expected_failure_copy_from_user_task_str"); > + ASSERT_GT(skel->bss->num_success_copy_from_user_task_str, 0, > + "num_success_copy_from_user_task_str"); > > bpf_iter_tasks__destroy(skel); > } > diff --git a/tools/testing/selftests/bpf/prog_tests/read_vsyscall.c b/tools/testing/selftests/bpf/prog_tests/read_vsyscall.c > index c7b9ba8b1d06..a8d1eaa67020 100644 > --- a/tools/testing/selftests/bpf/prog_tests/read_vsyscall.c > +++ b/tools/testing/selftests/bpf/prog_tests/read_vsyscall.c > @@ -24,6 +24,7 @@ struct read_ret_desc { > { .name = "copy_from_user", .ret = -EFAULT }, > { .name = "copy_from_user_task", .ret = -EFAULT }, > { .name = "copy_from_user_str", .ret = -EFAULT }, > + { .name = "copy_from_user_task_str", .ret = -EFAULT }, > }; > > void test_read_vsyscall(void) > diff --git a/tools/testing/selftests/bpf/progs/bpf_iter_tasks.c b/tools/testing/selftests/bpf/progs/bpf_iter_tasks.c > index bc10c4e4b4fa..90691e34b915 100644 > --- a/tools/testing/selftests/bpf/progs/bpf_iter_tasks.c > +++ b/tools/testing/selftests/bpf/progs/bpf_iter_tasks.c > @@ -9,6 +9,7 @@ char _license[] SEC("license") = "GPL"; > uint32_t tid = 0; > int num_unknown_tid = 0; > int num_known_tid = 0; > +void *user_ptr = 0; > > SEC("iter/task") > int dump_task(struct bpf_iter__task *ctx) > @@ -35,7 +36,9 @@ int dump_task(struct bpf_iter__task *ctx) > } > > int num_expected_failure_copy_from_user_task = 0; > +int num_expected_failure_copy_from_user_task_str = 0; > int num_success_copy_from_user_task = 0; > +int num_success_copy_from_user_task_str = 0; > > SEC("iter.s/task") > int dump_task_sleepable(struct bpf_iter__task *ctx) > @@ -44,6 +47,9 @@ int dump_task_sleepable(struct bpf_iter__task *ctx) > struct task_struct *task = ctx->task; > static const char info[] = " === END ==="; > struct pt_regs *regs; > + char task_str1[10] = "aaaaaaaaaa"; > + char task_str2[10], task_str3[10]; > + char task_str4[20] = "aaaaaaaaaaaaaaaaaaaa"; > void *ptr; > uint32_t user_data = 0; > int ret; > @@ -78,8 +84,57 @@ int dump_task_sleepable(struct bpf_iter__task *ctx) > BPF_SEQ_PRINTF(seq, "%s\n", info); > return 0; > } > + > ++num_success_copy_from_user_task; > > + /* Read an invalid pointer and ensure we get an error */ > + ptr = NULL; > + ret = bpf_copy_from_user_task_str((char *)task_str1, sizeof(task_str1), ptr, task, 0); > + if (ret >= 0 || task_str1[9] != 'a') { > + BPF_SEQ_PRINTF(seq, "%s\n", info); > + return 0; > + } > + > + /* Read an invalid pointer and ensure we get error with pad zeros flag */ > + ptr = NULL; > + ret = bpf_copy_from_user_task_str((char *)task_str1, sizeof(task_str1), ptr, task, BPF_F_PAD_ZEROS); > + if (ret >= 0 || task_str1[9] != '\0') { > + BPF_SEQ_PRINTF(seq, "%s\n", info); > + return 0; > + } > + > + ++num_expected_failure_copy_from_user_task_str; > + > + /* Same length as the string */ > + ret = bpf_copy_from_user_task_str((char *)task_str2, 10, user_ptr, task, 0); > + if (bpf_strncmp(task_str2, 10, "test_data\0") != 0 || ret != 10) { > + BPF_SEQ_PRINTF(seq, "%s\n", info); > + return 0; > + } > + > + /* Shorter length than the string */ > + ret = bpf_copy_from_user_task_str((char *)task_str3, 9, user_ptr, task, 0); > + if (bpf_strncmp(task_str3, 9, "test_dat\0") != 0 || ret != 9) { > + BPF_SEQ_PRINTF(seq, "%s\n", info); > + return 0; > + } > + > + /* Longer length than the string */ > + ret = bpf_copy_from_user_task_str((char *)task_str4, 20, user_ptr, task, 0); > + if (bpf_strncmp(task_str4, 10, "test_data\0") != 0 || ret != 10 || task_str4[sizeof(task_str4) - 1] != 'a') { > + BPF_SEQ_PRINTF(seq, "%s\n", info); > + return 0; > + } > + > + /* Longer length than the string with pad zeros flag */ > + ret = bpf_copy_from_user_task_str((char *)task_str4, 20, user_ptr, task, BPF_F_PAD_ZEROS); > + if (bpf_strncmp(task_str4, 10, "test_data\0") != 0 || ret != 10 || task_str4[sizeof(task_str4) - 1] != '\0') { looks like a long string, please check 100 character limit > + BPF_SEQ_PRINTF(seq, "%s\n", info); > + return 0; nit: this BPF_SEQ_PRINTF() + return 0 repetition is... repetitive :) `goto drop_out;` maybe? > + } > + > + ++num_success_copy_from_user_task_str; > + > if (ctx->meta->seq_num == 0) > BPF_SEQ_PRINTF(seq, " tgid gid data\n"); > > diff --git a/tools/testing/selftests/bpf/progs/read_vsyscall.c b/tools/testing/selftests/bpf/progs/read_vsyscall.c > index 39ebef430059..623c1c5bd2d0 100644 > --- a/tools/testing/selftests/bpf/progs/read_vsyscall.c > +++ b/tools/testing/selftests/bpf/progs/read_vsyscall.c > @@ -8,14 +8,15 @@ > > int target_pid = 0; > void *user_ptr = 0; > -int read_ret[9]; > +int read_ret[10]; > > char _license[] SEC("license") = "GPL"; > > /* > - * This is the only kfunc, the others are helpers > + * These are the kfuncs, the others are helpers > */ > int bpf_copy_from_user_str(void *dst, u32, const void *, u64) __weak __ksym; > +int bpf_copy_from_user_task_str(void *dst, u32, const void *, struct task_struct *, u64) __weak __ksym; these definitions should be coming from vmlinux.h, no need to add them manually anymore > > SEC("fentry/" SYS_PREFIX "sys_nanosleep") > int do_probe_read(void *ctx) > @@ -47,6 +48,7 @@ int do_copy_from_user(void *ctx) > read_ret[7] = bpf_copy_from_user_task(buf, sizeof(buf), user_ptr, > bpf_get_current_task_btf(), 0); > read_ret[8] = bpf_copy_from_user_str((char *)buf, sizeof(buf), user_ptr, 0); > + read_ret[9] = bpf_copy_from_user_task_str((char *)buf, sizeof(buf), user_ptr, bpf_get_current_task_btf(), 0); please drop all those (char *) casts, in C any pointer is implicitly castable to `void *` and `void *` is (implicitly) castable to any other pointer. C++ is stricter, but in C it's canonical to not do casting > > return 0; > } > -- > 2.43.5 >