Commit 275eae67 authored by Jiri Olsa's avatar Jiri Olsa Committed by Peter Zijlstra
Browse files

selftests/bpf: Add uprobe_regs_equal test



Changing uretprobe_regs_trigger to allow the test for both
uprobe and uretprobe and renaming it to uprobe_regs_equal.

We check that both uprobe and uretprobe probes (bpf programs)
see expected registers with few exceptions.

Signed-off-by: default avatarJiri Olsa <jolsa@kernel.org>
Signed-off-by: default avatarPeter Zijlstra (Intel) <peterz@infradead.org>
Acked-by: default avatarAndrii Nakryiko <andrii@kernel.org>
Link: https://lore.kernel.org/r/20250720112133.244369-19-jolsa@kernel.org
parent 875e1705
Loading
Loading
Loading
Loading
+42 −14
Original line number Diff line number Diff line
@@ -22,15 +22,17 @@

#pragma GCC diagnostic ignored "-Wattributes"

__naked unsigned long uretprobe_regs_trigger(void)
__attribute__((aligned(16)))
__nocf_check __weak __naked unsigned long uprobe_regs_trigger(void)
{
	asm volatile (
		".byte 0x0f, 0x1f, 0x44, 0x00, 0x00\n" /* nop5 */
		"movq $0xdeadbeef, %rax\n"
		"ret\n"
	);
}

__naked void uretprobe_regs(struct pt_regs *before, struct pt_regs *after)
__naked void uprobe_regs(struct pt_regs *before, struct pt_regs *after)
{
	asm volatile (
		"movq %r15,   0(%rdi)\n"
@@ -51,15 +53,17 @@ __naked void uretprobe_regs(struct pt_regs *before, struct pt_regs *after)
		"movq   $0, 120(%rdi)\n" /* orig_rax */
		"movq   $0, 128(%rdi)\n" /* rip      */
		"movq   $0, 136(%rdi)\n" /* cs       */
		"pushq %rax\n"
		"pushf\n"
		"pop %rax\n"
		"movq %rax, 144(%rdi)\n" /* eflags   */
		"pop %rax\n"
		"movq %rsp, 152(%rdi)\n" /* rsp      */
		"movq   $0, 160(%rdi)\n" /* ss       */

		/* save 2nd argument */
		"pushq %rsi\n"
		"call uretprobe_regs_trigger\n"
		"call uprobe_regs_trigger\n"

		/* save  return value and load 2nd argument pointer to rax */
		"pushq %rax\n"
@@ -99,25 +103,37 @@ __naked void uretprobe_regs(struct pt_regs *before, struct pt_regs *after)
);
}

