Commit | Line | Data |
---|---|---|
1da177e4 LT |
1 | /* |
2 | * Copyright (C) 2000, 2001, 2002 Jeff Dike (jdike@karaya.com) | |
3 | * Licensed under the GPL | |
4 | */ | |
5 | ||
6 | #include <stdio.h> | |
7 | #include <unistd.h> | |
8 | #include <signal.h> | |
9 | #include <sched.h> | |
10 | #include <errno.h> | |
11 | #include <stdarg.h> | |
12 | #include <stdlib.h> | |
13 | #include <setjmp.h> | |
14 | #include <sys/time.h> | |
15 | #include <sys/wait.h> | |
16 | #include <sys/mman.h> | |
17 | #include <asm/unistd.h> | |
18 | #include <asm/page.h> | |
19 | #include "user_util.h" | |
20 | #include "kern_util.h" | |
21 | #include "user.h" | |
22 | #include "process.h" | |
23 | #include "signal_kern.h" | |
24 | #include "signal_user.h" | |
25 | #include "sysdep/ptrace.h" | |
26 | #include "sysdep/sigcontext.h" | |
27 | #include "irq_user.h" | |
28 | #include "ptrace_user.h" | |
29 | #include "time_user.h" | |
30 | #include "init.h" | |
31 | #include "os.h" | |
32 | #include "uml-config.h" | |
1da177e4 LT |
33 | #include "choose-mode.h" |
34 | #include "mode.h" | |
d67b569f | 35 | #include "tempfile.h" |
1da177e4 LT |
36 | #ifdef UML_CONFIG_MODE_SKAS |
37 | #include "skas.h" | |
38 | #include "skas_ptrace.h" | |
39 | #include "registers.h" | |
40 | #endif | |
41 | ||
42 | void init_new_thread_stack(void *sig_stack, void (*usr1_handler)(int)) | |
43 | { | |
44 | int flags = 0, pages; | |
45 | ||
46 | if(sig_stack != NULL){ | |
47 | pages = (1 << UML_CONFIG_KERNEL_STACK_ORDER); | |
48 | set_sigstack(sig_stack, pages * page_size()); | |
49 | flags = SA_ONSTACK; | |
50 | } | |
51 | if(usr1_handler) set_handler(SIGUSR1, usr1_handler, flags, -1); | |
52 | } | |
53 | ||
54 | void init_new_thread_signals(int altstack) | |
55 | { | |
56 | int flags = altstack ? SA_ONSTACK : 0; | |
57 | ||
58 | set_handler(SIGSEGV, (__sighandler_t) sig_handler, flags, | |
59 | SIGUSR1, SIGIO, SIGWINCH, SIGALRM, SIGVTALRM, -1); | |
60 | set_handler(SIGTRAP, (__sighandler_t) sig_handler, flags, | |
61 | SIGUSR1, SIGIO, SIGWINCH, SIGALRM, SIGVTALRM, -1); | |
62 | set_handler(SIGFPE, (__sighandler_t) sig_handler, flags, | |
63 | SIGUSR1, SIGIO, SIGWINCH, SIGALRM, SIGVTALRM, -1); | |
64 | set_handler(SIGILL, (__sighandler_t) sig_handler, flags, | |
65 | SIGUSR1, SIGIO, SIGWINCH, SIGALRM, SIGVTALRM, -1); | |
66 | set_handler(SIGBUS, (__sighandler_t) sig_handler, flags, | |
67 | SIGUSR1, SIGIO, SIGWINCH, SIGALRM, SIGVTALRM, -1); | |
1da177e4 LT |
68 | set_handler(SIGUSR2, (__sighandler_t) sig_handler, |
69 | flags, SIGUSR1, SIGIO, SIGWINCH, SIGALRM, SIGVTALRM, -1); | |
70 | signal(SIGHUP, SIG_IGN); | |
71 | ||
72 | init_irq_signals(altstack); | |
73 | } | |
74 | ||
75 | struct tramp { | |
76 | int (*tramp)(void *); | |
77 | void *tramp_data; | |
78 | unsigned long temp_stack; | |
79 | int flags; | |
80 | int pid; | |
81 | }; | |
82 | ||
83 | /* See above for why sigkill is here */ | |
84 | ||
85 | int sigkill = SIGKILL; | |
86 | ||
87 | int outer_tramp(void *arg) | |
88 | { | |
89 | struct tramp *t; | |
90 | int sig = sigkill; | |
91 | ||
92 | t = arg; | |
93 | t->pid = clone(t->tramp, (void *) t->temp_stack + page_size()/2, | |
94 | t->flags, t->tramp_data); | |
95 | if(t->pid > 0) wait_for_stop(t->pid, SIGSTOP, PTRACE_CONT, NULL); | |
96 | kill(os_getpid(), sig); | |
97 | _exit(0); | |
98 | } | |
99 | ||
100 | int start_fork_tramp(void *thread_arg, unsigned long temp_stack, | |
101 | int clone_flags, int (*tramp)(void *)) | |
102 | { | |
103 | struct tramp arg; | |
104 | unsigned long sp; | |
105 | int new_pid, status, err; | |
106 | ||
107 | /* The trampoline will run on the temporary stack */ | |
108 | sp = stack_sp(temp_stack); | |
109 | ||
110 | clone_flags |= CLONE_FILES | SIGCHLD; | |
111 | ||
112 | arg.tramp = tramp; | |
113 | arg.tramp_data = thread_arg; | |
114 | arg.temp_stack = temp_stack; | |
115 | arg.flags = clone_flags; | |
116 | ||
117 | /* Start the process and wait for it to kill itself */ | |
118 | new_pid = clone(outer_tramp, (void *) sp, clone_flags, &arg); | |
119 | if(new_pid < 0) | |
120 | return(new_pid); | |
121 | ||
122 | CATCH_EINTR(err = waitpid(new_pid, &status, 0)); | |
123 | if(err < 0) | |
124 | panic("Waiting for outer trampoline failed - errno = %d", | |
125 | errno); | |
126 | ||
127 | if(!WIFSIGNALED(status) || (WTERMSIG(status) != SIGKILL)) | |
128 | panic("outer trampoline didn't exit with SIGKILL, " | |
129 | "status = %d", status); | |
130 | ||
131 | return(arg.pid); | |
132 | } | |
133 | ||
98fdffcc | 134 | static int ptrace_child(void) |
1da177e4 LT |
135 | { |
136 | int ret; | |
137 | int pid = os_getpid(), ppid = getppid(); | |
138 | int sc_result; | |
139 | ||
140 | if(ptrace(PTRACE_TRACEME, 0, 0, 0) < 0){ | |
141 | perror("ptrace"); | |
142 | os_kill_process(pid, 0); | |
143 | } | |
144 | os_stop_process(pid); | |
145 | ||
146 | /*This syscall will be intercepted by the parent. Don't call more than | |
147 | * once, please.*/ | |
148 | sc_result = os_getpid(); | |
149 | ||
150 | if (sc_result == pid) | |
151 | ret = 1; /*Nothing modified by the parent, we are running | |
152 | normally.*/ | |
153 | else if (sc_result == ppid) | |
154 | ret = 0; /*Expected in check_ptrace and check_sysemu when they | |
155 | succeed in modifying the stack frame*/ | |
156 | else | |
157 | ret = 2; /*Serious trouble! This could be caused by a bug in | |
158 | host 2.6 SKAS3/2.6 patch before release -V6, together | |
159 | with a bug in the UML code itself.*/ | |
160 | _exit(ret); | |
161 | } | |
162 | ||
98fdffcc | 163 | static int start_ptraced_child(void) |
1da177e4 | 164 | { |
1da177e4 LT |
165 | int pid, n, status; |
166 | ||
98fdffcc JD |
167 | pid = fork(); |
168 | if(pid == 0) | |
169 | ptrace_child(); | |
170 | ||
1da177e4 | 171 | if(pid < 0) |
98fdffcc | 172 | panic("check_ptrace : fork failed, errno = %d", errno); |
1da177e4 LT |
173 | CATCH_EINTR(n = waitpid(pid, &status, WUNTRACED)); |
174 | if(n < 0) | |
175 | panic("check_ptrace : wait failed, errno = %d", errno); | |
176 | if(!WIFSTOPPED(status) || (WSTOPSIG(status) != SIGSTOP)) | |
177 | panic("check_ptrace : expected SIGSTOP, got status = %d", | |
178 | status); | |
179 | ||
1da177e4 LT |
180 | return(pid); |
181 | } | |
182 | ||
183 | /* When testing for SYSEMU support, if it is one of the broken versions, we must | |
184 | * just avoid using sysemu, not panic, but only if SYSEMU features are broken. | |
185 | * So only for SYSEMU features we test mustpanic, while normal host features | |
186 | * must work anyway!*/ | |
98fdffcc | 187 | static int stop_ptraced_child(int pid, int exitcode, int mustexit) |
1da177e4 LT |
188 | { |
189 | int status, n, ret = 0; | |
190 | ||
191 | if(ptrace(PTRACE_CONT, pid, 0, 0) < 0) | |
98fdffcc | 192 | panic("stop_ptraced_child : ptrace failed, errno = %d", errno); |
1da177e4 LT |
193 | CATCH_EINTR(n = waitpid(pid, &status, 0)); |
194 | if(!WIFEXITED(status) || (WEXITSTATUS(status) != exitcode)) { | |
195 | int exit_with = WEXITSTATUS(status); | |
196 | if (exit_with == 2) | |
197 | printk("check_ptrace : child exited with status 2. " | |
198 | "Serious trouble happening! Try updating your " | |
199 | "host skas patch!\nDisabling SYSEMU support."); | |
200 | printk("check_ptrace : child exited with exitcode %d, while " | |
201 | "expecting %d; status 0x%x", exit_with, | |
202 | exitcode, status); | |
98fdffcc | 203 | if (mustexit) |
1da177e4 LT |
204 | panic("\n"); |
205 | else | |
206 | printk("\n"); | |
207 | ret = -1; | |
208 | } | |
209 | ||
1da177e4 LT |
210 | return ret; |
211 | } | |
212 | ||
213 | static int force_sysemu_disabled = 0; | |
214 | ||
cb66504d PBG |
215 | int ptrace_faultinfo = 1; |
216 | int proc_mm = 1; | |
217 | ||
218 | static int __init skas0_cmd_param(char *str, int* add) | |
219 | { | |
220 | ptrace_faultinfo = proc_mm = 0; | |
221 | return 0; | |
222 | } | |
223 | ||
1da177e4 LT |
224 | static int __init nosysemu_cmd_param(char *str, int* add) |
225 | { | |
226 | force_sysemu_disabled = 1; | |
227 | return 0; | |
228 | } | |
229 | ||
cb66504d PBG |
230 | __uml_setup("skas0", skas0_cmd_param, |
231 | "skas0\n" | |
232 | " Disables SKAS3 usage, so that SKAS0 is used, unless you \n" | |
233 | " specify mode=tt.\n\n"); | |
234 | ||
1da177e4 LT |
235 | __uml_setup("nosysemu", nosysemu_cmd_param, |
236 | "nosysemu\n" | |
237 | " Turns off syscall emulation patch for ptrace (SYSEMU) on.\n" | |
238 | " SYSEMU is a performance-patch introduced by Laurent Vivier. It changes\n" | |
239 | " behaviour of ptrace() and helps reducing host context switch rate.\n" | |
240 | " To make it working, you need a kernel patch for your host, too.\n" | |
241 | " See http://perso.wanadoo.fr/laurent.vivier/UML/ for further information.\n\n"); | |
242 | ||
243 | static void __init check_sysemu(void) | |
244 | { | |
1da177e4 LT |
245 | int pid, syscall, n, status, count=0; |
246 | ||
247 | printk("Checking syscall emulation patch for ptrace..."); | |
248 | sysemu_supported = 0; | |
98fdffcc | 249 | pid = start_ptraced_child(); |
1da177e4 LT |
250 | |
251 | if(ptrace(PTRACE_SYSEMU, pid, 0, 0) < 0) | |
252 | goto fail; | |
253 | ||
254 | CATCH_EINTR(n = waitpid(pid, &status, WUNTRACED)); | |
255 | if (n < 0) | |
256 | panic("check_sysemu : wait failed, errno = %d", errno); | |
257 | if(!WIFSTOPPED(status) || (WSTOPSIG(status) != SIGTRAP)) | |
258 | panic("check_sysemu : expected SIGTRAP, " | |
259 | "got status = %d", status); | |
260 | ||
261 | n = ptrace(PTRACE_POKEUSR, pid, PT_SYSCALL_RET_OFFSET, | |
262 | os_getpid()); | |
263 | if(n < 0) | |
264 | panic("check_sysemu : failed to modify system " | |
265 | "call return, errno = %d", errno); | |
266 | ||
98fdffcc | 267 | if (stop_ptraced_child(pid, 0, 0) < 0) |
1da177e4 LT |
268 | goto fail_stopped; |
269 | ||
270 | sysemu_supported = 1; | |
271 | printk("OK\n"); | |
272 | set_using_sysemu(!force_sysemu_disabled); | |
273 | ||
274 | printk("Checking advanced syscall emulation patch for ptrace..."); | |
98fdffcc | 275 | pid = start_ptraced_child(); |
1da177e4 LT |
276 | while(1){ |
277 | count++; | |
278 | if(ptrace(PTRACE_SYSEMU_SINGLESTEP, pid, 0, 0) < 0) | |
279 | goto fail; | |
280 | CATCH_EINTR(n = waitpid(pid, &status, WUNTRACED)); | |
281 | if(n < 0) | |
282 | panic("check_ptrace : wait failed, errno = %d", errno); | |
283 | if(!WIFSTOPPED(status) || (WSTOPSIG(status) != SIGTRAP)) | |
284 | panic("check_ptrace : expected (SIGTRAP|SYSCALL_TRAP), " | |
285 | "got status = %d", status); | |
286 | ||
287 | syscall = ptrace(PTRACE_PEEKUSR, pid, PT_SYSCALL_NR_OFFSET, | |
288 | 0); | |
289 | if(syscall == __NR_getpid){ | |
290 | if (!count) | |
291 | panic("check_ptrace : SYSEMU_SINGLESTEP doesn't singlestep"); | |
292 | n = ptrace(PTRACE_POKEUSR, pid, PT_SYSCALL_RET_OFFSET, | |
293 | os_getpid()); | |
294 | if(n < 0) | |
295 | panic("check_sysemu : failed to modify system " | |
296 | "call return, errno = %d", errno); | |
297 | break; | |
298 | } | |
299 | } | |
98fdffcc | 300 | if (stop_ptraced_child(pid, 0, 0) < 0) |
1da177e4 LT |
301 | goto fail_stopped; |
302 | ||
303 | sysemu_supported = 2; | |
304 | printk("OK\n"); | |
305 | ||
306 | if ( !force_sysemu_disabled ) | |
307 | set_using_sysemu(sysemu_supported); | |
308 | return; | |
309 | ||
310 | fail: | |
98fdffcc | 311 | stop_ptraced_child(pid, 1, 0); |
1da177e4 LT |
312 | fail_stopped: |
313 | printk("missing\n"); | |
314 | } | |
315 | ||
316 | void __init check_ptrace(void) | |
317 | { | |
1da177e4 LT |
318 | int pid, syscall, n, status; |
319 | ||
320 | printk("Checking that ptrace can change system call numbers..."); | |
98fdffcc | 321 | pid = start_ptraced_child(); |
1da177e4 LT |
322 | |
323 | if (ptrace(PTRACE_OLDSETOPTIONS, pid, 0, (void *)PTRACE_O_TRACESYSGOOD) < 0) | |
324 | panic("check_ptrace: PTRACE_SETOPTIONS failed, errno = %d", errno); | |
325 | ||
326 | while(1){ | |
327 | if(ptrace(PTRACE_SYSCALL, pid, 0, 0) < 0) | |
328 | panic("check_ptrace : ptrace failed, errno = %d", | |
329 | errno); | |
330 | CATCH_EINTR(n = waitpid(pid, &status, WUNTRACED)); | |
331 | if(n < 0) | |
332 | panic("check_ptrace : wait failed, errno = %d", errno); | |
333 | if(!WIFSTOPPED(status) || (WSTOPSIG(status) != SIGTRAP + 0x80)) | |
334 | panic("check_ptrace : expected SIGTRAP + 0x80, " | |
335 | "got status = %d", status); | |
336 | ||
337 | syscall = ptrace(PTRACE_PEEKUSR, pid, PT_SYSCALL_NR_OFFSET, | |
338 | 0); | |
339 | if(syscall == __NR_getpid){ | |
340 | n = ptrace(PTRACE_POKEUSR, pid, PT_SYSCALL_NR_OFFSET, | |
341 | __NR_getppid); | |
342 | if(n < 0) | |
343 | panic("check_ptrace : failed to modify system " | |
344 | "call, errno = %d", errno); | |
345 | break; | |
346 | } | |
347 | } | |
98fdffcc | 348 | stop_ptraced_child(pid, 0, 1); |
1da177e4 LT |
349 | printk("OK\n"); |
350 | check_sysemu(); | |
351 | } | |
352 | ||
353 | int run_kernel_thread(int (*fn)(void *), void *arg, void **jmp_ptr) | |
354 | { | |
355 | sigjmp_buf buf; | |
356 | int n; | |
357 | ||
358 | *jmp_ptr = &buf; | |
359 | n = sigsetjmp(buf, 1); | |
360 | if(n != 0) | |
361 | return(n); | |
362 | (*fn)(arg); | |
363 | return(0); | |
364 | } | |
365 | ||
366 | void forward_pending_sigio(int target) | |
367 | { | |
368 | sigset_t sigs; | |
369 | ||
370 | if(sigpending(&sigs)) | |
371 | panic("forward_pending_sigio : sigpending failed"); | |
372 | if(sigismember(&sigs, SIGIO)) | |
373 | kill(target, SIGIO); | |
374 | } | |
375 | ||
d67b569f JD |
376 | extern void *__syscall_stub_start, __syscall_stub_end; |
377 | ||
1da177e4 | 378 | #ifdef UML_CONFIG_MODE_SKAS |
cb66504d | 379 | |
d67b569f | 380 | static inline void check_skas3_ptrace_support(void) |
1da177e4 LT |
381 | { |
382 | struct ptrace_faultinfo fi; | |
d67b569f | 383 | int pid, n; |
1da177e4 LT |
384 | |
385 | printf("Checking for the skas3 patch in the host..."); | |
98fdffcc | 386 | pid = start_ptraced_child(); |
1da177e4 LT |
387 | |
388 | n = ptrace(PTRACE_FAULTINFO, pid, 0, &fi); | |
389 | if (n < 0) { | |
cb66504d | 390 | ptrace_faultinfo = 0; |
1da177e4 LT |
391 | if(errno == EIO) |
392 | printf("not found\n"); | |
393 | else { | |
394 | perror("not found"); | |
395 | } | |
d67b569f JD |
396 | } |
397 | else { | |
cb66504d PBG |
398 | if (!ptrace_faultinfo) |
399 | printf("found but disabled on command line\n"); | |
400 | else | |
401 | printf("found\n"); | |
1da177e4 LT |
402 | } |
403 | ||
404 | init_registers(pid); | |
98fdffcc | 405 | stop_ptraced_child(pid, 1, 1); |
1da177e4 LT |
406 | } |
407 | ||
408 | int can_do_skas(void) | |
409 | { | |
1da177e4 LT |
410 | printf("Checking for /proc/mm..."); |
411 | if (os_access("/proc/mm", OS_ACC_W_OK) < 0) { | |
d67b569f | 412 | proc_mm = 0; |
1da177e4 | 413 | printf("not found\n"); |
cb66504d PBG |
414 | } else { |
415 | if (!proc_mm) | |
416 | printf("found but disabled on command line\n"); | |
417 | else | |
418 | printf("found\n"); | |
1da177e4 LT |
419 | } |
420 | ||
d67b569f JD |
421 | check_skas3_ptrace_support(); |
422 | return 1; | |
1da177e4 LT |
423 | } |
424 | #else | |
425 | int can_do_skas(void) | |
426 | { | |
427 | return(0); | |
428 | } | |
429 | #endif |