Commit | Line | Data |
---|---|---|
04235c00 AL |
1 | #define _GNU_SOURCE |
2 | ||
3 | #include <sys/ptrace.h> | |
4 | #include <sys/types.h> | |
5 | #include <sys/wait.h> | |
6 | #include <sys/syscall.h> | |
7 | #include <sys/user.h> | |
8 | #include <unistd.h> | |
9 | #include <errno.h> | |
10 | #include <stddef.h> | |
11 | #include <stdio.h> | |
12 | #include <err.h> | |
13 | #include <string.h> | |
14 | #include <asm/ptrace-abi.h> | |
15 | #include <sys/auxv.h> | |
16 | ||
17 | /* Bitness-agnostic defines for user_regs_struct fields. */ | |
18 | #ifdef __x86_64__ | |
19 | # define user_syscall_nr orig_rax | |
20 | # define user_arg0 rdi | |
21 | # define user_arg1 rsi | |
22 | # define user_arg2 rdx | |
23 | # define user_arg3 r10 | |
24 | # define user_arg4 r8 | |
25 | # define user_arg5 r9 | |
26 | # define user_ip rip | |
27 | # define user_ax rax | |
28 | #else | |
29 | # define user_syscall_nr orig_eax | |
30 | # define user_arg0 ebx | |
31 | # define user_arg1 ecx | |
32 | # define user_arg2 edx | |
33 | # define user_arg3 esi | |
34 | # define user_arg4 edi | |
35 | # define user_arg5 ebp | |
36 | # define user_ip eip | |
37 | # define user_ax eax | |
38 | #endif | |
39 | ||
40 | static int nerrs = 0; | |
41 | ||
42 | struct syscall_args32 { | |
43 | uint32_t nr, arg0, arg1, arg2, arg3, arg4, arg5; | |
44 | }; | |
45 | ||
46 | #ifdef __i386__ | |
47 | extern void sys32_helper(struct syscall_args32 *, void *); | |
48 | extern void int80_and_ret(void); | |
49 | #endif | |
50 | ||
51 | /* | |
52 | * Helper to invoke int80 with controlled regs and capture the final regs. | |
53 | */ | |
54 | static void do_full_int80(struct syscall_args32 *args) | |
55 | { | |
56 | #ifdef __x86_64__ | |
57 | register unsigned long bp asm("bp") = args->arg5; | |
58 | asm volatile ("int $0x80" | |
59 | : "+a" (args->nr), | |
60 | "+b" (args->arg0), "+c" (args->arg1), "+d" (args->arg2), | |
61 | "+S" (args->arg3), "+D" (args->arg4), "+r" (bp)); | |
62 | args->arg5 = bp; | |
63 | #else | |
64 | sys32_helper(args, int80_and_ret); | |
65 | #endif | |
66 | } | |
67 | ||
68 | #ifdef __i386__ | |
69 | static void (*vsyscall32)(void); | |
70 | ||
71 | /* | |
72 | * Nasty helper to invoke AT_SYSINFO (i.e. __kernel_vsyscall) with | |
73 | * controlled regs and capture the final regs. This is so nasty that it | |
74 | * crashes my copy of gdb :) | |
75 | */ | |
76 | static void do_full_vsyscall32(struct syscall_args32 *args) | |
77 | { | |
78 | sys32_helper(args, vsyscall32); | |
79 | } | |
80 | #endif | |
81 | ||
82 | static siginfo_t wait_trap(pid_t chld) | |
83 | { | |
84 | siginfo_t si; | |
85 | if (waitid(P_PID, chld, &si, WEXITED|WSTOPPED) != 0) | |
86 | err(1, "waitid"); | |
87 | if (si.si_pid != chld) | |
88 | errx(1, "got unexpected pid in event\n"); | |
89 | if (si.si_code != CLD_TRAPPED) | |
90 | errx(1, "got unexpected event type %d\n", si.si_code); | |
91 | return si; | |
92 | } | |
93 | ||
94 | static void sethandler(int sig, void (*handler)(int, siginfo_t *, void *), | |
95 | int flags) | |
96 | { | |
97 | struct sigaction sa; | |
98 | memset(&sa, 0, sizeof(sa)); | |
99 | sa.sa_sigaction = handler; | |
100 | sa.sa_flags = SA_SIGINFO | flags; | |
101 | sigemptyset(&sa.sa_mask); | |
102 | if (sigaction(sig, &sa, 0)) | |
103 | err(1, "sigaction"); | |
104 | } | |
105 | ||
40361343 AL |
106 | static void setsigign(int sig, int flags) |
107 | { | |
108 | struct sigaction sa; | |
109 | memset(&sa, 0, sizeof(sa)); | |
110 | sa.sa_sigaction = (void *)SIG_IGN; | |
111 | sa.sa_flags = flags; | |
112 | sigemptyset(&sa.sa_mask); | |
113 | if (sigaction(sig, &sa, 0)) | |
114 | err(1, "sigaction"); | |
115 | } | |
116 | ||
04235c00 AL |
117 | static void clearhandler(int sig) |
118 | { | |
119 | struct sigaction sa; | |
120 | memset(&sa, 0, sizeof(sa)); | |
121 | sa.sa_handler = SIG_DFL; | |
122 | sigemptyset(&sa.sa_mask); | |
123 | if (sigaction(sig, &sa, 0)) | |
124 | err(1, "sigaction"); | |
125 | } | |
126 | ||
127 | #ifdef __x86_64__ | |
128 | # define REG_BP REG_RBP | |
129 | #else | |
130 | # define REG_BP REG_EBP | |
131 | #endif | |
132 | ||
133 | static void empty_handler(int sig, siginfo_t *si, void *ctx_void) | |
134 | { | |
135 | } | |
136 | ||
137 | static void test_sys32_regs(void (*do_syscall)(struct syscall_args32 *)) | |
138 | { | |
139 | struct syscall_args32 args = { | |
140 | .nr = 224, /* gettid */ | |
141 | .arg0 = 10, .arg1 = 11, .arg2 = 12, | |
142 | .arg3 = 13, .arg4 = 14, .arg5 = 15, | |
143 | }; | |
144 | ||
145 | do_syscall(&args); | |
146 | ||
147 | if (args.nr != getpid() || | |
148 | args.arg0 != 10 || args.arg1 != 11 || args.arg2 != 12 || | |
149 | args.arg3 != 13 || args.arg4 != 14 || args.arg5 != 15) { | |
1d723de7 | 150 | printf("[FAIL]\tgetpid() failed to preserve regs\n"); |
04235c00 AL |
151 | nerrs++; |
152 | } else { | |
153 | printf("[OK]\tgetpid() preserves regs\n"); | |
154 | } | |
155 | ||
156 | sethandler(SIGUSR1, empty_handler, 0); | |
157 | ||
158 | args.nr = 37; /* kill */ | |
159 | args.arg0 = getpid(); | |
160 | args.arg1 = SIGUSR1; | |
161 | do_syscall(&args); | |
162 | if (args.nr != 0 || | |
163 | args.arg0 != getpid() || args.arg1 != SIGUSR1 || args.arg2 != 12 || | |
164 | args.arg3 != 13 || args.arg4 != 14 || args.arg5 != 15) { | |
1d723de7 | 165 | printf("[FAIL]\tkill(getpid(), SIGUSR1) failed to preserve regs\n"); |
04235c00 AL |
166 | nerrs++; |
167 | } else { | |
168 | printf("[OK]\tkill(getpid(), SIGUSR1) preserves regs\n"); | |
169 | } | |
170 | clearhandler(SIGUSR1); | |
171 | } | |
172 | ||
173 | static void test_ptrace_syscall_restart(void) | |
174 | { | |
175 | printf("[RUN]\tptrace-induced syscall restart\n"); | |
176 | pid_t chld = fork(); | |
177 | if (chld < 0) | |
178 | err(1, "fork"); | |
179 | ||
180 | if (chld == 0) { | |
181 | if (ptrace(PTRACE_TRACEME, 0, 0, 0) != 0) | |
182 | err(1, "PTRACE_TRACEME"); | |
183 | ||
184 | printf("\tChild will make one syscall\n"); | |
185 | raise(SIGSTOP); | |
186 | ||
187 | syscall(SYS_gettid, 10, 11, 12, 13, 14, 15); | |
188 | _exit(0); | |
189 | } | |
190 | ||
191 | int status; | |
192 | ||
193 | /* Wait for SIGSTOP. */ | |
194 | if (waitpid(chld, &status, 0) != chld || !WIFSTOPPED(status)) | |
195 | err(1, "waitpid"); | |
196 | ||
197 | struct user_regs_struct regs; | |
198 | ||
199 | printf("[RUN]\tSYSEMU\n"); | |
200 | if (ptrace(PTRACE_SYSEMU, chld, 0, 0) != 0) | |
adcfd23e | 201 | err(1, "PTRACE_SYSEMU"); |
04235c00 AL |
202 | wait_trap(chld); |
203 | ||
204 | if (ptrace(PTRACE_GETREGS, chld, 0, ®s) != 0) | |
205 | err(1, "PTRACE_GETREGS"); | |
206 | ||
207 | if (regs.user_syscall_nr != SYS_gettid || | |
208 | regs.user_arg0 != 10 || regs.user_arg1 != 11 || | |
209 | regs.user_arg2 != 12 || regs.user_arg3 != 13 || | |
210 | regs.user_arg4 != 14 || regs.user_arg5 != 15) { | |
211 | printf("[FAIL]\tInitial args are wrong (nr=%lu, args=%lu %lu %lu %lu %lu %lu)\n", (unsigned long)regs.user_syscall_nr, (unsigned long)regs.user_arg0, (unsigned long)regs.user_arg1, (unsigned long)regs.user_arg2, (unsigned long)regs.user_arg3, (unsigned long)regs.user_arg4, (unsigned long)regs.user_arg5); | |
212 | nerrs++; | |
213 | } else { | |
214 | printf("[OK]\tInitial nr and args are correct\n"); | |
215 | } | |
216 | ||
217 | printf("[RUN]\tRestart the syscall (ip = 0x%lx)\n", | |
218 | (unsigned long)regs.user_ip); | |
219 | ||
220 | /* | |
221 | * This does exactly what it appears to do if syscall is int80 or | |
222 | * SYSCALL64. For SYSCALL32 or SYSENTER, though, this is highly | |
223 | * magical. It needs to work so that ptrace and syscall restart | |
224 | * work as expected. | |
225 | */ | |
226 | regs.user_ax = regs.user_syscall_nr; | |
227 | regs.user_ip -= 2; | |
228 | if (ptrace(PTRACE_SETREGS, chld, 0, ®s) != 0) | |
229 | err(1, "PTRACE_SETREGS"); | |
230 | ||
231 | if (ptrace(PTRACE_SYSEMU, chld, 0, 0) != 0) | |
adcfd23e | 232 | err(1, "PTRACE_SYSEMU"); |
04235c00 AL |
233 | wait_trap(chld); |
234 | ||
235 | if (ptrace(PTRACE_GETREGS, chld, 0, ®s) != 0) | |
236 | err(1, "PTRACE_GETREGS"); | |
237 | ||
238 | if (regs.user_syscall_nr != SYS_gettid || | |
239 | regs.user_arg0 != 10 || regs.user_arg1 != 11 || | |
240 | regs.user_arg2 != 12 || regs.user_arg3 != 13 || | |
241 | regs.user_arg4 != 14 || regs.user_arg5 != 15) { | |
242 | printf("[FAIL]\tRestart nr or args are wrong (nr=%lu, args=%lu %lu %lu %lu %lu %lu)\n", (unsigned long)regs.user_syscall_nr, (unsigned long)regs.user_arg0, (unsigned long)regs.user_arg1, (unsigned long)regs.user_arg2, (unsigned long)regs.user_arg3, (unsigned long)regs.user_arg4, (unsigned long)regs.user_arg5); | |
243 | nerrs++; | |
244 | } else { | |
245 | printf("[OK]\tRestarted nr and args are correct\n"); | |
246 | } | |
247 | ||
248 | printf("[RUN]\tChange nr and args and restart the syscall (ip = 0x%lx)\n", | |
249 | (unsigned long)regs.user_ip); | |
250 | ||
251 | regs.user_ax = SYS_getpid; | |
252 | regs.user_arg0 = 20; | |
253 | regs.user_arg1 = 21; | |
254 | regs.user_arg2 = 22; | |
255 | regs.user_arg3 = 23; | |
256 | regs.user_arg4 = 24; | |
257 | regs.user_arg5 = 25; | |
258 | regs.user_ip -= 2; | |
259 | ||
260 | if (ptrace(PTRACE_SETREGS, chld, 0, ®s) != 0) | |
261 | err(1, "PTRACE_SETREGS"); | |
262 | ||
263 | if (ptrace(PTRACE_SYSEMU, chld, 0, 0) != 0) | |
adcfd23e | 264 | err(1, "PTRACE_SYSEMU"); |
04235c00 AL |
265 | wait_trap(chld); |
266 | ||
267 | if (ptrace(PTRACE_GETREGS, chld, 0, ®s) != 0) | |
268 | err(1, "PTRACE_GETREGS"); | |
269 | ||
270 | if (regs.user_syscall_nr != SYS_getpid || | |
271 | regs.user_arg0 != 20 || regs.user_arg1 != 21 || regs.user_arg2 != 22 || | |
272 | regs.user_arg3 != 23 || regs.user_arg4 != 24 || regs.user_arg5 != 25) { | |
273 | printf("[FAIL]\tRestart nr or args are wrong (nr=%lu, args=%lu %lu %lu %lu %lu %lu)\n", (unsigned long)regs.user_syscall_nr, (unsigned long)regs.user_arg0, (unsigned long)regs.user_arg1, (unsigned long)regs.user_arg2, (unsigned long)regs.user_arg3, (unsigned long)regs.user_arg4, (unsigned long)regs.user_arg5); | |
274 | nerrs++; | |
275 | } else { | |
276 | printf("[OK]\tReplacement nr and args are correct\n"); | |
277 | } | |
278 | ||
279 | if (ptrace(PTRACE_CONT, chld, 0, 0) != 0) | |
280 | err(1, "PTRACE_CONT"); | |
281 | if (waitpid(chld, &status, 0) != chld) | |
282 | err(1, "waitpid"); | |
283 | if (!WIFEXITED(status) || WEXITSTATUS(status) != 0) { | |
284 | printf("[FAIL]\tChild failed\n"); | |
285 | nerrs++; | |
286 | } else { | |
287 | printf("[OK]\tChild exited cleanly\n"); | |
288 | } | |
289 | } | |
290 | ||
40361343 AL |
291 | static void test_restart_under_ptrace(void) |
292 | { | |
293 | printf("[RUN]\tkernel syscall restart under ptrace\n"); | |
294 | pid_t chld = fork(); | |
295 | if (chld < 0) | |
296 | err(1, "fork"); | |
297 | ||
298 | if (chld == 0) { | |
299 | if (ptrace(PTRACE_TRACEME, 0, 0, 0) != 0) | |
300 | err(1, "PTRACE_TRACEME"); | |
301 | ||
302 | printf("\tChild will take a nap until signaled\n"); | |
303 | setsigign(SIGUSR1, SA_RESTART); | |
304 | raise(SIGSTOP); | |
305 | ||
306 | syscall(SYS_pause, 0, 0, 0, 0, 0, 0); | |
307 | _exit(0); | |
308 | } | |
309 | ||
310 | int status; | |
311 | ||
312 | /* Wait for SIGSTOP. */ | |
313 | if (waitpid(chld, &status, 0) != chld || !WIFSTOPPED(status)) | |
314 | err(1, "waitpid"); | |
315 | ||
316 | struct user_regs_struct regs; | |
317 | ||
318 | printf("[RUN]\tSYSCALL\n"); | |
319 | if (ptrace(PTRACE_SYSCALL, chld, 0, 0) != 0) | |
320 | err(1, "PTRACE_SYSCALL"); | |
321 | wait_trap(chld); | |
322 | ||
323 | /* We should be stopped at pause(2) entry. */ | |
324 | ||
325 | if (ptrace(PTRACE_GETREGS, chld, 0, ®s) != 0) | |
326 | err(1, "PTRACE_GETREGS"); | |
327 | ||
328 | if (regs.user_syscall_nr != SYS_pause || | |
329 | regs.user_arg0 != 0 || regs.user_arg1 != 0 || | |
330 | regs.user_arg2 != 0 || regs.user_arg3 != 0 || | |
331 | regs.user_arg4 != 0 || regs.user_arg5 != 0) { | |
332 | printf("[FAIL]\tInitial args are wrong (nr=%lu, args=%lu %lu %lu %lu %lu %lu)\n", (unsigned long)regs.user_syscall_nr, (unsigned long)regs.user_arg0, (unsigned long)regs.user_arg1, (unsigned long)regs.user_arg2, (unsigned long)regs.user_arg3, (unsigned long)regs.user_arg4, (unsigned long)regs.user_arg5); | |
333 | nerrs++; | |
334 | } else { | |
335 | printf("[OK]\tInitial nr and args are correct\n"); | |
336 | } | |
337 | ||
338 | /* Interrupt it. */ | |
339 | kill(chld, SIGUSR1); | |
340 | ||
341 | /* Advance. We should be stopped at exit. */ | |
342 | printf("[RUN]\tSYSCALL\n"); | |
343 | if (ptrace(PTRACE_SYSCALL, chld, 0, 0) != 0) | |
344 | err(1, "PTRACE_SYSCALL"); | |
345 | wait_trap(chld); | |
346 | ||
347 | if (ptrace(PTRACE_GETREGS, chld, 0, ®s) != 0) | |
348 | err(1, "PTRACE_GETREGS"); | |
349 | ||
350 | if (regs.user_syscall_nr != SYS_pause || | |
351 | regs.user_arg0 != 0 || regs.user_arg1 != 0 || | |
352 | regs.user_arg2 != 0 || regs.user_arg3 != 0 || | |
353 | regs.user_arg4 != 0 || regs.user_arg5 != 0) { | |
354 | printf("[FAIL]\tArgs after SIGUSR1 are wrong (nr=%lu, args=%lu %lu %lu %lu %lu %lu)\n", (unsigned long)regs.user_syscall_nr, (unsigned long)regs.user_arg0, (unsigned long)regs.user_arg1, (unsigned long)regs.user_arg2, (unsigned long)regs.user_arg3, (unsigned long)regs.user_arg4, (unsigned long)regs.user_arg5); | |
355 | nerrs++; | |
356 | } else { | |
357 | printf("[OK]\tArgs after SIGUSR1 are correct (ax = %ld)\n", | |
358 | (long)regs.user_ax); | |
359 | } | |
360 | ||
361 | /* Poke the regs back in. This must not break anything. */ | |
362 | if (ptrace(PTRACE_SETREGS, chld, 0, ®s) != 0) | |
363 | err(1, "PTRACE_SETREGS"); | |
364 | ||
365 | /* Catch the (ignored) SIGUSR1. */ | |
366 | if (ptrace(PTRACE_CONT, chld, 0, 0) != 0) | |
367 | err(1, "PTRACE_CONT"); | |
368 | if (waitpid(chld, &status, 0) != chld) | |
369 | err(1, "waitpid"); | |
370 | if (!WIFSTOPPED(status)) { | |
371 | printf("[FAIL]\tChild was stopped for SIGUSR1 (status = 0x%x)\n", status); | |
372 | nerrs++; | |
373 | } else { | |
374 | printf("[OK]\tChild got SIGUSR1\n"); | |
375 | } | |
376 | ||
377 | /* The next event should be pause(2) again. */ | |
378 | printf("[RUN]\tStep again\n"); | |
379 | if (ptrace(PTRACE_SYSCALL, chld, 0, 0) != 0) | |
380 | err(1, "PTRACE_SYSCALL"); | |
381 | wait_trap(chld); | |
382 | ||
383 | /* We should be stopped at pause(2) entry. */ | |
384 | ||
385 | if (ptrace(PTRACE_GETREGS, chld, 0, ®s) != 0) | |
386 | err(1, "PTRACE_GETREGS"); | |
387 | ||
388 | if (regs.user_syscall_nr != SYS_pause || | |
389 | regs.user_arg0 != 0 || regs.user_arg1 != 0 || | |
390 | regs.user_arg2 != 0 || regs.user_arg3 != 0 || | |
391 | regs.user_arg4 != 0 || regs.user_arg5 != 0) { | |
392 | printf("[FAIL]\tpause did not restart (nr=%lu, args=%lu %lu %lu %lu %lu %lu)\n", (unsigned long)regs.user_syscall_nr, (unsigned long)regs.user_arg0, (unsigned long)regs.user_arg1, (unsigned long)regs.user_arg2, (unsigned long)regs.user_arg3, (unsigned long)regs.user_arg4, (unsigned long)regs.user_arg5); | |
393 | nerrs++; | |
394 | } else { | |
395 | printf("[OK]\tpause(2) restarted correctly\n"); | |
396 | } | |
397 | ||
398 | /* Kill it. */ | |
399 | kill(chld, SIGKILL); | |
400 | if (waitpid(chld, &status, 0) != chld) | |
401 | err(1, "waitpid"); | |
402 | } | |
403 | ||
04235c00 AL |
404 | int main() |
405 | { | |
406 | printf("[RUN]\tCheck int80 return regs\n"); | |
407 | test_sys32_regs(do_full_int80); | |
408 | ||
409 | #if defined(__i386__) && (!defined(__GLIBC__) || __GLIBC__ > 2 || __GLIBC_MINOR__ >= 16) | |
410 | vsyscall32 = (void *)getauxval(AT_SYSINFO); | |
411 | printf("[RUN]\tCheck AT_SYSINFO return regs\n"); | |
412 | test_sys32_regs(do_full_vsyscall32); | |
413 | #endif | |
414 | ||
415 | test_ptrace_syscall_restart(); | |
416 | ||
40361343 AL |
417 | test_restart_under_ptrace(); |
418 | ||
04235c00 AL |
419 | return 0; |
420 | } |