Chromium Code Reviews| OLD | NEW |
|---|---|
| (Empty) | |
| 1 /* | |
| 2 * Copyright (c) 2012 The Native Client Authors. All rights reserved. | |
| 3 * Use of this source code is governed by a BSD-style license that can be | |
| 4 * found in the LICENSE file. | |
| 5 */ | |
| 6 | |
| 7 #include "native_client/src/trusted/platform/nacl_process.h" | |
| 8 | |
| 9 #include <errno.h> | |
| 10 #include <fcntl.h> | |
| 11 #include <limits.h> | |
| 12 #include <signal.h> | |
| 13 #include <stdlib.h> | |
| 14 #include <sys/resource.h> | |
| 15 #include <sys/time.h> | |
| 16 #include <sys/types.h> | |
| 17 #include <sys/wait.h> | |
| 18 #include <unistd.h> | |
| 19 | |
| 20 #if NACL_LINUX | |
| 21 #include <dirent.h> | |
| 22 #include <sys/stat.h> | |
| 23 #include <sys/syscall.h> | |
| 24 #elif NACL_OSX | |
| 25 #include <mach/mach.h> | |
| 26 #endif | |
| 27 | |
| 28 #include "native_client/src/include/nacl_macros.h" | |
| 29 #include "native_client/src/include/portability.h" | |
| 30 #include "native_client/src/include/portability_string.h" | |
| 31 | |
| 32 #include "native_client/src/shared/platform/nacl_check.h" | |
| 33 #include "native_client/src/shared/platform/nacl_log.h" | |
| 34 #include "native_client/src/shared/platform/nacl_sync.h" | |
| 35 #include "native_client/src/shared/platform/nacl_sync_checked.h" | |
| 36 | |
| 37 #include "native_client/src/trusted/service_runtime/nacl_signal.h" | |
| 38 | |
| 39 #if NACL_OSX | |
| 40 #include <crt_externs.h> | |
| 41 #include <sys/event.h> | |
| 42 #else | |
| 43 extern char **environ; | |
| 44 #endif | |
| 45 | |
| 46 #if NACL_LINUX | |
| 47 struct linux_dirent { | |
| 48 long d_ino; | |
| 49 off_t d_off; | |
| 50 unsigned short d_reclen; | |
| 51 char d_name[]; | |
| 52 }; | |
| 53 #endif | |
| 54 | |
| 55 static const int kSignals[] = { | |
|
Mark Seaborn
2012/08/24 00:22:42
This is duplicating code from nacl_signal.c.
| |
| 56 #if NACL_LINUX | |
| 57 SIGSTKFLT, | |
| 58 NACL_THREAD_SUSPEND_SIGNAL, | |
| 59 #endif | |
| 60 SIGINT, SIGQUIT, SIGILL, SIGTRAP, SIGBUS, SIGFPE, SIGSEGV | |
| 61 }; | |
| 62 | |
| 63 #if NACL_OSX | |
| 64 #define NACL_MACH_EXCEPTION_MASK EXC_MASK_BAD_ACCESS | |
|
Mark Seaborn
2012/08/24 00:22:42
This is duplicating code from osx/mach_exception_h
| |
| 65 #endif | |
| 66 | |
| 67 #if NACL_LINUX | |
| 68 static const rlim_t kSystemDefaultMaxFds = 8192; | |
| 69 static const char *kFDDir = "/proc/self/fd"; | |
| 70 #else | |
| 71 static const rlim_t kSystemDefaultMaxFds = 256; | |
| 72 /*static const char *kFDDir = "/dev/fd";*/ | |
| 73 #endif | |
| 74 | |
| 75 static void NaClResetSignalHandlers() { | |
| 76 struct sigaction dfl; | |
| 77 size_t i; | |
| 78 #if NACL_OSX | |
| 79 int rv = task_set_exception_ports(mach_task_self(), NACL_MACH_EXCEPTION_MASK, | |
| 80 MACH_PORT_NULL, EXCEPTION_DEFAULT, | |
| 81 THREAD_STATE_NONE); | |
| 82 if (rv != KERN_SUCCESS) { | |
| 83 NaClLog(LOG_FATAL, "Failed to unregister default exception handler.\n"); | |
| 84 } | |
| 85 #endif | |
| 86 | |
| 87 memset(&dfl, 0, sizeof dfl); | |
| 88 dfl.sa_handler = SIG_DFL; | |
| 89 CHECK(sigemptyset(&dfl.sa_mask) == 0); | |
| 90 | |
| 91 for (i = 0; i < NACL_ARRAY_SIZE(kSignals); i++) { | |
| 92 if (sigaction(kSignals[i], &dfl, NULL) != 0) { | |
| 93 NaClLog(LOG_FATAL, | |
| 94 "Failed to unregister handler for %d with error %d\n", | |
| 95 kSignals[i], errno); | |
| 96 } | |
| 97 } | |
| 98 } | |
| 99 | |
| 100 static void NaClCloseAllFds() { | |
| 101 struct rlimit nofile; | |
| 102 rlim_t max_fds; | |
| 103 int fd; | |
| 104 #if NACL_LINUX | |
| 105 unsigned char buf[512]; | |
| 106 size_t offset = 0; | |
| 107 size_t size = 0; | |
| 108 int dir_fd; | |
| 109 #endif | |
| 110 | |
| 111 /* Get the maximum number of FDs possible. */ | |
| 112 if (getrlimit(RLIMIT_NOFILE, &nofile)) { | |
| 113 NaClLog(LOG_ERROR, "NaClProcessFork: getrlimit failed.\n"); | |
| 114 max_fds = kSystemDefaultMaxFds; | |
| 115 } else { | |
| 116 max_fds = nofile.rlim_cur; | |
| 117 } | |
| 118 | |
| 119 if (max_fds > INT_MAX) { | |
| 120 max_fds = INT_MAX; | |
| 121 } | |
| 122 | |
| 123 #if NACL_LINUX | |
| 124 dir_fd = open(kFDDir, O_RDONLY | O_DIRECTORY); | |
|
Mark Seaborn
2012/08/24 00:22:42
I am sceptical you really want to close all FDs.
| |
| 125 if (-1 == dir_fd) { | |
| 126 NaClLog(LOG_FATAL, | |
| 127 "NaClProcessFork: failed to open %s, error %d.\n", | |
| 128 kFDDir, errno); | |
| 129 } | |
| 130 | |
| 131 for (;;) { | |
| 132 struct linux_dirent *dirent; | |
| 133 char *endptr; | |
| 134 int rv; | |
| 135 | |
| 136 if (size != 0) { | |
| 137 dirent = (struct linux_dirent *)&buf[offset]; | |
| 138 offset += dirent->d_reclen; | |
| 139 } | |
| 140 if (offset == size) { | |
| 141 rv = syscall(__NR_getdents64, dir_fd, buf, sizeof buf); | |
|
Mark Seaborn
2012/08/24 00:22:42
Why are you using the getdents syscall directly?
| |
| 142 if (rv == 0) { | |
| 143 /* We are done, there are no more entries */ | |
| 144 break; | |
| 145 } else if (rv == -1) { | |
| 146 NaClLog(LOG_ERROR, | |
| 147 "NaClCloseAllFds: getdents64 failed error %d\n", errno); | |
| 148 break; | |
| 149 } | |
| 150 size = rv; | |
| 151 offset = 0; | |
| 152 } | |
| 153 dirent = (struct linux_dirent *)&buf[offset]; | |
| 154 | |
| 155 /* Skip . and .. entries. */ | |
| 156 if (dirent->d_name[0] == '.') { | |
| 157 continue; | |
| 158 } | |
| 159 | |
| 160 fd = strtol(dirent->d_name, &endptr, 10); | |
| 161 if (endptr != NULL || fd < 0) { | |
| 162 continue; | |
| 163 } | |
| 164 | |
| 165 if (fd == STDIN_FILENO || | |
| 166 fd == STDOUT_FILENO || | |
| 167 fd == STDERR_FILENO || | |
| 168 fd == dir_fd) { | |
| 169 continue; | |
| 170 } | |
| 171 | |
| 172 /* Valgrind opens FDs >= |max_fds|, handle them here. */ | |
| 173 if (fd < (int) max_fds) { | |
| 174 DCHECK(close(fd) == 0); | |
| 175 } | |
| 176 } | |
| 177 | |
| 178 DCHECK(close(dir_fd) == 0); | |
| 179 #else | |
| 180 for (fd = 0; fd < (int) max_fds; ++fd) { | |
| 181 if (fd == STDIN_FILENO || | |
| 182 fd == STDOUT_FILENO || | |
| 183 fd == STDERR_FILENO) { | |
| 184 continue; | |
| 185 } | |
| 186 | |
| 187 /* | |
| 188 * We deliberately ignore any errors because we do | |
|
Mark Seaborn
2012/08/24 00:22:42
It would be better to check for EBADF and fail if
| |
| 189 * not know whether the filedescriptor is valid. | |
| 190 */ | |
| 191 close(fd); | |
| 192 } | |
| 193 #endif | |
| 194 } | |
| 195 | |
| 196 int NaClProcessLaunch(struct NaClProcess *npp, | |
| 197 char *const *argv, | |
| 198 char *const *envp, | |
| 199 int flags) { | |
| 200 pid_t pid; | |
| 201 | |
| 202 NaClLog(2, | |
| 203 "NaClProcessLaunch(0x%08"NACL_PRIxPTR")\n", | |
| 204 (uintptr_t) npp); | |
| 205 | |
| 206 CHECK(npp != NULL); | |
| 207 | |
| 208 pid = fork(); | |
| 209 if (pid < 0) { | |
| 210 NaClLog(LOG_ERROR, "NaClProcessFork: fork failed\n"); | |
| 211 return 0; | |
| 212 } else if (pid == 0) { | |
| 213 /* Child process */ | |
| 214 int null_fd; | |
| 215 int new_fd; | |
| 216 | |
| 217 /* We do not want parent and child to share standard input. */ | |
| 218 null_fd = open("/dev/null", O_RDONLY); | |
|
Mark Seaborn
2012/08/24 00:22:42
This doesn't work inside an outer sandbox...
| |
| 219 if (null_fd < 0) { | |
| 220 NaClLog(LOG_ERROR, | |
| 221 "NaClProcessFork: failed to open /dev/null\n"); | |
| 222 _exit(127); | |
| 223 } | |
| 224 | |
| 225 new_fd = dup2(null_fd, STDIN_FILENO); | |
| 226 if (new_fd != STDIN_FILENO) { | |
| 227 NaClLog(LOG_ERROR, | |
| 228 "NaClProcessFork: failed to dup /dev/null for stdin\n"); | |
| 229 _exit(127); | |
| 230 } | |
| 231 DCHECK(close(null_fd) != 0); | |
| 232 | |
| 233 if (0 != (flags & NACL_PROCESS_LAUNCH_NEW_GROUP)) { | |
| 234 /* Setup new process group. */ | |
| 235 if (setpgid(0, 0) < 0) { | |
| 236 NaClLog(LOG_ERROR, | |
| 237 "NaClProcessFork: setpgid failed error %d\n", errno); | |
| 238 _exit(127); | |
| 239 } | |
| 240 } | |
| 241 | |
| 242 /* | |
| 243 * The previous signal handlers are likely to be meaningless in | |
| 244 * the child's context so we reset them to the defaults. | |
| 245 */ | |
| 246 NaClResetSignalHandlers(); | |
| 247 | |
| 248 if (0 != (flags & NACL_PROCESS_LAUNCH_CLOSE_FDS)) { | |
| 249 NaClCloseAllFds(); | |
| 250 } | |
| 251 | |
| 252 if (NULL != envp) { | |
| 253 #if NACL_OSX | |
| 254 *_NSGetEnviron() = (char **) envp; | |
| 255 #else | |
| 256 environ = (char **) envp; | |
| 257 #endif | |
| 258 } | |
| 259 | |
| 260 NaClLog(4, | |
| 261 "NaClProcessLaunch: exec application '%s'\n", | |
| 262 argv[0]); | |
| 263 | |
| 264 execvp(argv[0], argv); | |
| 265 | |
| 266 /* | |
| 267 * When successful, exec* does not return, so if we reached | |
| 268 * here, there must have been an error; report it. | |
| 269 */ | |
| 270 NaClLog(LOG_FATAL, | |
| 271 "NaclProcessSpawn: failed to execvp, error %d\n", | |
| 272 errno); | |
| 273 } | |
| 274 | |
| 275 /* Parent process */ | |
| 276 NaClLog(4, "NaClProcessFork: forked child process %d\n", pid); | |
| 277 | |
| 278 if (npp != NULL) { | |
|
Mark Seaborn
2012/08/24 00:22:42
You already did CHECK(npp != NULL) earlier
| |
| 279 npp->pid = pid; | |
| 280 } | |
| 281 | |
| 282 return 1; | |
| 283 } | |
| 284 | |
| 285 /* | |
| 286 * Attempts to kill the process identified by the given process handle. | |
| 287 * The exit_code is ignored since POSIX can't enforce that. | |
| 288 */ | |
| 289 int NaClProcessKill(struct NaClProcess *npp, int exit_code, int wait) { | |
|
Mark Seaborn
2012/08/24 00:22:42
I don't see any tests that cover this function...
| |
| 290 static unsigned int kMaxSleepMs = 1000; | |
| 291 int retval; | |
| 292 UNREFERENCED_PARAMETER(exit_code); | |
| 293 | |
| 294 NaClLog(2, | |
| 295 "NaClProcessKill(0x%08"NACL_PRIxPTR", %d, %d)\n", | |
| 296 (uintptr_t) npp, exit_code, wait); | |
| 297 | |
| 298 CHECK(npp != NULL); | |
| 299 CHECK(npp->pid > 1); | |
| 300 | |
| 301 retval = kill(npp->pid, SIGTERM); | |
|
Mark Seaborn
2012/08/24 00:22:42
Why are you trying SIGTERM and then trying SIGKILL
| |
| 302 if (-1 == retval) { | |
| 303 NaClLog(LOG_ERROR, | |
| 304 "NaClProcessKill: unable to terminate process %d\n", errno); | |
| 305 goto done; | |
| 306 } | |
| 307 | |
| 308 if (wait != 0) { | |
| 309 unsigned int sleep_ms = 4; | |
| 310 int retries = 60; | |
| 311 int exited = 0; | |
| 312 | |
| 313 /* The process may not end immediately due to pending I/O */ | |
| 314 while (retries-- > 0) { | |
| 315 pid_t pid = waitpid(npp->pid, NULL, WNOHANG); | |
| 316 if (pid == npp->pid) { | |
| 317 exited = 1; | |
| 318 break; | |
| 319 } else if (pid == -1) { | |
| 320 if (ECHILD == errno) { | |
| 321 /* | |
| 322 * The wait may fail with ECHILD if another process also waited for | |
| 323 * the same pid, causing the process state to get cleaned up. | |
| 324 */ | |
| 325 exited = 1; | |
| 326 break; | |
| 327 } | |
| 328 NaClLog(LOG_ERROR, | |
| 329 "NaClProcessKill: waitpid(%d) returned error %d\n", | |
| 330 npp->pid, errno); | |
| 331 } | |
| 332 | |
| 333 usleep(sleep_ms * 1000); | |
| 334 if (sleep_ms < kMaxSleepMs) { | |
| 335 sleep_ms *= 2; | |
| 336 } | |
| 337 } | |
| 338 | |
| 339 /* | |
| 340 * If we're waiting and the child hasn't died by now, force it | |
| 341 * with a SIGKILL. | |
| 342 */ | |
| 343 if (!exited) { | |
| 344 if (-1 == (retval = kill(npp->pid, SIGKILL))) { | |
| 345 NaClLog(LOG_ERROR, | |
| 346 "NaClProcessKill: failed to kill process %d\n", errno); | |
| 347 } | |
| 348 } | |
| 349 } | |
| 350 | |
| 351 done: | |
| 352 return retval; | |
| 353 } | |
| 354 | |
| 355 int NaClProcessGetStatus(struct NaClProcess *npp, | |
| 356 int *status) { | |
| 357 int tmp_status = 0; | |
| 358 pid_t pid; | |
| 359 | |
| 360 NaClLog(2, | |
| 361 ("NaClProcessGetStatus(0x%08"NACL_PRIxPTR | |
| 362 ", 0x%08"NACL_PRIxPTR")\n"), | |
| 363 (uintptr_t) npp, (uintptr_t) status); | |
| 364 | |
| 365 CHECK(npp != NULL); | |
| 366 | |
| 367 NaClLog(4, | |
| 368 "NaClProcessGetStatus: checking status of process %d\n", | |
| 369 (int) npp->pid); | |
| 370 | |
| 371 pid = waitpid(npp->pid, &tmp_status, WNOHANG); | |
| 372 if (pid == -1) { | |
| 373 NaClLog(LOG_ERROR, | |
| 374 "NaClProcessGetStatus: waitpid(%d) returned error %d\n", | |
| 375 npp->pid, errno); | |
| 376 return 0; | |
| 377 } else if (pid == 0) { | |
| 378 *status = NACL_PROCESS_STATUS_STILL_RUNNING; | |
| 379 goto done; | |
| 380 } | |
| 381 | |
| 382 if (WIFSIGNALED(tmp_status)) { | |
| 383 switch (WTERMSIG(tmp_status)) { | |
| 384 case SIGABRT: | |
| 385 case SIGBUS: | |
| 386 case SIGFPE: | |
| 387 case SIGILL: | |
| 388 case SIGSEGV: | |
| 389 *status = NACL_PROCESS_STATUS_CRASHED; | |
| 390 goto done; | |
| 391 case SIGINT: | |
| 392 case SIGKILL: | |
| 393 case SIGTERM: | |
| 394 *status = NACL_PROCESS_STATUS_KILLED; | |
| 395 goto done; | |
| 396 default: | |
| 397 break; | |
| 398 } | |
| 399 } | |
| 400 | |
| 401 if (WIFEXITED(tmp_status) != 0 && WEXITSTATUS(tmp_status) != 0) { | |
| 402 *status = NACL_PROCESS_STATUS_ABNORMAL_EXIT; | |
| 403 goto done; | |
| 404 } | |
| 405 *status = NACL_PROCESS_STATUS_NORMAL_EXIT; | |
| 406 | |
| 407 done: | |
| 408 return 1; | |
| 409 } | |
| 410 | |
| 411 int NaClProcessWaitForExitCode(struct NaClProcess *npp, | |
| 412 int *exit_code) { | |
| 413 int status; | |
| 414 | |
| 415 NaClLog(2, | |
| 416 ("NaClProcessWaitForExitCode(0x%08"NACL_PRIxPTR | |
| 417 ", 0x%08"NACL_PRIxPTR")\n"), | |
| 418 (uintptr_t) npp, (uintptr_t) exit_code); | |
| 419 | |
| 420 CHECK(npp != NULL); | |
| 421 | |
| 422 if (-1 == waitpid(npp->pid, &status, 0)) { | |
| 423 return 0; | |
| 424 } | |
| 425 | |
| 426 if (WIFEXITED(status) != 0) { | |
| 427 *exit_code = WEXITSTATUS(status); | |
| 428 return 1; | |
| 429 } | |
| 430 | |
| 431 /* Check whether the process signaled */ | |
| 432 CHECK(WIFSIGNALED(status) != 0); | |
| 433 return 0; | |
| 434 } | |
| OLD | NEW |