Skip to content

Commit 3e8e257

Browse files
olsajirimhiramat
authored andcommitted
selftests/bpf: Add uretprobe syscall test for regs integrity
Add uretprobe syscall test that compares register values before and after the uretprobe is hit. It also compares the register values seen from attached bpf program. Link: https://lore.kernel.org/all/[email protected]/ Acked-by: Andrii Nakryiko <[email protected]> Reviewed-by: Masami Hiramatsu (Google) <[email protected]> Signed-off-by: Jiri Olsa <[email protected]> Signed-off-by: Masami Hiramatsu (Google) <[email protected]>
1 parent 29edd8b commit 3e8e257

File tree

3 files changed

+182
-0
lines changed

3 files changed

+182
-0
lines changed

tools/include/linux/compiler.h

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -62,6 +62,10 @@
6262
#define __nocf_check __attribute__((nocf_check))
6363
#endif
6464

65+
#ifndef __naked
66+
#define __naked __attribute__((__naked__))
67+
#endif
68+
6569
/* Are two types/vars the same type (ignoring qualifiers)? */
6670
#ifndef __same_type
6771
# define __same_type(a, b) __builtin_types_compatible_p(typeof(a), typeof(b))
Lines changed: 163 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,163 @@
1+
// SPDX-License-Identifier: GPL-2.0
2+
3+
#include <test_progs.h>
4+
5+
#ifdef __x86_64__
6+
7+
#include <unistd.h>
8+
#include <asm/ptrace.h>
9+
#include <linux/compiler.h>
10+
#include "uprobe_syscall.skel.h"
11+
12+
__naked unsigned long uretprobe_regs_trigger(void)
13+
{
14+
asm volatile (
15+
"movq $0xdeadbeef, %rax\n"
16+
"ret\n"
17+
);
18+
}
19+
20+
__naked void uretprobe_regs(struct pt_regs *before, struct pt_regs *after)
21+
{
22+
asm volatile (
23+
"movq %r15, 0(%rdi)\n"
24+
"movq %r14, 8(%rdi)\n"
25+
"movq %r13, 16(%rdi)\n"
26+
"movq %r12, 24(%rdi)\n"
27+
"movq %rbp, 32(%rdi)\n"
28+
"movq %rbx, 40(%rdi)\n"
29+
"movq %r11, 48(%rdi)\n"
30+
"movq %r10, 56(%rdi)\n"
31+
"movq %r9, 64(%rdi)\n"
32+
"movq %r8, 72(%rdi)\n"
33+
"movq %rax, 80(%rdi)\n"
34+
"movq %rcx, 88(%rdi)\n"
35+
"movq %rdx, 96(%rdi)\n"
36+
"movq %rsi, 104(%rdi)\n"
37+
"movq %rdi, 112(%rdi)\n"
38+
"movq $0, 120(%rdi)\n" /* orig_rax */
39+
"movq $0, 128(%rdi)\n" /* rip */
40+
"movq $0, 136(%rdi)\n" /* cs */
41+
"pushf\n"
42+
"pop %rax\n"
43+
"movq %rax, 144(%rdi)\n" /* eflags */
44+
"movq %rsp, 152(%rdi)\n" /* rsp */
45+
"movq $0, 160(%rdi)\n" /* ss */
46+
47+
/* save 2nd argument */
48+
"pushq %rsi\n"
49+
"call uretprobe_regs_trigger\n"
50+
51+
/* save return value and load 2nd argument pointer to rax */
52+
"pushq %rax\n"
53+
"movq 8(%rsp), %rax\n"
54+
55+
"movq %r15, 0(%rax)\n"
56+
"movq %r14, 8(%rax)\n"
57+
"movq %r13, 16(%rax)\n"
58+
"movq %r12, 24(%rax)\n"
59+
"movq %rbp, 32(%rax)\n"
60+
"movq %rbx, 40(%rax)\n"
61+
"movq %r11, 48(%rax)\n"
62+
"movq %r10, 56(%rax)\n"
63+
"movq %r9, 64(%rax)\n"
64+
"movq %r8, 72(%rax)\n"
65+
"movq %rcx, 88(%rax)\n"
66+
"movq %rdx, 96(%rax)\n"
67+
"movq %rsi, 104(%rax)\n"
68+
"movq %rdi, 112(%rax)\n"
69+
"movq $0, 120(%rax)\n" /* orig_rax */
70+
"movq $0, 128(%rax)\n" /* rip */
71+
"movq $0, 136(%rax)\n" /* cs */
72+
73+
/* restore return value and 2nd argument */
74+
"pop %rax\n"
75+
"pop %rsi\n"
76+
77+
"movq %rax, 80(%rsi)\n"
78+
79+
"pushf\n"
80+
"pop %rax\n"
81+
82+
"movq %rax, 144(%rsi)\n" /* eflags */
83+
"movq %rsp, 152(%rsi)\n" /* rsp */
84+
"movq $0, 160(%rsi)\n" /* ss */
85+
"ret\n"
86+
);
87+
}
88+
89+
static void test_uretprobe_regs_equal(void)
90+
{
91+
struct uprobe_syscall *skel = NULL;
92+
struct pt_regs before = {}, after = {};
93+
unsigned long *pb = (unsigned long *) &before;
94+
unsigned long *pa = (unsigned long *) &after;
95+
unsigned long *pp;
96+
unsigned int i, cnt;
97+
int err;
98+
99+
skel = uprobe_syscall__open_and_load();
100+
if (!ASSERT_OK_PTR(skel, "uprobe_syscall__open_and_load"))
101+
goto cleanup;
102+
103+
err = uprobe_syscall__attach(skel);
104+
if (!ASSERT_OK(err, "uprobe_syscall__attach"))
105+
goto cleanup;
106+
107+
uretprobe_regs(&before, &after);
108+
109+
pp = (unsigned long *) &skel->bss->regs;
110+
cnt = sizeof(before)/sizeof(*pb);
111+
112+
for (i = 0; i < cnt; i++) {
113+
unsigned int offset = i * sizeof(unsigned long);
114+
115+
/*
116+
* Check register before and after uretprobe_regs_trigger call
117+
* that triggers the uretprobe.
118+
*/
119+
switch (offset) {
120+
case offsetof(struct pt_regs, rax):
121+
ASSERT_EQ(pa[i], 0xdeadbeef, "return value");
122+
break;
123+
default:
124+
if (!ASSERT_EQ(pb[i], pa[i], "register before-after value check"))
125+
fprintf(stdout, "failed register offset %u\n", offset);
126+
}
127+
128+
/*
129+
* Check register seen from bpf program and register after
130+
* uretprobe_regs_trigger call
131+
*/
132+
switch (offset) {
133+
/*
134+
* These values will be different (not set in uretprobe_regs),
135+
* we don't care.
136+
*/
137+
case offsetof(struct pt_regs, orig_rax):
138+
case offsetof(struct pt_regs, rip):
139+
case offsetof(struct pt_regs, cs):
140+
case offsetof(struct pt_regs, rsp):
141+
case offsetof(struct pt_regs, ss):
142+
break;
143+
default:
144+
if (!ASSERT_EQ(pp[i], pa[i], "register prog-after value check"))
145+
fprintf(stdout, "failed register offset %u\n", offset);
146+
}
147+
}
148+
149+
cleanup:
150+
uprobe_syscall__destroy(skel);
151+
}
152+
#else
153+
static void test_uretprobe_regs_equal(void)
154+
{
155+
test__skip();
156+
}
157+
#endif
158+
159+
void test_uprobe_syscall(void)
160+
{
161+
if (test__start_subtest("uretprobe_regs_equal"))
162+
test_uretprobe_regs_equal();
163+
}
Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
// SPDX-License-Identifier: GPL-2.0
2+
#include "vmlinux.h"
3+
#include <bpf/bpf_helpers.h>
4+
#include <string.h>
5+
6+
struct pt_regs regs;
7+
8+
char _license[] SEC("license") = "GPL";
9+
10+
SEC("uretprobe//proc/self/exe:uretprobe_regs_trigger")
11+
int uretprobe(struct pt_regs *ctx)
12+
{
13+
__builtin_memcpy(&regs, ctx, sizeof(regs));
14+
return 0;
15+
}

0 commit comments

Comments
 (0)