xref: /linux/tools/testing/selftests/bpf/prog_tests/uprobe.c (revision 07fdad3a93756b872da7b53647715c48d0f4a2d0)
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 
8 static 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 
26 static 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 
37 static 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 
92 cleanup:
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 
106 static __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 
145 static 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 
189 cleanup:
190 	test_uprobe__destroy(skel);
191 }
192 
193 static noinline unsigned long uprobe_regs_change_ip_1(void)
194 {
195 	return 0xc0ffee;
196 }
197 
198 static noinline unsigned long uprobe_regs_change_ip_2(void)
199 {
200 	return 0xdeadbeef;
201 }
202 
203 static 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 
229 cleanup:
230 	test_uprobe__destroy(skel);
231 }
232 
233 static 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
241 static void test_uprobe_regs_change(void) { }
242 #endif
243 
244 void test_uprobe(void)
245 {
246 	if (test__start_subtest("attach"))
247 		test_uprobe_attach();
248 	test_uprobe_regs_change();
249 }
250