Skip to content

Commit 7034242

Browse files
anakryikoAlexei Starovoitov
authored and
Alexei Starovoitov
committed
selftests/bpf: extend multi-uprobe tests with child thread case
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 <[email protected]> Acked-by: Jiri Olsa <[email protected]> Link: https://lore.kernel.org/r/[email protected] Signed-off-by: Alexei Starovoitov <[email protected]>
1 parent 04d939a commit 7034242

File tree

2 files changed

+115
-9
lines changed

2 files changed

+115
-9
lines changed

tools/testing/selftests/bpf/prog_tests/uprobe_multi_test.c

Lines changed: 100 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
// SPDX-License-Identifier: GPL-2.0
22

33
#include <unistd.h>
4+
#include <pthread.h>
45
#include <test_progs.h>
56
#include "uprobe_multi.skel.h"
67
#include "uprobe_multi_bench.skel.h"
@@ -27,7 +28,10 @@ noinline void uprobe_multi_func_3(void)
2728

2829
struct child {
2930
int go[2];
31+
int c2p[2]; /* child -> parent channel */
3032
int pid;
33+
int tid;
34+
pthread_t thread;
3135
};
3236

3337
static void release_child(struct child *child)
@@ -38,6 +42,10 @@ static void release_child(struct child *child)
3842
return;
3943
close(child->go[1]);
4044
close(child->go[0]);
45+
if (child->thread)
46+
pthread_join(child->thread, NULL);
47+
close(child->c2p[0]);
48+
close(child->c2p[1]);
4149
if (child->pid > 0)
4250
waitpid(child->pid, &child_status, 0);
4351
}
@@ -63,7 +71,7 @@ static struct child *spawn_child(void)
6371
if (pipe(child.go))
6472
return NULL;
6573

66-
child.pid = fork();
74+
child.pid = child.tid = fork();
6775
if (child.pid < 0) {
6876
release_child(&child);
6977
errno = EINVAL;
@@ -89,6 +97,66 @@ static struct child *spawn_child(void)
8997
return &child;
9098
}
9199

100+
static void *child_thread(void *ctx)
101+
{
102+
struct child *child = ctx;
103+
int c = 0, err;
104+
105+
child->tid = syscall(SYS_gettid);
106+
107+
/* let parent know we are ready */
108+
err = write(child->c2p[1], &c, 1);
109+
if (err != 1)
110+
pthread_exit(&err);
111+
112+
/* wait for parent's kick */
113+
err = read(child->go[0], &c, 1);
114+
if (err != 1)
115+
pthread_exit(&err);
116+
117+
uprobe_multi_func_1();
118+
uprobe_multi_func_2();
119+
uprobe_multi_func_3();
120+
121+
err = 0;
122+
pthread_exit(&err);
123+
}
124+
125+
static struct child *spawn_thread(void)
126+
{
127+
static struct child child;
128+
int c, err;
129+
130+
/* pipe to notify child to execute the trigger functions */
131+
if (pipe(child.go))
132+
return NULL;
133+
/* pipe to notify parent that child thread is ready */
134+
if (pipe(child.c2p)) {
135+
close(child.go[0]);
136+
close(child.go[1]);
137+
return NULL;
138+
}
139+
140+
child.pid = getpid();
141+
142+
err = pthread_create(&child.thread, NULL, child_thread, &child);
143+
if (err) {
144+
err = -errno;
145+
close(child.go[0]);
146+
close(child.go[1]);
147+
close(child.c2p[0]);
148+
close(child.c2p[1]);
149+
errno = -err;
150+
return NULL;
151+
}
152+
153+
err = read(child.c2p[0], &c, 1);
154+
if (!ASSERT_EQ(err, 1, "child_thread_ready"))
155+
return NULL;
156+
157+
return &child;
158+
}
159+
92160
static void uprobe_multi_test_run(struct uprobe_multi *skel, struct child *child)
93161
{
94162
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
103171
* passed at the probe attach.
104172
*/
105173
skel->bss->pid = child ? 0 : getpid();
174+
skel->bss->expect_pid = child ? child->pid : 0;
175+
176+
/* trigger all probes, if we are testing child *process*, just to make
177+
* sure that PID filtering doesn't let through activations from wrong
178+
* PIDs; when we test child *thread*, we don't want to do this to
179+
* avoid double counting number of triggering events
180+
*/
181+
if (!child || !child->thread) {
182+
uprobe_multi_func_1();
183+
uprobe_multi_func_2();
184+
uprobe_multi_func_3();
185+
}
106186

107187
if (child)
108188
kick_child(child);
109189

110-
/* trigger all probes */
111-
uprobe_multi_func_1();
112-
uprobe_multi_func_2();
113-
uprobe_multi_func_3();
114-
115190
/*
116191
* There are 2 entry and 2 exit probe called for each uprobe_multi_func_[123]
117192
* 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
126201

127202
ASSERT_EQ(skel->bss->uprobe_multi_sleep_result, 6, "uprobe_multi_sleep_result");
128203

129-
if (child)
204+
ASSERT_FALSE(skel->bss->bad_pid_seen, "bad_pid_seen");
205+
206+
if (child) {
130207
ASSERT_EQ(skel->bss->child_pid, child->pid, "uprobe_multi_child_pid");
208+
ASSERT_EQ(skel->bss->child_tid, child->tid, "uprobe_multi_child_tid");
209+
}
131210
}
132211

133212
static void test_skel_api(void)
@@ -210,6 +289,13 @@ test_attach_api(const char *binary, const char *pattern, struct bpf_uprobe_multi
210289
return;
211290

212291
__test_attach_api(binary, pattern, opts, child);
292+
293+
/* pid filter (thread) */
294+
child = spawn_thread();
295+
if (!ASSERT_OK_PTR(child, "spawn_thread"))
296+
return;
297+
298+
__test_attach_api(binary, pattern, opts, child);
213299
}
214300

