selftests/bpf: Add tests for forked/cloned optimized uprobes

Adding tests for forked/cloned optimized uprobes and make
sure the child can properly execute optimized probe for
both fork (dups mm) and clone with CLONE_VM.

Signed-off-by: Jiri Olsa <jolsa@kernel.org>
Signed-off-by: Peter Zijlstra (Intel) <peterz@infradead.org>
Signed-off-by: Ingo Molnar <mingo@kernel.org>
Reviewed-by: Jakub Sitnicki <jakub@cloudflare.com>
Link: https://patch.msgid.link/20260703114917.238144-14-jolsa@kernel.org
This commit is contained in:
Jiri Olsa 2026-07-03 13:49:17 +02:00 committed by Ingo Molnar
parent eccf368562
commit d7ebbae57d

View File

@ -4,6 +4,8 @@
#ifdef __x86_64__
#define _GNU_SOURCE
#include <sched.h>
#include <unistd.h>
#include <asm/ptrace.h>
#include <linux/compiler.h>
@ -13,6 +15,7 @@
#include <sys/syscall.h>
#include <sys/prctl.h>
#include <asm/prctl.h>
#include <stdnoreturn.h>
#include "uprobe_syscall.skel.h"
#include "uprobe_syscall_executed.skel.h"
#include "bpf/libbpf_internal.h"
@ -954,6 +957,87 @@ static void test_uprobe_error(void)
ASSERT_EQ(errno, EPROTO, "errno");
}
__attribute__((aligned(16)))
__nocf_check __weak __naked void uprobe_fork_test(void)
{
asm volatile (
".byte 0x66, 0x2e, 0x0f, 0x1f, 0x84, 0x00, 0x00, 0x00, 0x00, 0x00\n" /* nop10 */
"ret\n"
);
}
static noreturn int child_func(void *arg)
{
struct uprobe_syscall_executed *skel = arg;
/* Make sure the child's probe is still there and optimized.. */
if (memcmp(uprobe_fork_test, lea_rsp, sizeof(lea_rsp)))
_exit(1);
skel->bss->pid = getpid();
/* .. and it executes properly. */
uprobe_fork_test();
if (skel->bss->executed != 3)
_exit(2);
_exit(0);
}
static void test_uprobe_fork_optimized(bool clone_vm)
{
struct uprobe_syscall_executed *skel = NULL;
unsigned long offset;
int pid, status, err;
char stack[65535];
offset = get_uprobe_offset(&uprobe_fork_test);
if (!ASSERT_GE(offset, 0, "get_uprobe_offset"))
return;
skel = uprobe_syscall_executed__open_and_load();
if (!ASSERT_OK_PTR(skel, "open_and_load"))
goto cleanup;
skel->links.test_uprobe = bpf_program__attach_uprobe_opts(skel->progs.test_uprobe,
-1, "/proc/self/exe", offset, NULL);
if (!ASSERT_OK_PTR(skel->links.test_uprobe, "attach_uprobe"))
goto cleanup;
skel->bss->pid = getpid();
/* Trigger optimization of uprobe in uprobe_fork_test. */
uprobe_fork_test();
uprobe_fork_test();
/* Make sure it got optimied. */
if (!ASSERT_OK(memcmp(uprobe_fork_test, lea_rsp, sizeof(lea_rsp)), "optimized"))
goto cleanup;
if (clone_vm) {
pid = clone(child_func, stack + sizeof(stack), CLONE_VM|SIGCHLD, skel);
if (!ASSERT_GT(pid, 0, "clone"))
goto cleanup;
} else {
pid = fork();
if (!ASSERT_GE(pid, 0, "fork"))
goto cleanup;
if (pid == 0)
child_func(skel);
}
/* Wait for the child and verify it exited properly with 0. */
err = waitpid(pid, &status, 0);
if (ASSERT_EQ(err, pid, "waitpid")) {
ASSERT_EQ(WIFEXITED(status), 1, "child_exited");
ASSERT_EQ(WEXITSTATUS(status), 0, "child_exit_code");
}
cleanup:
uprobe_syscall_executed__destroy(skel);
}
static void __test_uprobe_syscall(void)
{
if (test__start_subtest("uretprobe_regs_equal"))
@ -974,6 +1058,10 @@ static void __test_uprobe_syscall(void)
test_uprobe_race();
if (test__start_subtest("uprobe_red_zone"))
test_uprobe_red_zone();
if (test__start_subtest("uprobe_optimized_fork"))
test_uprobe_fork_optimized(false);
if (test__start_subtest("uprobe_optimized_clone_vm"))
test_uprobe_fork_optimized(true);
if (test__start_subtest("uprobe_error"))
test_uprobe_error();
if (test__start_subtest("uprobe_regs_equal"))