Linux kernel mirror (for testing)
git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel
os
linux
1// SPDX-License-Identifier: GPL-2.0
2/* Copyright (c) 2023 Hengqi Chen */
3
4#include <test_progs.h>
5#include <asm/ptrace.h>
6#include "test_uprobe.skel.h"
7
8static FILE *urand_spawn(int *pid)
9{
10 FILE *f;
11
12 /* urandom_read's stdout is wired into f */
13 f = popen("./urandom_read 1 report-pid", "r");
14 if (!f)
15 return NULL;
16
17 if (fscanf(f, "%d", pid) != 1) {
18 pclose(f);
19 errno = EINVAL;
20 return NULL;
21 }
22
23 return f;
24}
25
26static int urand_trigger(FILE **urand_pipe)
27{
28 int exit_code;
29
30 /* pclose() waits for child process to exit and returns their exit code */
31 exit_code = pclose(*urand_pipe);
32 *urand_pipe = NULL;
33
34 return exit_code;
35}
36
37static void test_uprobe_attach(void)
38{
39 LIBBPF_OPTS(bpf_uprobe_opts, uprobe_opts);
40 struct test_uprobe *skel;
41 FILE *urand_pipe = NULL;
42 int urand_pid = 0, err;
43
44 skel = test_uprobe__open_and_load();
45 if (!ASSERT_OK_PTR(skel, "skel_open"))
46 return;
47
48 urand_pipe = urand_spawn(&urand_pid);
49 if (!ASSERT_OK_PTR(urand_pipe, "urand_spawn"))
50 goto cleanup;
51
52 skel->bss->my_pid = urand_pid;
53
54 /* Manual attach uprobe to urandlib_api
55 * There are two `urandlib_api` symbols in .dynsym section:
56 * - urandlib_api@LIBURANDOM_READ_1.0.0
57 * - urandlib_api@@LIBURANDOM_READ_2.0.0
58 * Both are global bind and would cause a conflict if user
59 * specify the symbol name without a version suffix
60 */
61 uprobe_opts.func_name = "urandlib_api";
62 skel->links.test4 = bpf_program__attach_uprobe_opts(skel->progs.test4,
63 urand_pid,
64 "./liburandom_read.so",
65 0 /* offset */,
66 &uprobe_opts);
67 if (!ASSERT_ERR_PTR(skel->links.test4, "urandlib_api_attach_conflict"))
68 goto cleanup;
69
70 uprobe_opts.func_name = "urandlib_api@LIBURANDOM_READ_1.0.0";
71 skel->links.test4 = bpf_program__attach_uprobe_opts(skel->progs.test4,
72 urand_pid,
73 "./liburandom_read.so",
74 0 /* offset */,
75 &uprobe_opts);
76 if (!ASSERT_OK_PTR(skel->links.test4, "urandlib_api_attach_ok"))
77 goto cleanup;
78
79 /* Auto attach 3 u[ret]probes to urandlib_api_sameoffset */
80 err = test_uprobe__attach(skel);
81 if (!ASSERT_OK(err, "skel_attach"))
82 goto cleanup;
83
84 /* trigger urandom_read */
85 ASSERT_OK(urand_trigger(&urand_pipe), "urand_exit_code");
86
87 ASSERT_EQ(skel->bss->test1_result, 1, "urandlib_api_sameoffset");
88 ASSERT_EQ(skel->bss->test2_result, 1, "urandlib_api_sameoffset@v1");
89 ASSERT_EQ(skel->bss->test3_result, 3, "urandlib_api_sameoffset@@v2");
90 ASSERT_EQ(skel->bss->test4_result, 1, "urandlib_api");
91
92cleanup:
93 if (urand_pipe)
94 pclose(urand_pipe);
95 test_uprobe__destroy(skel);
96}
97
98#ifdef __x86_64__
99__naked __maybe_unused unsigned long uprobe_regs_change_trigger(void)
100{
101 asm volatile (
102 "ret\n"
103 );
104}
105
106static __naked void uprobe_regs_change(struct pt_regs *before, struct pt_regs *after)
107{
108 asm volatile (
109 "movq %r11, 48(%rdi)\n"
110 "movq %r10, 56(%rdi)\n"
111 "movq %r9, 64(%rdi)\n"
112 "movq %r8, 72(%rdi)\n"
113 "movq %rax, 80(%rdi)\n"
114 "movq %rcx, 88(%rdi)\n"
115 "movq %rdx, 96(%rdi)\n"
116 "movq %rsi, 104(%rdi)\n"
117 "movq %rdi, 112(%rdi)\n"
118
119 /* save 2nd argument */
120 "pushq %rsi\n"
121 "call uprobe_regs_change_trigger\n"
122
123 /* save return value and load 2nd argument pointer to rax */
124 "pushq %rax\n"
125 "movq 8(%rsp), %rax\n"
126
127 "movq %r11, 48(%rax)\n"
128 "movq %r10, 56(%rax)\n"
129 "movq %r9, 64(%rax)\n"
130 "movq %r8, 72(%rax)\n"
131 "movq %rcx, 88(%rax)\n"
132 "movq %rdx, 96(%rax)\n"
133 "movq %rsi, 104(%rax)\n"
134 "movq %rdi, 112(%rax)\n"
135
136 /* restore return value and 2nd argument */
137 "pop %rax\n"
138 "pop %rsi\n"
139
140 "movq %rax, 80(%rsi)\n"
141 "ret\n"
142 );
143}
144
145static void regs_common(void)
146{
147 struct pt_regs before = {}, after = {}, expected = {
148 .rax = 0xc0ffe,
149 .rcx = 0xbad,
150 .rdx = 0xdead,
151 .r8 = 0x8,
152 .r9 = 0x9,
153 .r10 = 0x10,
154 .r11 = 0x11,
155 .rdi = 0x12,
156 .rsi = 0x13,
157 };
158 LIBBPF_OPTS(bpf_uprobe_opts, uprobe_opts);
159 struct test_uprobe *skel;
160
161 skel = test_uprobe__open_and_load();
162 if (!ASSERT_OK_PTR(skel, "skel_open"))
163 return;
164
165 skel->bss->my_pid = getpid();
166 skel->bss->regs = expected;
167
168 uprobe_opts.func_name = "uprobe_regs_change_trigger";
169 skel->links.test_regs_change = bpf_program__attach_uprobe_opts(skel->progs.test_regs_change,
170 -1,
171 "/proc/self/exe",
172 0 /* offset */,
173 &uprobe_opts);
174 if (!ASSERT_OK_PTR(skel->links.test_regs_change, "bpf_program__attach_uprobe_opts"))
175 goto cleanup;
176
177 uprobe_regs_change(&before, &after);
178
179 ASSERT_EQ(after.rax, expected.rax, "ax");
180 ASSERT_EQ(after.rcx, expected.rcx, "cx");
181 ASSERT_EQ(after.rdx, expected.rdx, "dx");
182 ASSERT_EQ(after.r8, expected.r8, "r8");
183 ASSERT_EQ(after.r9, expected.r9, "r9");
184 ASSERT_EQ(after.r10, expected.r10, "r10");
185 ASSERT_EQ(after.r11, expected.r11, "r11");
186 ASSERT_EQ(after.rdi, expected.rdi, "rdi");
187 ASSERT_EQ(after.rsi, expected.rsi, "rsi");
188
189cleanup:
190 test_uprobe__destroy(skel);
191}
192
193static noinline unsigned long uprobe_regs_change_ip_1(void)
194{
195 return 0xc0ffee;
196}
197
198static noinline unsigned long uprobe_regs_change_ip_2(void)
199{
200 return 0xdeadbeef;
201}
202
203static void regs_ip(void)
204{
205 LIBBPF_OPTS(bpf_uprobe_opts, uprobe_opts);
206 struct test_uprobe *skel;
207 unsigned long ret;
208
209 skel = test_uprobe__open_and_load();
210 if (!ASSERT_OK_PTR(skel, "skel_open"))
211 return;
212
213 skel->bss->my_pid = getpid();
214 skel->bss->ip = (unsigned long) uprobe_regs_change_ip_2;
215
216 uprobe_opts.func_name = "uprobe_regs_change_ip_1";
217 skel->links.test_regs_change_ip = bpf_program__attach_uprobe_opts(
218 skel->progs.test_regs_change_ip,
219 -1,
220 "/proc/self/exe",
221 0 /* offset */,
222 &uprobe_opts);
223 if (!ASSERT_OK_PTR(skel->links.test_regs_change_ip, "bpf_program__attach_uprobe_opts"))
224 goto cleanup;
225
226 ret = uprobe_regs_change_ip_1();
227 ASSERT_EQ(ret, 0xdeadbeef, "ret");
228
229cleanup:
230 test_uprobe__destroy(skel);
231}
232
233static void test_uprobe_regs_change(void)
234{
235 if (test__start_subtest("regs_change_common"))
236 regs_common();
237 if (test__start_subtest("regs_change_ip"))
238 regs_ip();
239}
240#else
241static void test_uprobe_regs_change(void) { }
242#endif
243
244void test_uprobe(void)
245{
246 if (test__start_subtest("attach"))
247 test_uprobe_attach();
248 test_uprobe_regs_change();
249}