215301
static void test_attach_api_pattern(void)
@@ -495,6 +581,13 @@ static void test_link_api(void)
495581
return;
496582

497583
__test_link_api(child);
584+
585+
/* pid filter (thread) */
586+
child = spawn_thread();
587+
if (!ASSERT_OK_PTR(child, "spawn_thread"))
588+
return;
589+
590+
__test_link_api(child);
498591
}
499592

500593
static void test_bench_attach_uprobe(void)

tools/testing/selftests/bpf/progs/uprobe_multi.c

Lines changed: 15 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,10 @@ __u64 uprobe_multi_sleep_result = 0;
2222

2323
int pid = 0;
2424
int child_pid = 0;
25+
int child_tid = 0;
26+
27+
int expect_pid = 0;
28+
bool bad_pid_seen = false;
2529

2630
bool test_cookie = false;
2731
void *user_ptr = 0;
@@ -36,11 +40,19 @@ static __always_inline bool verify_sleepable_user_copy(void)
3640

3741
static void uprobe_multi_check(void *ctx, bool is_return, bool is_sleep)
3842
{
39-
child_pid = bpf_get_current_pid_tgid() >> 32;
43+
__u64 cur_pid_tgid = bpf_get_current_pid_tgid();
44+
__u32 cur_pid;
4045

41-
if (pid && child_pid != pid)
46+
cur_pid = cur_pid_tgid >> 32;
47+
if (pid && cur_pid != pid)
4248
return;
4349

50+
if (expect_pid && cur_pid != expect_pid)
51+
bad_pid_seen = true;
52+
53+
child_pid = cur_pid_tgid >> 32;
54+
child_tid = (__u32)cur_pid_tgid;
55+
4456
__u64 cookie = test_cookie ? bpf_get_attach_cookie(ctx) : 0;
4557
__u64 addr = bpf_get_func_ip(ctx);
4658

@@ -97,5 +109,6 @@ int uretprobe_sleep(struct pt_regs *ctx)
97109
SEC("uprobe.multi//proc/self/exe:uprobe_multi_func_*")
98110
int uprobe_extra(struct pt_regs *ctx)
99111
{
112+
/* we need this one just to mix PID-filtered and global uprobes */
100113
return 0;
101114
}

0 commit comments

Comments
 (0)