From: Marcus Wichelmann <marcus.wichelmann@hetzner-cloud.de>
To: Jordan Rome <linux@jordanrome.com>, bpf@vger.kernel.org
Cc: linux-mm@kvack.org, Alexei Starovoitov <ast@kernel.org>,
Daniel Borkmann <daniel@iogearbox.net>,
Andrii Nakryiko <andrii@kernel.org>,
Kernel Team <kernel-team@fb.com>,
Andrew Morton <akpm@linux-foundation.org>,
Shakeel Butt <shakeel.butt@linux.dev>,
Alexander Potapenko <glider@google.com>
Subject: Re: [bpf-next v8 3/3] selftests/bpf: Add tests for bpf_copy_from_user_task_str
Date: Fri, 21 Feb 2025 16:00:47 +0100 [thread overview]
Message-ID: <386d3514-1822-45a2-a2c5-1567a0d599a5@hetzner-cloud.de> (raw)
In-Reply-To: <20250213152125.1837400-3-linux@jordanrome.com>
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?
# 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@jordanrome.com>
> ---
> .../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@hetzner-cloud.de
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
------------------
next prev parent reply other threads:[~2025-02-21 15:01 UTC|newest]
Thread overview: 13+ messages / expand[flat|nested] mbox.gz Atom feed top
2025-02-13 15:21 [bpf-next v8 1/3] mm: add copy_remote_vm_str Jordan Rome
2025-02-13 15:21 ` [bpf-next v8 2/3] bpf: Add bpf_copy_from_user_task_str kfunc Jordan Rome
2025-02-13 15:21 ` [bpf-next v8 3/3] selftests/bpf: Add tests for bpf_copy_from_user_task_str Jordan Rome
2025-02-21 15:00 ` Marcus Wichelmann [this message]
2025-02-21 18:26 ` Andrii Nakryiko
2025-02-22 13:19 ` Marcus Wichelmann
2025-02-19 23:33 ` [bpf-next v8 1/3] mm: add copy_remote_vm_str Shakeel Butt
2025-02-20 0:19 ` Andrew Morton
2025-02-20 1:02 ` Andrii Nakryiko
2025-02-20 11:22 ` Jordan Rome
2025-02-20 0:18 ` Andrew Morton
2025-02-20 1:02 ` Andrii Nakryiko
2025-02-20 1:20 ` patchwork-bot+netdevbpf
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=386d3514-1822-45a2-a2c5-1567a0d599a5@hetzner-cloud.de \
--to=marcus.wichelmann@hetzner-cloud.de \
--cc=akpm@linux-foundation.org \
--cc=andrii@kernel.org \
--cc=ast@kernel.org \
--cc=bpf@vger.kernel.org \
--cc=daniel@iogearbox.net \
--cc=glider@google.com \
--cc=kernel-team@fb.com \
--cc=linux-mm@kvack.org \
--cc=linux@jordanrome.com \
--cc=shakeel.butt@linux.dev \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox