From: Jiri Olsa <olsajiri@gmail.com>
To: Andrii Nakryiko <andrii@kernel.org>
Cc: bpf@vger.kernel.org, ast@kernel.org, daniel@iogearbox.net,
martin.lau@kernel.org, kernel-team@meta.com
Subject: Re: [PATCH v2 bpf 4/5] selftests/bpf: extend multi-uprobe tests with child thread case
Date: Wed, 22 May 2024 09:46:36 +0200 [thread overview]
Message-ID: <Zk2i3Ohmnv52Zn08@krava> (raw)
In-Reply-To: <20240521163401.3005045-5-andrii@kernel.org>
On Tue, May 21, 2024 at 09:34:00AM -0700, Andrii Nakryiko wrote:
> Extend existing multi-uprobe tests to test that PID filtering works
> correctly. We already have child *process* tests, but we need also child
> *thread* tests. This patch adds spawn_thread() helper to start child
> thread, wait for it to be ready, and then instruct it to trigger desired
> uprobes.
>
> Additionally, we extend BPF-side code to track thread ID, not just
> process ID. Also we detect whether extraneous triggerings with
> unexpected process IDs happened, and validate that none of that happened
> in practice.
>
> These changes prove that fixed PID filtering logic for multi-uprobe
> works as expected. These tests fail on old kernels.
>
> Signed-off-by: Andrii Nakryiko <andrii@kernel.org>
Acked-by: Jiri Olsa <jolsa@kernel.org>
jirka
> ---
> .../bpf/prog_tests/uprobe_multi_test.c | 107 ++++++++++++++++--
> .../selftests/bpf/progs/uprobe_multi.c | 17 ++-
> 2 files changed, 115 insertions(+), 9 deletions(-)
>
> diff --git a/tools/testing/selftests/bpf/prog_tests/uprobe_multi_test.c b/tools/testing/selftests/bpf/prog_tests/uprobe_multi_test.c
> index 38fda42fd70f..677232d31432 100644
> --- a/tools/testing/selftests/bpf/prog_tests/uprobe_multi_test.c
> +++ b/tools/testing/selftests/bpf/prog_tests/uprobe_multi_test.c
> @@ -1,6 +1,7 @@
> // SPDX-License-Identifier: GPL-2.0
>
> #include <unistd.h>
> +#include <pthread.h>
> #include <test_progs.h>
> #include "uprobe_multi.skel.h"
> #include "uprobe_multi_bench.skel.h"
> @@ -27,7 +28,10 @@ noinline void uprobe_multi_func_3(void)
>
> struct child {
> int go[2];
> + int c2p[2]; /* child -> parent channel */
> int pid;
> + int tid;
> + pthread_t thread;
> };
>
> static void release_child(struct child *child)
> @@ -38,6 +42,10 @@ static void release_child(struct child *child)
> return;
> close(child->go[1]);
> close(child->go[0]);
> + if (child->thread)
> + pthread_join(child->thread, NULL);
> + close(child->c2p[0]);
> + close(child->c2p[1]);
> if (child->pid > 0)
> waitpid(child->pid, &child_status, 0);
> }
> @@ -63,7 +71,7 @@ static struct child *spawn_child(void)
> if (pipe(child.go))
> return NULL;
>
> - child.pid = fork();
> + child.pid = child.tid = fork();
> if (child.pid < 0) {
> release_child(&child);
> errno = EINVAL;
> @@ -89,6 +97,66 @@ static struct child *spawn_child(void)
> return &child;
> }
>
> +static void *child_thread(void *ctx)
> +{
> + struct child *child = ctx;
> + int c = 0, err;
> +
> + child->tid = syscall(SYS_gettid);
> +
> + /* let parent know we are ready */
> + err = write(child->c2p[1], &c, 1);
> + if (err != 1)
> + pthread_exit(&err);
> +
> + /* wait for parent's kick */
> + err = read(child->go[0], &c, 1);
> + if (err != 1)
> + pthread_exit(&err);
> +
> + uprobe_multi_func_1();
> + uprobe_multi_func_2();
> + uprobe_multi_func_3();
> +
> + err = 0;
> + pthread_exit(&err);
> +}
> +
> +static struct child *spawn_thread(void)
> +{
> + static struct child child;
> + int c, err;
> +
> + /* pipe to notify child to execute the trigger functions */
> + if (pipe(child.go))
> + return NULL;
> + /* pipe to notify parent that child thread is ready */
> + if (pipe(child.c2p)) {
> + close(child.go[0]);
> + close(child.go[1]);
> + return NULL;
> + }
> +
> + child.pid = getpid();
> +
> + err = pthread_create(&child.thread, NULL, child_thread, &child);
> + if (err) {
> + err = -errno;
> + close(child.go[0]);
> + close(child.go[1]);
> + close(child.c2p[0]);
> + close(child.c2p[1]);
> + errno = -err;
> + return NULL;
> + }
> +
> + err = read(child.c2p[0], &c, 1);
> + if (!ASSERT_EQ(err, 1, "child_thread_ready"))
> + return NULL;
> +
> + return &child;
> +}
> +
> static void uprobe_multi_test_run(struct uprobe_multi *skel, struct child *child)
> {
> skel->bss->uprobe_multi_func_1_addr = (__u64) uprobe_multi_func_1;
> @@ -103,15 +171,22 @@ static void uprobe_multi_test_run(struct uprobe_multi *skel, struct child *child
> * passed at the probe attach.
> */
> skel->bss->pid = child ? 0 : getpid();
> + skel->bss->expect_pid = child ? child->pid : 0;
> +
> + /* trigger all probes, if we are testing child *process*, just to make
> + * sure that PID filtering doesn't let through activations from wrong
> + * PIDs; when we test child *thread*, we don't want to do this to
> + * avoid double counting number of triggering events
> + */
> + if (!child || !child->thread) {
> + uprobe_multi_func_1();
> + uprobe_multi_func_2();
> + uprobe_multi_func_3();
> + }
>
> if (child)
> kick_child(child);
>
> - /* trigger all probes */
> - uprobe_multi_func_1();
> - uprobe_multi_func_2();
> - uprobe_multi_func_3();
> -
> /*
> * There are 2 entry and 2 exit probe called for each uprobe_multi_func_[123]
> * function and each slepable probe (6) increments uprobe_multi_sleep_result.
> @@ -126,8 +201,12 @@ static void uprobe_multi_test_run(struct uprobe_multi *skel, struct child *child
>
> ASSERT_EQ(skel->bss->uprobe_multi_sleep_result, 6, "uprobe_multi_sleep_result");
>
> - if (child)
> + ASSERT_FALSE(skel->bss->bad_pid_seen, "bad_pid_seen");
> +
> + if (child) {
> ASSERT_EQ(skel->bss->child_pid, child->pid, "uprobe_multi_child_pid");
> + ASSERT_EQ(skel->bss->child_tid, child->tid, "uprobe_multi_child_tid");
> + }
> }
>
> static void test_skel_api(void)
> @@ -210,6 +289,13 @@ test_attach_api(const char *binary, const char *pattern, struct bpf_uprobe_multi
> return;
>
> __test_attach_api(binary, pattern, opts, child);
> +
> + /* pid filter (thread) */
> + child = spawn_thread();
> + if (!ASSERT_OK_PTR(child, "spawn_thread"))
> + return;
> +
> + __test_attach_api(binary, pattern, opts, child);
> }
>
> static void test_attach_api_pattern(void)
> @@ -495,6 +581,13 @@ static void test_link_api(void)
> return;
>
> __test_link_api(child);
> +
> + /* pid filter (thread) */
> + child = spawn_thread();
> + if (!ASSERT_OK_PTR(child, "spawn_thread"))
> + return;
> +
> + __test_link_api(child);
> }
>
> static void test_bench_attach_uprobe(void)
> diff --git a/tools/testing/selftests/bpf/progs/uprobe_multi.c b/tools/testing/selftests/bpf/progs/uprobe_multi.c
> index 419d9aa28fce..86a7ff5d3726 100644
> --- a/tools/testing/selftests/bpf/progs/uprobe_multi.c
> +++ b/tools/testing/selftests/bpf/progs/uprobe_multi.c
> @@ -22,6 +22,10 @@ __u64 uprobe_multi_sleep_result = 0;
>
> int pid = 0;
> int child_pid = 0;
> +int child_tid = 0;
> +
> +int expect_pid = 0;
> +bool bad_pid_seen = false;
>
> bool test_cookie = false;
> void *user_ptr = 0;
> @@ -36,11 +40,19 @@ static __always_inline bool verify_sleepable_user_copy(void)
>
> static void uprobe_multi_check(void *ctx, bool is_return, bool is_sleep)
> {
> - child_pid = bpf_get_current_pid_tgid() >> 32;
> + __u64 cur_pid_tgid = bpf_get_current_pid_tgid();
> + __u32 cur_pid;
>
> - if (pid && child_pid != pid)
> + cur_pid = cur_pid_tgid >> 32;
> + if (pid && cur_pid != pid)
> return;
>
> + if (expect_pid && cur_pid != expect_pid)
> + bad_pid_seen = true;
> +
> + child_pid = cur_pid_tgid >> 32;
> + child_tid = (__u32)cur_pid_tgid;
> +
> __u64 cookie = test_cookie ? bpf_get_attach_cookie(ctx) : 0;
> __u64 addr = bpf_get_func_ip(ctx);
>
> @@ -97,5 +109,6 @@ int uretprobe_sleep(struct pt_regs *ctx)
> SEC("uprobe.multi//proc/self/exe:uprobe_multi_func_*")
> int uprobe_extra(struct pt_regs *ctx)
> {
> + /* we need this one just to mix PID-filtered and global uprobes */
> return 0;
> }
> --
> 2.43.0
>
>
next prev parent reply other threads:[~2024-05-22 7:46 UTC|newest]
Thread overview: 8+ messages / expand[flat|nested] mbox.gz Atom feed top
2024-05-21 16:33 [PATCH v2 bpf 0/5] Fix BPF multi-uprobe PID filtering logic Andrii Nakryiko
2024-05-21 16:33 ` [PATCH v2 bpf 1/5] bpf: fix " Andrii Nakryiko
2024-05-21 16:33 ` [PATCH v2 bpf 2/5] bpf: remove unnecessary rcu_read_{lock,unlock}() in multi-uprobe attach logic Andrii Nakryiko
2024-05-21 16:33 ` [PATCH v2 bpf 3/5] libbpf: detect broken PID filtering logic for multi-uprobe Andrii Nakryiko
2024-05-21 16:34 ` [PATCH v2 bpf 4/5] selftests/bpf: extend multi-uprobe tests with child thread case Andrii Nakryiko
2024-05-22 7:46 ` Jiri Olsa [this message]
2024-05-21 16:34 ` [PATCH v2 bpf 5/5] selftests/bpf: extend multi-uprobe tests with USDTs Andrii Nakryiko
2024-05-25 17:50 ` [PATCH v2 bpf 0/5] Fix BPF multi-uprobe PID filtering logic 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=Zk2i3Ohmnv52Zn08@krava \
--to=olsajiri@gmail.com \
--cc=andrii@kernel.org \
--cc=ast@kernel.org \
--cc=bpf@vger.kernel.org \
--cc=daniel@iogearbox.net \
--cc=kernel-team@meta.com \
--cc=martin.lau@kernel.org \
/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