]>
Commit | Line | Data |
---|---|---|
60d339f6 | 1 | /* |
1da177e4 LT |
2 | * Copyright (C) 2000, 2001, 2002 Jeff Dike (jdike@karaya.com) |
3 | * Licensed under the GPL | |
4 | */ | |
5 | ||
6 | #include <stdio.h> | |
0f80bc85 JD |
7 | #include <stddef.h> |
8 | #include <stdarg.h> | |
9 | #include <stdlib.h> | |
10 | #include <string.h> | |
1da177e4 LT |
11 | #include <unistd.h> |
12 | #include <signal.h> | |
13 | #include <sched.h> | |
0f80bc85 | 14 | #include <fcntl.h> |
1da177e4 | 15 | #include <errno.h> |
1da177e4 LT |
16 | #include <setjmp.h> |
17 | #include <sys/time.h> | |
18 | #include <sys/wait.h> | |
19 | #include <sys/mman.h> | |
20 | #include <asm/unistd.h> | |
21 | #include <asm/page.h> | |
0f80bc85 | 22 | #include <sys/types.h> |
1da177e4 LT |
23 | #include "user_util.h" |
24 | #include "kern_util.h" | |
25 | #include "user.h" | |
1da177e4 LT |
26 | #include "signal_kern.h" |
27 | #include "signal_user.h" | |
28 | #include "sysdep/ptrace.h" | |
29 | #include "sysdep/sigcontext.h" | |
30 | #include "irq_user.h" | |
31 | #include "ptrace_user.h" | |
0f80bc85 | 32 | #include "mem_user.h" |
1da177e4 LT |
33 | #include "time_user.h" |
34 | #include "init.h" | |
35 | #include "os.h" | |
36 | #include "uml-config.h" | |
1da177e4 LT |
37 | #include "choose-mode.h" |
38 | #include "mode.h" | |
d67b569f | 39 | #include "tempfile.h" |
0f80bc85 JD |
40 | #include "kern_constants.h" |
41 | ||
1da177e4 LT |
42 | #ifdef UML_CONFIG_MODE_SKAS |
43 | #include "skas.h" | |
44 | #include "skas_ptrace.h" | |
45 | #include "registers.h" | |
46 | #endif | |
47 | ||
b85e9680 | 48 | static int ptrace_child(void *arg) |
1da177e4 LT |
49 | { |
50 | int ret; | |
51 | int pid = os_getpid(), ppid = getppid(); | |
52 | int sc_result; | |
53 | ||
54 | if(ptrace(PTRACE_TRACEME, 0, 0, 0) < 0){ | |
55 | perror("ptrace"); | |
56 | os_kill_process(pid, 0); | |
57 | } | |
58 | os_stop_process(pid); | |
59 | ||
60 | /*This syscall will be intercepted by the parent. Don't call more than | |
61 | * once, please.*/ | |
62 | sc_result = os_getpid(); | |
63 | ||
64 | if (sc_result == pid) | |
65 | ret = 1; /*Nothing modified by the parent, we are running | |
66 | normally.*/ | |
67 | else if (sc_result == ppid) | |
68 | ret = 0; /*Expected in check_ptrace and check_sysemu when they | |
69 | succeed in modifying the stack frame*/ | |
70 | else | |
71 | ret = 2; /*Serious trouble! This could be caused by a bug in | |
72 | host 2.6 SKAS3/2.6 patch before release -V6, together | |
73 | with a bug in the UML code itself.*/ | |
74 | _exit(ret); | |
75 | } | |
76 | ||
b85e9680 | 77 | static int start_ptraced_child(void **stack_out) |
1da177e4 | 78 | { |
b85e9680 JD |
79 | void *stack; |
80 | unsigned long sp; | |
1da177e4 | 81 | int pid, n, status; |
60d339f6 | 82 | |
b85e9680 JD |
83 | stack = mmap(NULL, PAGE_SIZE, PROT_READ | PROT_WRITE | PROT_EXEC, |
84 | MAP_PRIVATE | MAP_ANONYMOUS, -1, 0); | |
85 | if(stack == MAP_FAILED) | |
86 | panic("check_ptrace : mmap failed, errno = %d", errno); | |
87 | sp = (unsigned long) stack + PAGE_SIZE - sizeof(void *); | |
88 | pid = clone(ptrace_child, (void *) sp, SIGCHLD, NULL); | |
1da177e4 | 89 | if(pid < 0) |
60d339f6 | 90 | panic("start_ptraced_child : clone failed, errno = %d", errno); |
1da177e4 LT |
91 | CATCH_EINTR(n = waitpid(pid, &status, WUNTRACED)); |
92 | if(n < 0) | |
60d339f6 | 93 | panic("check_ptrace : clone failed, errno = %d", errno); |
1da177e4 LT |
94 | if(!WIFSTOPPED(status) || (WSTOPSIG(status) != SIGSTOP)) |
95 | panic("check_ptrace : expected SIGSTOP, got status = %d", | |
96 | status); | |
97 | ||
b85e9680 | 98 | *stack_out = stack; |
1da177e4 LT |
99 | return(pid); |
100 | } | |
101 | ||
60d339f6 GS |
102 | /* When testing for SYSEMU support, if it is one of the broken versions, we |
103 | * must just avoid using sysemu, not panic, but only if SYSEMU features are | |
104 | * broken. | |
1da177e4 | 105 | * So only for SYSEMU features we test mustpanic, while normal host features |
60d339f6 GS |
106 | * must work anyway! |
107 | */ | |
108 | static int stop_ptraced_child(int pid, void *stack, int exitcode, | |
109 | int mustpanic) | |
1da177e4 LT |
110 | { |
111 | int status, n, ret = 0; | |
112 | ||
113 | if(ptrace(PTRACE_CONT, pid, 0, 0) < 0) | |
b85e9680 | 114 | panic("check_ptrace : ptrace failed, errno = %d", errno); |
1da177e4 LT |
115 | CATCH_EINTR(n = waitpid(pid, &status, 0)); |
116 | if(!WIFEXITED(status) || (WEXITSTATUS(status) != exitcode)) { | |
117 | int exit_with = WEXITSTATUS(status); | |
118 | if (exit_with == 2) | |
119 | printk("check_ptrace : child exited with status 2. " | |
120 | "Serious trouble happening! Try updating your " | |
121 | "host skas patch!\nDisabling SYSEMU support."); | |
122 | printk("check_ptrace : child exited with exitcode %d, while " | |
123 | "expecting %d; status 0x%x", exit_with, | |
124 | exitcode, status); | |
b85e9680 | 125 | if (mustpanic) |
1da177e4 LT |
126 | panic("\n"); |
127 | else | |
128 | printk("\n"); | |
129 | ret = -1; | |
130 | } | |
131 | ||
b85e9680 JD |
132 | if(munmap(stack, PAGE_SIZE) < 0) |
133 | panic("check_ptrace : munmap failed, errno = %d", errno); | |
1da177e4 LT |
134 | return ret; |
135 | } | |
136 | ||
cb66504d PBG |
137 | int ptrace_faultinfo = 1; |
138 | int proc_mm = 1; | |
139 | ||
140 | static int __init skas0_cmd_param(char *str, int* add) | |
141 | { | |
142 | ptrace_faultinfo = proc_mm = 0; | |
143 | return 0; | |
144 | } | |
145 | ||
9e3d862e PBG |
146 | /* The two __uml_setup would conflict, without this stupid alias. */ |
147 | ||
148 | static int __init mode_skas0_cmd_param(char *str, int* add) | |
149 | __attribute__((alias("skas0_cmd_param"))); | |
150 | ||
60d339f6 GS |
151 | __uml_setup("skas0", skas0_cmd_param, |
152 | "skas0\n" | |
153 | " Disables SKAS3 usage, so that SKAS0 is used, unless \n" | |
154 | " you specify mode=tt.\n\n"); | |
155 | ||
9e3d862e PBG |
156 | __uml_setup("mode=skas0", mode_skas0_cmd_param, |
157 | "mode=skas0\n" | |
158 | " Disables SKAS3 usage, so that SKAS0 is used, unless you \n" | |
159 | " specify mode=tt. Note that this was recently added - on \n" | |
160 | " older kernels you must use simply \"skas0\".\n\n"); | |
161 | ||
60d339f6 GS |
162 | static int force_sysemu_disabled = 0; |
163 | ||
1da177e4 LT |
164 | static int __init nosysemu_cmd_param(char *str, int* add) |
165 | { | |
166 | force_sysemu_disabled = 1; | |
167 | return 0; | |
168 | } | |
169 | ||
170 | __uml_setup("nosysemu", nosysemu_cmd_param, | |
60d339f6 GS |
171 | "nosysemu\n" |
172 | " Turns off syscall emulation patch for ptrace (SYSEMU) on.\n" | |
173 | " SYSEMU is a performance-patch introduced by Laurent Vivier. It changes\n" | |
174 | " behaviour of ptrace() and helps reducing host context switch rate.\n" | |
175 | " To make it working, you need a kernel patch for your host, too.\n" | |
176 | " See http://perso.wanadoo.fr/laurent.vivier/UML/ for further \n" | |
177 | " information.\n\n"); | |
1da177e4 LT |
178 | |
179 | static void __init check_sysemu(void) | |
180 | { | |
b85e9680 | 181 | void *stack; |
f9dfefe4 | 182 | int pid, n, status, count=0; |
1da177e4 LT |
183 | |
184 | printk("Checking syscall emulation patch for ptrace..."); | |
185 | sysemu_supported = 0; | |
b85e9680 | 186 | pid = start_ptraced_child(&stack); |
1da177e4 LT |
187 | |
188 | if(ptrace(PTRACE_SYSEMU, pid, 0, 0) < 0) | |
189 | goto fail; | |
190 | ||
191 | CATCH_EINTR(n = waitpid(pid, &status, WUNTRACED)); | |
192 | if (n < 0) | |
193 | panic("check_sysemu : wait failed, errno = %d", errno); | |
194 | if(!WIFSTOPPED(status) || (WSTOPSIG(status) != SIGTRAP)) | |
195 | panic("check_sysemu : expected SIGTRAP, " | |
196 | "got status = %d", status); | |
197 | ||
198 | n = ptrace(PTRACE_POKEUSR, pid, PT_SYSCALL_RET_OFFSET, | |
199 | os_getpid()); | |
200 | if(n < 0) | |
201 | panic("check_sysemu : failed to modify system " | |
202 | "call return, errno = %d", errno); | |
203 | ||
b85e9680 | 204 | if (stop_ptraced_child(pid, stack, 0, 0) < 0) |
1da177e4 LT |
205 | goto fail_stopped; |
206 | ||
207 | sysemu_supported = 1; | |
208 | printk("OK\n"); | |
209 | set_using_sysemu(!force_sysemu_disabled); | |
210 | ||
211 | printk("Checking advanced syscall emulation patch for ptrace..."); | |
b85e9680 | 212 | pid = start_ptraced_child(&stack); |
f9dfefe4 BS |
213 | |
214 | if(ptrace(PTRACE_OLDSETOPTIONS, pid, 0, | |
215 | (void *) PTRACE_O_TRACESYSGOOD) < 0) | |
216 | panic("check_ptrace: PTRACE_OLDSETOPTIONS failed, errno = %d", | |
217 | errno); | |
218 | ||
1da177e4 LT |
219 | while(1){ |
220 | count++; | |
221 | if(ptrace(PTRACE_SYSEMU_SINGLESTEP, pid, 0, 0) < 0) | |
222 | goto fail; | |
223 | CATCH_EINTR(n = waitpid(pid, &status, WUNTRACED)); | |
224 | if(n < 0) | |
225 | panic("check_ptrace : wait failed, errno = %d", errno); | |
f9dfefe4 | 226 | if(WIFSTOPPED(status) && (WSTOPSIG(status) == (SIGTRAP|0x80))){ |
1da177e4 | 227 | if (!count) |
f9dfefe4 BS |
228 | panic("check_ptrace : SYSEMU_SINGLESTEP " |
229 | "doesn't singlestep"); | |
1da177e4 LT |
230 | n = ptrace(PTRACE_POKEUSR, pid, PT_SYSCALL_RET_OFFSET, |
231 | os_getpid()); | |
232 | if(n < 0) | |
233 | panic("check_sysemu : failed to modify system " | |
234 | "call return, errno = %d", errno); | |
235 | break; | |
236 | } | |
f9dfefe4 BS |
237 | else if(WIFSTOPPED(status) && (WSTOPSIG(status) == SIGTRAP)) |
238 | count++; | |
239 | else | |
240 | panic("check_ptrace : expected SIGTRAP or " | |
241 | "(SIGTRAP|0x80), got status = %d", status); | |
1da177e4 | 242 | } |
b85e9680 | 243 | if (stop_ptraced_child(pid, stack, 0, 0) < 0) |
1da177e4 LT |
244 | goto fail_stopped; |
245 | ||
246 | sysemu_supported = 2; | |
247 | printk("OK\n"); | |
248 | ||
249 | if ( !force_sysemu_disabled ) | |
250 | set_using_sysemu(sysemu_supported); | |
251 | return; | |
252 | ||
253 | fail: | |
b85e9680 | 254 | stop_ptraced_child(pid, stack, 1, 0); |
1da177e4 LT |
255 | fail_stopped: |
256 | printk("missing\n"); | |
257 | } | |
258 | ||
60d339f6 | 259 | static void __init check_ptrace(void) |
1da177e4 | 260 | { |
b85e9680 | 261 | void *stack; |
1da177e4 LT |
262 | int pid, syscall, n, status; |
263 | ||
264 | printk("Checking that ptrace can change system call numbers..."); | |
b85e9680 | 265 | pid = start_ptraced_child(&stack); |
1da177e4 | 266 | |
60d339f6 GS |
267 | if(ptrace(PTRACE_OLDSETOPTIONS, pid, 0, (void *)PTRACE_O_TRACESYSGOOD) < 0) |
268 | panic("check_ptrace: PTRACE_OLDSETOPTIONS failed, errno = %d", errno); | |
1da177e4 LT |
269 | |
270 | while(1){ | |
271 | if(ptrace(PTRACE_SYSCALL, pid, 0, 0) < 0) | |
60d339f6 | 272 | panic("check_ptrace : ptrace failed, errno = %d", |
1da177e4 LT |
273 | errno); |
274 | CATCH_EINTR(n = waitpid(pid, &status, WUNTRACED)); | |
275 | if(n < 0) | |
276 | panic("check_ptrace : wait failed, errno = %d", errno); | |
f9dfefe4 BS |
277 | if(!WIFSTOPPED(status) || (WSTOPSIG(status) != (SIGTRAP|0x80))) |
278 | panic("check_ptrace : expected (SIGTRAP|0x80), " | |
1da177e4 | 279 | "got status = %d", status); |
60d339f6 | 280 | |
1da177e4 LT |
281 | syscall = ptrace(PTRACE_PEEKUSR, pid, PT_SYSCALL_NR_OFFSET, |
282 | 0); | |
283 | if(syscall == __NR_getpid){ | |
284 | n = ptrace(PTRACE_POKEUSR, pid, PT_SYSCALL_NR_OFFSET, | |
285 | __NR_getppid); | |
286 | if(n < 0) | |
287 | panic("check_ptrace : failed to modify system " | |
288 | "call, errno = %d", errno); | |
289 | break; | |
290 | } | |
291 | } | |
b85e9680 | 292 | stop_ptraced_child(pid, stack, 0, 1); |
1da177e4 LT |
293 | printk("OK\n"); |
294 | check_sysemu(); | |
295 | } | |
296 | ||
0f80bc85 JD |
297 | extern int create_tmp_file(unsigned long len); |
298 | ||
299 | static void check_tmpexec(void) | |
300 | { | |
301 | void *addr; | |
302 | int err, fd = create_tmp_file(UM_KERN_PAGE_SIZE); | |
303 | ||
304 | addr = mmap(NULL, UM_KERN_PAGE_SIZE, | |
305 | PROT_READ | PROT_WRITE | PROT_EXEC, MAP_PRIVATE, fd, 0); | |
306 | printf("Checking PROT_EXEC mmap in /tmp..."); | |
307 | fflush(stdout); | |
308 | if(addr == MAP_FAILED){ | |
309 | err = errno; | |
310 | perror("failed"); | |
311 | if(err == EPERM) | |
312 | printf("/tmp must be not mounted noexec\n"); | |
313 | exit(1); | |
314 | } | |
315 | printf("OK\n"); | |
316 | munmap(addr, UM_KERN_PAGE_SIZE); | |
317 | ||
318 | close(fd); | |
319 | } | |
320 | ||
60d339f6 | 321 | void os_early_checks(void) |
1da177e4 | 322 | { |
60d339f6 | 323 | check_ptrace(); |
0f80bc85 JD |
324 | |
325 | /* Need to check this early because mmapping happens before the | |
326 | * kernel is running. | |
327 | */ | |
328 | check_tmpexec(); | |
1da177e4 LT |
329 | } |
330 | ||
d9838d86 BS |
331 | static int __init noprocmm_cmd_param(char *str, int* add) |
332 | { | |
333 | proc_mm = 0; | |
334 | return 0; | |
335 | } | |
336 | ||
337 | __uml_setup("noprocmm", noprocmm_cmd_param, | |
338 | "noprocmm\n" | |
339 | " Turns off usage of /proc/mm, even if host supports it.\n" | |
340 | " To support /proc/mm, the host needs to be patched using\n" | |
341 | " the current skas3 patch.\n\n"); | |
342 | ||
343 | static int __init noptracefaultinfo_cmd_param(char *str, int* add) | |
344 | { | |
345 | ptrace_faultinfo = 0; | |
346 | return 0; | |
347 | } | |
348 | ||
349 | __uml_setup("noptracefaultinfo", noptracefaultinfo_cmd_param, | |
350 | "noptracefaultinfo\n" | |
351 | " Turns off usage of PTRACE_FAULTINFO, even if host supports\n" | |
352 | " it. To support PTRACE_FAULTINFO, the host needs to be patched\n" | |
353 | " using the current skas3 patch.\n\n"); | |
354 | ||
1da177e4 | 355 | #ifdef UML_CONFIG_MODE_SKAS |
d67b569f | 356 | static inline void check_skas3_ptrace_support(void) |
1da177e4 LT |
357 | { |
358 | struct ptrace_faultinfo fi; | |
b85e9680 | 359 | void *stack; |
d67b569f | 360 | int pid, n; |
1da177e4 LT |
361 | |
362 | printf("Checking for the skas3 patch in the host..."); | |
b85e9680 | 363 | pid = start_ptraced_child(&stack); |
1da177e4 LT |
364 | |
365 | n = ptrace(PTRACE_FAULTINFO, pid, 0, &fi); | |
366 | if (n < 0) { | |
cb66504d | 367 | ptrace_faultinfo = 0; |
1da177e4 LT |
368 | if(errno == EIO) |
369 | printf("not found\n"); | |
60d339f6 | 370 | else |
1da177e4 | 371 | perror("not found"); |
d67b569f JD |
372 | } |
373 | else { | |
cb66504d PBG |
374 | if (!ptrace_faultinfo) |
375 | printf("found but disabled on command line\n"); | |
376 | else | |
377 | printf("found\n"); | |
1da177e4 LT |
378 | } |
379 | ||
380 | init_registers(pid); | |
b85e9680 | 381 | stop_ptraced_child(pid, stack, 1, 1); |
1da177e4 LT |
382 | } |
383 | ||
384 | int can_do_skas(void) | |
385 | { | |
1da177e4 LT |
386 | printf("Checking for /proc/mm..."); |
387 | if (os_access("/proc/mm", OS_ACC_W_OK) < 0) { | |
60d339f6 | 388 | proc_mm = 0; |
1da177e4 | 389 | printf("not found\n"); |
60d339f6 GS |
390 | } |
391 | else { | |
cb66504d PBG |
392 | if (!proc_mm) |
393 | printf("found but disabled on command line\n"); | |
394 | else | |
395 | printf("found\n"); | |
1da177e4 LT |
396 | } |
397 | ||
d67b569f JD |
398 | check_skas3_ptrace_support(); |
399 | return 1; | |
1da177e4 LT |
400 | } |
401 | #else | |
402 | int can_do_skas(void) | |
403 | { | |
404 | return(0); | |
405 | } | |
406 | #endif | |
0f80bc85 JD |
407 | |
408 | int have_devanon = 0; | |
409 | ||
410 | void check_devanon(void) | |
411 | { | |
412 | int fd; | |
413 | ||
414 | printk("Checking for /dev/anon on the host..."); | |
415 | fd = open("/dev/anon", O_RDWR); | |
416 | if(fd < 0){ | |
417 | printk("Not available (open failed with errno %d)\n", errno); | |
418 | return; | |
419 | } | |
420 | ||
421 | printk("OK\n"); | |
422 | have_devanon = 1; | |
423 | } | |
424 | ||
425 | int __init parse_iomem(char *str, int *add) | |
426 | { | |
427 | struct iomem_region *new; | |
428 | struct uml_stat buf; | |
429 | char *file, *driver; | |
430 | int fd, err, size; | |
431 | ||
432 | driver = str; | |
433 | file = strchr(str,','); | |
434 | if(file == NULL){ | |
435 | printf("parse_iomem : failed to parse iomem\n"); | |
436 | goto out; | |
437 | } | |
438 | *file = '\0'; | |
439 | file++; | |
440 | fd = os_open_file(file, of_rdwr(OPENFLAGS()), 0); | |
441 | if(fd < 0){ | |
442 | os_print_error(fd, "parse_iomem - Couldn't open io file"); | |
443 | goto out; | |
444 | } | |
445 | ||
446 | err = os_stat_fd(fd, &buf); | |
447 | if(err < 0){ | |
448 | os_print_error(err, "parse_iomem - cannot stat_fd file"); | |
449 | goto out_close; | |
450 | } | |
451 | ||
452 | new = malloc(sizeof(*new)); | |
453 | if(new == NULL){ | |
454 | perror("Couldn't allocate iomem_region struct"); | |
455 | goto out_close; | |
456 | } | |
457 | ||
458 | size = (buf.ust_size + UM_KERN_PAGE_SIZE) & ~(UM_KERN_PAGE_SIZE - 1); | |
459 | ||
460 | *new = ((struct iomem_region) { .next = iomem_regions, | |
461 | .driver = driver, | |
462 | .fd = fd, | |
463 | .size = size, | |
464 | .phys = 0, | |
465 | .virt = 0 }); | |
466 | iomem_regions = new; | |
467 | iomem_size += new->size + UM_KERN_PAGE_SIZE; | |
468 | ||
469 | return(0); | |
470 | out_close: | |
471 | os_close_file(fd); | |
472 | out: | |
473 | return(1); | |
474 | } | |
475 |