static void test_uretprobe_regs_equal(void)
static void test_uprobe_regs_equal(bool retprobe)
{
	LIBBPF_OPTS(bpf_uprobe_opts, opts,
		.retprobe = retprobe,
	);
	struct uprobe_syscall *skel = NULL;
	struct pt_regs before = {}, after = {};
	unsigned long *pb = (unsigned long *) &before;
	unsigned long *pa = (unsigned long *) &after;
	unsigned long *pp;
	unsigned long offset;
	unsigned int i, cnt;
	int err;

	offset = get_uprobe_offset(&uprobe_regs_trigger);
	if (!ASSERT_GE(offset, 0, "get_uprobe_offset"))
		return;

	skel = uprobe_syscall__open_and_load();
	if (!ASSERT_OK_PTR(skel, "uprobe_syscall__open_and_load"))
		goto cleanup;

	err = uprobe_syscall__attach(skel);
	if (!ASSERT_OK(err, "uprobe_syscall__attach"))
	skel->links.probe = bpf_program__attach_uprobe_opts(skel->progs.probe,
				0, "/proc/self/exe", offset, &opts);
	if (!ASSERT_OK_PTR(skel->links.probe, "bpf_program__attach_uprobe_opts"))
		goto cleanup;

	uretprobe_regs(&before, &after);
	/* make sure uprobe gets optimized */
	if (!retprobe)
		uprobe_regs_trigger();

	uprobe_regs(&before, &after);

	pp = (unsigned long *) &skel->bss->regs;
	cnt = sizeof(before)/sizeof(*pb);
@@ -126,7 +142,7 @@ static void test_uretprobe_regs_equal(void)
		unsigned int offset = i * sizeof(unsigned long);

		/*
		 * Check register before and after uretprobe_regs_trigger call
		 * Check register before and after uprobe_regs_trigger call
		 * that triggers the uretprobe.
		 */
		switch (offset) {
@@ -140,7 +156,7 @@ static void test_uretprobe_regs_equal(void)

		/*
		 * Check register seen from bpf program and register after
		 * uretprobe_regs_trigger call
		 * uprobe_regs_trigger call (with rax exception, check below).
		 */
		switch (offset) {
		/*
@@ -153,6 +169,15 @@ static void test_uretprobe_regs_equal(void)
		case offsetof(struct pt_regs, rsp):
		case offsetof(struct pt_regs, ss):
			break;
		/*
		 * uprobe does not see return value in rax, it needs to see the
		 * original (before) rax value
		 */
		case offsetof(struct pt_regs, rax):
			if (!retprobe) {
				ASSERT_EQ(pp[i], pb[i], "uprobe rax prog-before value check");
				break;
			}
		default:
			if (!ASSERT_EQ(pp[i], pa[i], "register prog-after value check"))
				fprintf(stdout, "failed register offset %u\n", offset);
@@ -190,13 +215,13 @@ static void test_uretprobe_regs_change(void)
	unsigned long cnt = sizeof(before)/sizeof(*pb);
	unsigned int i, err, offset;

	offset = get_uprobe_offset(uretprobe_regs_trigger);
	offset = get_uprobe_offset(uprobe_regs_trigger);

	err = write_bpf_testmod_uprobe(offset);
	if (!ASSERT_OK(err, "register_uprobe"))
		return;

	uretprobe_regs(&before, &after);
	uprobe_regs(&before, &after);

	err = write_bpf_testmod_uprobe(0);
	if (!ASSERT_OK(err, "unregister_uprobe"))
@@ -616,7 +641,8 @@ static void test_uretprobe_shadow_stack(void)
	/* Run all the tests with shadow stack in place. */
	shstk_is_enabled = true;

	test_uretprobe_regs_equal();
	test_uprobe_regs_equal(false);
	test_uprobe_regs_equal(true);
	test_uretprobe_regs_change();
	test_uretprobe_syscall_call();

@@ -772,7 +798,7 @@ static void test_uprobe_sigill(void)
static void __test_uprobe_syscall(void)
{
	if (test__start_subtest("uretprobe_regs_equal"))
		test_uretprobe_regs_equal();
		test_uprobe_regs_equal(true);
	if (test__start_subtest("uretprobe_regs_change"))
		test_uretprobe_regs_change();
	if (test__start_subtest("uretprobe_syscall_call"))
@@ -791,6 +817,8 @@ static void __test_uprobe_syscall(void)
		test_uprobe_race();
	if (test__start_subtest("uprobe_sigill"))
		test_uprobe_sigill();
	if (test__start_subtest("uprobe_regs_equal"))
		test_uprobe_regs_equal(false);
}
#else
static void __test_uprobe_syscall(void)
+2 −2
Original line number Diff line number Diff line
@@ -7,8 +7,8 @@ struct pt_regs regs;

char _license[] SEC("license") = "GPL";

SEC("uretprobe//proc/self/exe:uretprobe_regs_trigger")
int uretprobe(struct pt_regs *ctx)
SEC("uprobe")
int probe(struct pt_regs *ctx)
{
	__builtin_memcpy(&regs, ctx, sizeof(regs));
	return 0;