1 /* 2 * Copyright (C) 2000 - 2007 Jeff Dike (jdike@{addtoit,linux.intel}.com) 3 * Licensed under the GPL 4 */ 5 6 #include <stdio.h> 7 #include <stdlib.h> 8 #include <stdarg.h> 9 #include <unistd.h> 10 #include <errno.h> 11 #include <fcntl.h> 12 #include <sched.h> 13 #include <signal.h> 14 #include <string.h> 15 #include <sys/mman.h> 16 #include <sys/stat.h> 17 #include <sys/wait.h> 18 #include <sys/time.h> 19 #include <sys/resource.h> 20 #include <asm/unistd.h> 21 #include <init.h> 22 #include <os.h> 23 #include <mem_user.h> 24 #include <ptrace_user.h> 25 #include <registers.h> 26 #include <skas.h> 27 28 static void ptrace_child(void) 29 { 30 int ret; 31 /* Calling os_getpid because some libcs cached getpid incorrectly */ 32 int pid = os_getpid(), ppid = getppid(); 33 int sc_result; 34 35 if (change_sig(SIGWINCH, 0) < 0 || 36 ptrace(PTRACE_TRACEME, 0, 0, 0) < 0) { 37 perror("ptrace"); 38 kill(pid, SIGKILL); 39 } 40 kill(pid, SIGSTOP); 41 42 /* 43 * This syscall will be intercepted by the parent. Don't call more than 44 * once, please. 45 */ 46 sc_result = os_getpid(); 47 48 if (sc_result == pid) 49 /* Nothing modified by the parent, we are running normally. */ 50 ret = 1; 51 else if (sc_result == ppid) 52 /* 53 * Expected in check_ptrace and check_sysemu when they succeed 54 * in modifying the stack frame 55 */ 56 ret = 0; 57 else 58 /* Serious trouble! This could be caused by a bug in host 2.6 59 * SKAS3/2.6 patch before release -V6, together with a bug in 60 * the UML code itself. 61 */ 62 ret = 2; 63 64 exit(ret); 65 } 66 67 static void fatal_perror(const char *str) 68 { 69 perror(str); 70 exit(1); 71 } 72 73 static void fatal(char *fmt, ...) 74 { 75 va_list list; 76 77 va_start(list, fmt); 78 vfprintf(stderr, fmt, list); 79 va_end(list); 80 81 exit(1); 82 } 83 84 static void non_fatal(char *fmt, ...) 85 { 86 va_list list; 87 88 va_start(list, fmt); 89 vfprintf(stderr, fmt, list); 90 va_end(list); 91 } 92 93 static int start_ptraced_child(void) 94 { 95 int pid, n, status; 96 97 pid = fork(); 98 if (pid == 0) 99 ptrace_child(); 100 else if (pid < 0) 101 fatal_perror("start_ptraced_child : fork failed"); 102 103 CATCH_EINTR(n = waitpid(pid, &status, WUNTRACED)); 104 if (n < 0) 105 fatal_perror("check_ptrace : waitpid failed"); 106 if (!WIFSTOPPED(status) || (WSTOPSIG(status) != SIGSTOP)) 107 fatal("check_ptrace : expected SIGSTOP, got status = %d", 108 status); 109 110 return pid; 111 } 112 113 /* When testing for SYSEMU support, if it is one of the broken versions, we 114 * must just avoid using sysemu, not panic, but only if SYSEMU features are 115 * broken. 116 * So only for SYSEMU features we test mustpanic, while normal host features 117 * must work anyway! 118 */ 119 static int stop_ptraced_child(int pid, int exitcode, int mustexit) 120 { 121 int status, n, ret = 0; 122 123 if (ptrace(PTRACE_CONT, pid, 0, 0) < 0) { 124 perror("stop_ptraced_child : ptrace failed"); 125 return -1; 126 } 127 CATCH_EINTR(n = waitpid(pid, &status, 0)); 128 if (!WIFEXITED(status) || (WEXITSTATUS(status) != exitcode)) { 129 int exit_with = WEXITSTATUS(status); 130 if (exit_with == 2) 131 non_fatal("check_ptrace : child exited with status 2. " 132 "\nDisabling SYSEMU support.\n"); 133 non_fatal("check_ptrace : child exited with exitcode %d, while " 134 "expecting %d; status 0x%x\n", exit_with, 135 exitcode, status); 136 if (mustexit) 137 exit(1); 138 ret = -1; 139 } 140 141 return ret; 142 } 143 144 /* Changed only during early boot */ 145 static int force_sysemu_disabled = 0; 146 147 static int __init nosysemu_cmd_param(char *str, int* add) 148 { 149 force_sysemu_disabled = 1; 150 return 0; 151 } 152 153 __uml_setup("nosysemu", nosysemu_cmd_param, 154 "nosysemu\n" 155 " Turns off syscall emulation patch for ptrace (SYSEMU) on.\n" 156 " SYSEMU is a performance-patch introduced by Laurent Vivier. It changes\n" 157 " behaviour of ptrace() and helps reducing host context switch rate.\n" 158 " To make it working, you need a kernel patch for your host, too.\n" 159 " See http://perso.wanadoo.fr/laurent.vivier/UML/ for further \n" 160 " information.\n\n"); 161 162 static void __init check_sysemu(void) 163 { 164 unsigned long regs[MAX_REG_NR]; 165 int pid, n, status, count=0; 166 167 non_fatal("Checking syscall emulation patch for ptrace..."); 168 sysemu_supported = 0; 169 pid = start_ptraced_child(); 170 171 if (ptrace(PTRACE_SYSEMU, pid, 0, 0) < 0) 172 goto fail; 173 174 CATCH_EINTR(n = waitpid(pid, &status, WUNTRACED)); 175 if (n < 0) 176 fatal_perror("check_sysemu : wait failed"); 177 if (!WIFSTOPPED(status) || (WSTOPSIG(status) != SIGTRAP)) 178 fatal("check_sysemu : expected SIGTRAP, got status = %d\n", 179 status); 180 181 if (ptrace(PTRACE_GETREGS, pid, 0, regs) < 0) 182 fatal_perror("check_sysemu : PTRACE_GETREGS failed"); 183 if (PT_SYSCALL_NR(regs) != __NR_getpid) { 184 non_fatal("check_sysemu got system call number %d, " 185 "expected %d...", PT_SYSCALL_NR(regs), __NR_getpid); 186 goto fail; 187 } 188 189 n = ptrace(PTRACE_POKEUSER, pid, PT_SYSCALL_RET_OFFSET, os_getpid()); 190 if (n < 0) { 191 non_fatal("check_sysemu : failed to modify system call " 192 "return"); 193 goto fail; 194 } 195 196 if (stop_ptraced_child(pid, 0, 0) < 0) 197 goto fail_stopped; 198 199 sysemu_supported = 1; 200 non_fatal("OK\n"); 201 set_using_sysemu(!force_sysemu_disabled); 202 203 non_fatal("Checking advanced syscall emulation patch for ptrace..."); 204 pid = start_ptraced_child(); 205 206 if ((ptrace(PTRACE_OLDSETOPTIONS, pid, 0, 207 (void *) PTRACE_O_TRACESYSGOOD) < 0)) 208 fatal_perror("check_sysemu: PTRACE_OLDSETOPTIONS failed"); 209 210 while (1) { 211 count++; 212 if (ptrace(PTRACE_SYSEMU_SINGLESTEP, pid, 0, 0) < 0) 213 goto fail; 214 CATCH_EINTR(n = waitpid(pid, &status, WUNTRACED)); 215 if (n < 0) 216 fatal_perror("check_sysemu: wait failed"); 217 218 if (WIFSTOPPED(status) && 219 (WSTOPSIG(status) == (SIGTRAP|0x80))) { 220 if (!count) { 221 non_fatal("check_sysemu: SYSEMU_SINGLESTEP " 222 "doesn't singlestep"); 223 goto fail; 224 } 225 n = ptrace(PTRACE_POKEUSER, pid, PT_SYSCALL_RET_OFFSET, 226 os_getpid()); 227 if (n < 0) 228 fatal_perror("check_sysemu : failed to modify " 229 "system call return"); 230 break; 231 } 232 else if (WIFSTOPPED(status) && (WSTOPSIG(status) == SIGTRAP)) 233 count++; 234 else { 235 non_fatal("check_sysemu: expected SIGTRAP or " 236 "(SIGTRAP | 0x80), got status = %d\n", 237 status); 238 goto fail; 239 } 240 } 241 if (stop_ptraced_child(pid, 0, 0) < 0) 242 goto fail_stopped; 243 244 sysemu_supported = 2; 245 non_fatal("OK\n"); 246 247 if (!force_sysemu_disabled) 248 set_using_sysemu(sysemu_supported); 249 return; 250 251 fail: 252 stop_ptraced_child(pid, 1, 0); 253 fail_stopped: 254 non_fatal("missing\n"); 255 } 256 257 static void __init check_ptrace(void) 258 { 259 int pid, syscall, n, status; 260 261 non_fatal("Checking that ptrace can change system call numbers..."); 262 pid = start_ptraced_child(); 263 264 if ((ptrace(PTRACE_OLDSETOPTIONS, pid, 0, 265 (void *) PTRACE_O_TRACESYSGOOD) < 0)) 266 fatal_perror("check_ptrace: PTRACE_OLDSETOPTIONS failed"); 267 268 while (1) { 269 if (ptrace(PTRACE_SYSCALL, pid, 0, 0) < 0) 270 fatal_perror("check_ptrace : ptrace failed"); 271 272 CATCH_EINTR(n = waitpid(pid, &status, WUNTRACED)); 273 if (n < 0) 274 fatal_perror("check_ptrace : wait failed"); 275 276 if (!WIFSTOPPED(status) || 277 (WSTOPSIG(status) != (SIGTRAP | 0x80))) 278 fatal("check_ptrace : expected (SIGTRAP|0x80), " 279 "got status = %d", status); 280 281 syscall = ptrace(PTRACE_PEEKUSER, pid, PT_SYSCALL_NR_OFFSET, 282 0); 283 if (syscall == __NR_getpid) { 284 n = ptrace(PTRACE_POKEUSER, pid, PT_SYSCALL_NR_OFFSET, 285 __NR_getppid); 286 if (n < 0) 287 fatal_perror("check_ptrace : failed to modify " 288 "system call"); 289 break; 290 } 291 } 292 stop_ptraced_child(pid, 0, 1); 293 non_fatal("OK\n"); 294 check_sysemu(); 295 } 296 297 extern void check_tmpexec(void); 298 299 static void __init check_coredump_limit(void) 300 { 301 struct rlimit lim; 302 int err = getrlimit(RLIMIT_CORE, &lim); 303 304 if (err) { 305 perror("Getting core dump limit"); 306 return; 307 } 308 309 printf("Core dump limits :\n\tsoft - "); 310 if (lim.rlim_cur == RLIM_INFINITY) 311 printf("NONE\n"); 312 else printf("%lu\n", lim.rlim_cur); 313 314 printf("\thard - "); 315 if (lim.rlim_max == RLIM_INFINITY) 316 printf("NONE\n"); 317 else printf("%lu\n", lim.rlim_max); 318 } 319 320 void __init os_early_checks(void) 321 { 322 int pid; 323 324 /* Print out the core dump limits early */ 325 check_coredump_limit(); 326 327 check_ptrace(); 328 329 /* Need to check this early because mmapping happens before the 330 * kernel is running. 331 */ 332 check_tmpexec(); 333 334 pid = start_ptraced_child(); 335 if (init_registers(pid)) 336 fatal("Failed to initialize default registers"); 337 stop_ptraced_child(pid, 1, 1); 338 } 339 340 int __init parse_iomem(char *str, int *add) 341 { 342 struct iomem_region *new; 343 struct stat64 buf; 344 char *file, *driver; 345 int fd, size; 346 347 driver = str; 348 file = strchr(str,','); 349 if (file == NULL) { 350 fprintf(stderr, "parse_iomem : failed to parse iomem\n"); 351 goto out; 352 } 353 *file = '\0'; 354 file++; 355 fd = open(file, O_RDWR, 0); 356 if (fd < 0) { 357 perror("parse_iomem - Couldn't open io file"); 358 goto out; 359 } 360 361 if (fstat64(fd, &buf) < 0) { 362 perror("parse_iomem - cannot stat_fd file"); 363 goto out_close; 364 } 365 366 new = malloc(sizeof(*new)); 367 if (new == NULL) { 368 perror("Couldn't allocate iomem_region struct"); 369 goto out_close; 370 } 371 372 size = (buf.st_size + UM_KERN_PAGE_SIZE) & ~(UM_KERN_PAGE_SIZE - 1); 373 374 *new = ((struct iomem_region) { .next = iomem_regions, 375 .driver = driver, 376 .fd = fd, 377 .size = size, 378 .phys = 0, 379 .virt = 0 }); 380 iomem_regions = new; 381 iomem_size += new->size + UM_KERN_PAGE_SIZE; 382 383 return 0; 384 out_close: 385 close(fd); 386 out: 387 return 1; 388 } 389