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 "signal_kern.h" 23 #include "signal_user.h" 24 #include "sysdep/ptrace.h" 25 #include "sysdep/sigcontext.h" 26 #include "irq_user.h" 27 #include "ptrace_user.h" 28 #include "time_user.h" 29 #include "init.h" 30 #include "os.h" 31 #include "uml-config.h" 32 #include "choose-mode.h" 33 #include "mode.h" 34 #include "tempfile.h" 35 #ifdef UML_CONFIG_MODE_SKAS 36 #include "skas.h" 37 #include "skas_ptrace.h" 38 #include "registers.h" 39 #endif 40 41 static int ptrace_child(void *arg) 42 { 43 int ret; 44 int pid = os_getpid(), ppid = getppid(); 45 int sc_result; 46 47 if(ptrace(PTRACE_TRACEME, 0, 0, 0) < 0){ 48 perror("ptrace"); 49 os_kill_process(pid, 0); 50 } 51 os_stop_process(pid); 52 53 /*This syscall will be intercepted by the parent. Don't call more than 54 * once, please.*/ 55 sc_result = os_getpid(); 56 57 if (sc_result == pid) 58 ret = 1; /*Nothing modified by the parent, we are running 59 normally.*/ 60 else if (sc_result == ppid) 61 ret = 0; /*Expected in check_ptrace and check_sysemu when they 62 succeed in modifying the stack frame*/ 63 else 64 ret = 2; /*Serious trouble! This could be caused by a bug in 65 host 2.6 SKAS3/2.6 patch before release -V6, together 66 with a bug in the UML code itself.*/ 67 _exit(ret); 68 } 69 70 static int start_ptraced_child(void **stack_out) 71 { 72 void *stack; 73 unsigned long sp; 74 int pid, n, status; 75 76 stack = mmap(NULL, PAGE_SIZE, PROT_READ | PROT_WRITE | PROT_EXEC, 77 MAP_PRIVATE | MAP_ANONYMOUS, -1, 0); 78 if(stack == MAP_FAILED) 79 panic("check_ptrace : mmap failed, errno = %d", errno); 80 sp = (unsigned long) stack + PAGE_SIZE - sizeof(void *); 81 pid = clone(ptrace_child, (void *) sp, SIGCHLD, NULL); 82 if(pid < 0) 83 panic("start_ptraced_child : clone failed, errno = %d", errno); 84 CATCH_EINTR(n = waitpid(pid, &status, WUNTRACED)); 85 if(n < 0) 86 panic("check_ptrace : clone failed, errno = %d", errno); 87 if(!WIFSTOPPED(status) || (WSTOPSIG(status) != SIGSTOP)) 88 panic("check_ptrace : expected SIGSTOP, got status = %d", 89 status); 90 91 *stack_out = stack; 92 return(pid); 93 } 94 95 /* When testing for SYSEMU support, if it is one of the broken versions, we 96 * must just avoid using sysemu, not panic, but only if SYSEMU features are 97 * broken. 98 * So only for SYSEMU features we test mustpanic, while normal host features 99 * must work anyway! 100 */ 101 static int stop_ptraced_child(int pid, void *stack, int exitcode, 102 int mustpanic) 103 { 104 int status, n, ret = 0; 105 106 if(ptrace(PTRACE_CONT, pid, 0, 0) < 0) 107 panic("check_ptrace : ptrace failed, errno = %d", errno); 108 CATCH_EINTR(n = waitpid(pid, &status, 0)); 109 if(!WIFEXITED(status) || (WEXITSTATUS(status) != exitcode)) { 110 int exit_with = WEXITSTATUS(status); 111 if (exit_with == 2) 112 printk("check_ptrace : child exited with status 2. " 113 "Serious trouble happening! Try updating your " 114 "host skas patch!\nDisabling SYSEMU support."); 115 printk("check_ptrace : child exited with exitcode %d, while " 116 "expecting %d; status 0x%x", exit_with, 117 exitcode, status); 118 if (mustpanic) 119 panic("\n"); 120 else 121 printk("\n"); 122 ret = -1; 123 } 124 125 if(munmap(stack, PAGE_SIZE) < 0) 126 panic("check_ptrace : munmap failed, errno = %d", errno); 127 return ret; 128 } 129 130 int ptrace_faultinfo = 1; 131 int proc_mm = 1; 132 133 static int __init skas0_cmd_param(char *str, int* add) 134 { 135 ptrace_faultinfo = proc_mm = 0; 136 return 0; 137 } 138 139 __uml_setup("skas0", skas0_cmd_param, 140 "skas0\n" 141 " Disables SKAS3 usage, so that SKAS0 is used, unless \n" 142 " you specify mode=tt.\n\n"); 143 144 static int force_sysemu_disabled = 0; 145 146 static int __init nosysemu_cmd_param(char *str, int* add) 147 { 148 force_sysemu_disabled = 1; 149 return 0; 150 } 151 152 __uml_setup("nosysemu", nosysemu_cmd_param, 153 "nosysemu\n" 154 " Turns off syscall emulation patch for ptrace (SYSEMU) on.\n" 155 " SYSEMU is a performance-patch introduced by Laurent Vivier. It changes\n" 156 " behaviour of ptrace() and helps reducing host context switch rate.\n" 157 " To make it working, you need a kernel patch for your host, too.\n" 158 " See http://perso.wanadoo.fr/laurent.vivier/UML/ for further \n" 159 " information.\n\n"); 160 161 static void __init check_sysemu(void) 162 { 163 void *stack; 164 int pid, n, status, count=0; 165 166 printk("Checking syscall emulation patch for ptrace..."); 167 sysemu_supported = 0; 168 pid = start_ptraced_child(&stack); 169 170 if(ptrace(PTRACE_SYSEMU, pid, 0, 0) < 0) 171 goto fail; 172 173 CATCH_EINTR(n = waitpid(pid, &status, WUNTRACED)); 174 if (n < 0) 175 panic("check_sysemu : wait failed, errno = %d", errno); 176 if(!WIFSTOPPED(status) || (WSTOPSIG(status) != SIGTRAP)) 177 panic("check_sysemu : expected SIGTRAP, " 178 "got status = %d", status); 179 180 n = ptrace(PTRACE_POKEUSR, pid, PT_SYSCALL_RET_OFFSET, 181 os_getpid()); 182 if(n < 0) 183 panic("check_sysemu : failed to modify system " 184 "call return, errno = %d", errno); 185 186 if (stop_ptraced_child(pid, stack, 0, 0) < 0) 187 goto fail_stopped; 188 189 sysemu_supported = 1; 190 printk("OK\n"); 191 set_using_sysemu(!force_sysemu_disabled); 192 193 printk("Checking advanced syscall emulation patch for ptrace..."); 194 pid = start_ptraced_child(&stack); 195 196 if(ptrace(PTRACE_OLDSETOPTIONS, pid, 0, 197 (void *) PTRACE_O_TRACESYSGOOD) < 0) 198 panic("check_ptrace: PTRACE_OLDSETOPTIONS failed, errno = %d", 199 errno); 200 201 while(1){ 202 count++; 203 if(ptrace(PTRACE_SYSEMU_SINGLESTEP, pid, 0, 0) < 0) 204 goto fail; 205 CATCH_EINTR(n = waitpid(pid, &status, WUNTRACED)); 206 if(n < 0) 207 panic("check_ptrace : wait failed, errno = %d", errno); 208 if(WIFSTOPPED(status) && (WSTOPSIG(status) == (SIGTRAP|0x80))){ 209 if (!count) 210 panic("check_ptrace : SYSEMU_SINGLESTEP " 211 "doesn't singlestep"); 212 n = ptrace(PTRACE_POKEUSR, pid, PT_SYSCALL_RET_OFFSET, 213 os_getpid()); 214 if(n < 0) 215 panic("check_sysemu : failed to modify system " 216 "call return, errno = %d", errno); 217 break; 218 } 219 else if(WIFSTOPPED(status) && (WSTOPSIG(status) == SIGTRAP)) 220 count++; 221 else 222 panic("check_ptrace : expected SIGTRAP or " 223 "(SIGTRAP|0x80), got status = %d", status); 224 } 225 if (stop_ptraced_child(pid, stack, 0, 0) < 0) 226 goto fail_stopped; 227 228 sysemu_supported = 2; 229 printk("OK\n"); 230 231 if ( !force_sysemu_disabled ) 232 set_using_sysemu(sysemu_supported); 233 return; 234 235 fail: 236 stop_ptraced_child(pid, stack, 1, 0); 237 fail_stopped: 238 printk("missing\n"); 239 } 240 241 static void __init check_ptrace(void) 242 { 243 void *stack; 244 int pid, syscall, n, status; 245 246 printk("Checking that ptrace can change system call numbers..."); 247 pid = start_ptraced_child(&stack); 248 249 if(ptrace(PTRACE_OLDSETOPTIONS, pid, 0, (void *)PTRACE_O_TRACESYSGOOD) < 0) 250 panic("check_ptrace: PTRACE_OLDSETOPTIONS failed, errno = %d", errno); 251 252 while(1){ 253 if(ptrace(PTRACE_SYSCALL, pid, 0, 0) < 0) 254 panic("check_ptrace : ptrace failed, errno = %d", 255 errno); 256 CATCH_EINTR(n = waitpid(pid, &status, WUNTRACED)); 257 if(n < 0) 258 panic("check_ptrace : wait failed, errno = %d", errno); 259 if(!WIFSTOPPED(status) || (WSTOPSIG(status) != (SIGTRAP|0x80))) 260 panic("check_ptrace : expected (SIGTRAP|0x80), " 261 "got status = %d", status); 262 263 syscall = ptrace(PTRACE_PEEKUSR, pid, PT_SYSCALL_NR_OFFSET, 264 0); 265 if(syscall == __NR_getpid){ 266 n = ptrace(PTRACE_POKEUSR, pid, PT_SYSCALL_NR_OFFSET, 267 __NR_getppid); 268 if(n < 0) 269 panic("check_ptrace : failed to modify system " 270 "call, errno = %d", errno); 271 break; 272 } 273 } 274 stop_ptraced_child(pid, stack, 0, 1); 275 printk("OK\n"); 276 check_sysemu(); 277 } 278 279 void os_early_checks(void) 280 { 281 check_ptrace(); 282 } 283 284 static int __init noprocmm_cmd_param(char *str, int* add) 285 { 286 proc_mm = 0; 287 return 0; 288 } 289 290 __uml_setup("noprocmm", noprocmm_cmd_param, 291 "noprocmm\n" 292 " Turns off usage of /proc/mm, even if host supports it.\n" 293 " To support /proc/mm, the host needs to be patched using\n" 294 " the current skas3 patch.\n\n"); 295 296 static int __init noptracefaultinfo_cmd_param(char *str, int* add) 297 { 298 ptrace_faultinfo = 0; 299 return 0; 300 } 301 302 __uml_setup("noptracefaultinfo", noptracefaultinfo_cmd_param, 303 "noptracefaultinfo\n" 304 " Turns off usage of PTRACE_FAULTINFO, even if host supports\n" 305 " it. To support PTRACE_FAULTINFO, the host needs to be patched\n" 306 " using the current skas3 patch.\n\n"); 307 308 #ifdef UML_CONFIG_MODE_SKAS 309 static inline void check_skas3_ptrace_support(void) 310 { 311 struct ptrace_faultinfo fi; 312 void *stack; 313 int pid, n; 314 315 printf("Checking for the skas3 patch in the host..."); 316 pid = start_ptraced_child(&stack); 317 318 n = ptrace(PTRACE_FAULTINFO, pid, 0, &fi); 319 if (n < 0) { 320 ptrace_faultinfo = 0; 321 if(errno == EIO) 322 printf("not found\n"); 323 else 324 perror("not found"); 325 } 326 else { 327 if (!ptrace_faultinfo) 328 printf("found but disabled on command line\n"); 329 else 330 printf("found\n"); 331 } 332 333 init_registers(pid); 334 stop_ptraced_child(pid, stack, 1, 1); 335 } 336 337 int can_do_skas(void) 338 { 339 printf("Checking for /proc/mm..."); 340 if (os_access("/proc/mm", OS_ACC_W_OK) < 0) { 341 proc_mm = 0; 342 printf("not found\n"); 343 } 344 else { 345 if (!proc_mm) 346 printf("found but disabled on command line\n"); 347 else 348 printf("found\n"); 349 } 350 351 check_skas3_ptrace_support(); 352 return 1; 353 } 354 #else 355 int can_do_skas(void) 356 { 357 return(0); 358 } 359 #endif 360