On Fri, Feb 21, 2025 at 7:01 AM Marcus Wichelmann <marcus.wichelmann@xxxxxxxxxxxxxxxx> wrote: > > Hi, > > I'm not sure what I'm doing wrong, but after rebasing on latest bpf-next > which includes this patch, I'm no longer able to build the bpf selftests: > > # pushd tools/testing/selftests/bpf/ > # make -j80 > [...] > GEN-SKEL [test_progs] bpf_iter_task_vmas.skel.h > CLNG-BPF [test_progs] bpf_iter_tasks.bpf.o > progs/bpf_iter_tasks.c:98:8: error: call to undeclared function 'bpf_copy_from_user_task_str'; ISO C99 and later do not support implicit function declarations [-Wimplicit-function-declaration] > 98 | ret = bpf_copy_from_user_task_str((char *)task_str1, sizeof(task_str1), ptr, task, 0); > | ^ > 1 error generated. > make: *** [Makefile:733: /root/linux/tools/testing/selftests/bpf/bpf_iter_tasks.bpf.o] Error 1 > > I suppose the function definition should be in the vmlinux.h? > Yes, it should be in vmlinux.h, and if you don't have it, then you must have a bit too old pahole. $ git tag --contains ce4d0bc0200e3 v1.27 v1.28 > # grep bpf_copy tools/include/vmlinux.h > typedef u64 (*btf_bpf_copy_from_user)(void *, u32, const void *); > typedef u64 (*btf_bpf_copy_from_user_task)(void *, u32, const void *, struct task_struct *, u64); > > The kernel built from this revision is booted while trying to compile > the selftests. I can also see that the kfunc is there when dumping without "format c": > > # bpftool btf dump file /sys/kernel/btf/vmlinux | grep bpf_copy_from_user_task_str > [116060] FUNC 'bpf_copy_from_user_task_str' type_id=116059 linkage=static > > But when dumping the vmlinux headers with "format c", I cannot see the kfunc. > I'm not sure if this is normal: > > # bpftool btf dump file /sys/kernel/btf/vmlinux format c | grep bpf_copy_from_user_task_str > # > > CONFIG_BPF SYSCALL is enabled. Are there other config options that > have to be enabled that I may be missing? > > I'm trying this on a aarch64 system. I also tried to fully clean > my working tree using `git clean -d -x -f`, which didn't help > either. > > Thanks! > Marcus > > > Am 13.02.25 um 16:21 schrieb Jordan Rome: > > 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 | 68 +++++++++++ > > .../selftests/bpf/prog_tests/read_vsyscall.c | 1 + > > .../selftests/bpf/progs/bpf_iter_tasks.c | 110 ++++++++++++++++++ > > .../selftests/bpf/progs/read_vsyscall.c | 11 +- > > 4 files changed, 188 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..add4a18c33bd 100644 > > --- a/tools/testing/selftests/bpf/prog_tests/bpf_iter.c > > +++ b/tools/testing/selftests/bpf/prog_tests/bpf_iter.c > > @@ -323,19 +323,87 @@ static void test_task_pidfd(void) > > static void test_task_sleepable(void) > > { > > struct bpf_iter_tasks *skel; > > + int pid, status, err, data_pipe[2], finish_pipe[2], c; > > + char *test_data = NULL; > > + char *test_data_long = NULL; > > + char *data[2]; > > + > > + if (!ASSERT_OK(pipe(data_pipe), "data_pipe") || > > + !ASSERT_OK(pipe(finish_pipe), "finish_pipe")) > > + return; > > > > skel = bpf_iter_tasks__open_and_load(); > > if (!ASSERT_OK_PTR(skel, "bpf_iter_tasks__open_and_load")) > > return; > > > > + pid = fork(); > > + if (!ASSERT_GE(pid, 0, "fork")) > > + return; > > + > > + if (pid == 0) { > > + /* child */ > > + close(data_pipe[0]); > > + close(finish_pipe[1]); > > + > > + test_data = malloc(sizeof(char) * 10); > > + strncpy(test_data, "test_data", 10); > > + test_data[9] = '\0'; > > + > > + test_data_long = malloc(sizeof(char) * 5000); > > + for (int i = 0; i < 5000; ++i) { > > + if (i % 2 == 0) > > + test_data_long[i] = 'b'; > > + else > > + test_data_long[i] = 'a'; > > + } > > + test_data_long[4999] = '\0'; > > + > > + data[0] = test_data; > > + data[1] = test_data_long; > > + > > + write(data_pipe[1], &data, sizeof(data)); > > + > > + /* keep child alive until after the test */ > > + err = read(finish_pipe[0], &c, 1); > > + if (err != 1) > > + exit(-1); > > + > > + close(data_pipe[1]); > > + close(finish_pipe[0]); > > + _exit(0); > > + } > > + > > + /* parent */ > > + close(data_pipe[1]); > > + close(finish_pipe[0]); > > + > > + err = read(data_pipe[0], &data, sizeof(data)); > > + ASSERT_EQ(err, sizeof(data), "read_check"); > > + > > + skel->bss->user_ptr = data[0]; > > + skel->bss->user_ptr_long = data[1]; > > + skel->bss->pid = pid; > > + > > 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); > > + > > + write(finish_pipe[1], &c, 1); > > + err = waitpid(pid, &status, 0); > > + ASSERT_EQ(err, pid, "waitpid"); > > + ASSERT_EQ(status, 0, "zero_child_exit"); > > + > > + close(data_pipe[0]); > > + close(finish_pipe[1]); > > } > > > > static void test_task_stack(void) > > 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..966ee5a7b066 100644 > > --- a/tools/testing/selftests/bpf/progs/bpf_iter_tasks.c > > +++ b/tools/testing/selftests/bpf/progs/bpf_iter_tasks.c > > @@ -9,6 +9,13 @@ char _license[] SEC("license") = "GPL"; > > uint32_t tid = 0; > > int num_unknown_tid = 0; > > int num_known_tid = 0; > > +void *user_ptr = 0; > > +void *user_ptr_long = 0; > > +uint32_t pid = 0; > > + > > +static char big_str1[5000]; > > +static char big_str2[5005]; > > +static char big_str3[4996]; > > > > SEC("iter/task") > > int dump_task(struct bpf_iter__task *ctx) > > @@ -35,7 +42,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 +53,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 +90,106 @@ 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' || task_str1[0] != '\0') { > > + 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' || task_str1[0] != '\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); > > + /* only need to do the task pid check once */ > > + if (bpf_strncmp(task_str2, 10, "test_data\0") != 0 || ret != 10 || task->tgid != pid) { > > + BPF_SEQ_PRINTF(seq, "%s\n", info); > > + return 0; > > + } > > + > > + /* Shorter length than the string */ > > + ret = bpf_copy_from_user_task_str((char *)task_str3, 2, user_ptr, task, 0); > > + if (bpf_strncmp(task_str3, 2, "t\0") != 0 || ret != 2) { > > + 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') { > > + BPF_SEQ_PRINTF(seq, "%s\n", info); > > + return 0; > > + } > > + > > + /* Longer length than the string past a page boundary */ > > + ret = bpf_copy_from_user_task_str(big_str1, 5000, user_ptr, task, 0); > > + if (bpf_strncmp(big_str1, 10, "test_data\0") != 0 || ret != 10) { > > + BPF_SEQ_PRINTF(seq, "%s\n", info); > > + return 0; > > + } > > + > > + /* String that crosses a page boundary */ > > + ret = bpf_copy_from_user_task_str(big_str1, 5000, user_ptr_long, task, BPF_F_PAD_ZEROS); > > + if (bpf_strncmp(big_str1, 4, "baba") != 0 || ret != 5000 > > + || bpf_strncmp(big_str1 + 4996, 4, "bab\0") != 0) { > > + BPF_SEQ_PRINTF(seq, "%s\n", info); > > + return 0; > > + } > > + > > + for (int i = 0; i < 4999; ++i) { > > + if (i % 2 == 0) { > > + if (big_str1[i] != 'b') { > > + BPF_SEQ_PRINTF(seq, "%s\n", info); > > + return 0; > > + } > > + } else { > > + if (big_str1[i] != 'a') { > > + BPF_SEQ_PRINTF(seq, "%s\n", info); > > + return 0; > > + } > > + } > > + } > > + > > + /* Longer length than the string that crosses a page boundary */ > > + ret = bpf_copy_from_user_task_str(big_str2, 5005, user_ptr_long, task, BPF_F_PAD_ZEROS); > > + if (bpf_strncmp(big_str2, 4, "baba") != 0 || ret != 5000 > > + || bpf_strncmp(big_str2 + 4996, 5, "bab\0\0") != 0) { > > + BPF_SEQ_PRINTF(seq, "%s\n", info); > > + return 0; > > + } > > + > > + /* Shorter length than the string that crosses a page boundary */ > > + ret = bpf_copy_from_user_task_str(big_str3, 4996, user_ptr_long, task, 0); > > + if (bpf_strncmp(big_str3, 4, "baba") != 0 || ret != 4996 > > + || bpf_strncmp(big_str3 + 4992, 4, "bab\0") != 0) { > > + BPF_SEQ_PRINTF(seq, "%s\n", info); > > + return 0; > > + } > > + > > + ++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..395591374d4f 100644 > > --- a/tools/testing/selftests/bpf/progs/read_vsyscall.c > > +++ b/tools/testing/selftests/bpf/progs/read_vsyscall.c > > @@ -8,14 +8,16 @@ > > > > 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; > > > > SEC("fentry/" SYS_PREFIX "sys_nanosleep") > > int do_probe_read(void *ctx) > > @@ -47,6 +49,11 @@ 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); > > > > return 0; > > } > > -- > > 2.43.5 > > > > > > -- > Best regards, > Marcus Wichelmann > Linux Networking Specialist > Team SDN > > ______________________________ > > Hetzner Cloud GmbH > Feringastraße 12A > 85774 Unterföhring > Germany > > Phone: +49 89 381690 150 > E-Mail: marcus.wichelmann@xxxxxxxxxxxxxxxx > > Handelsregister München HRB 226782 > Geschäftsführer: Sebastian Färber, Markus Kalmuk > > ------------------ > For information on the processing of your personal > data in the context of this contact, please see > https://hetzner-cloud.de/datenschutz > ------------------ >