| OLD | NEW |
| 1 // Copyright (c) 2012 The Chromium Authors. All rights reserved. | 1 // Copyright (c) 2012 The Chromium Authors. All rights reserved. |
| 2 // Use of this source code is governed by a BSD-style license that can be | 2 // Use of this source code is governed by a BSD-style license that can be |
| 3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
| 4 | 4 |
| 5 #include "content/zygote/zygote_linux.h" | 5 #include "content/zygote/zygote_linux.h" |
| 6 | 6 |
| 7 #include <fcntl.h> | 7 #include <fcntl.h> |
| 8 #include <string.h> | 8 #include <string.h> |
| 9 #include <sys/socket.h> | 9 #include <sys/socket.h> |
| 10 #include <sys/types.h> | 10 #include <sys/types.h> |
| (...skipping 65 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 76 // We need to accept SIGCHLD, even though our handler is a no-op because | 76 // We need to accept SIGCHLD, even though our handler is a no-op because |
| 77 // otherwise we cannot wait on children. (According to POSIX 2001.) | 77 // otherwise we cannot wait on children. (According to POSIX 2001.) |
| 78 struct sigaction action; | 78 struct sigaction action; |
| 79 memset(&action, 0, sizeof(action)); | 79 memset(&action, 0, sizeof(action)); |
| 80 action.sa_handler = &SIGCHLDHandler; | 80 action.sa_handler = &SIGCHLDHandler; |
| 81 CHECK(sigaction(SIGCHLD, &action, NULL) == 0); | 81 CHECK(sigaction(SIGCHLD, &action, NULL) == 0); |
| 82 | 82 |
| 83 if (UsingSUIDSandbox()) { | 83 if (UsingSUIDSandbox()) { |
| 84 // Let the ZygoteHost know we are ready to go. | 84 // Let the ZygoteHost know we are ready to go. |
| 85 // The receiving code is in content/browser/zygote_host_linux.cc. | 85 // The receiving code is in content/browser/zygote_host_linux.cc. |
| 86 std::vector<int> empty; | |
| 87 bool r = UnixDomainSocket::SendMsg(kZygoteSocketPairFd, | 86 bool r = UnixDomainSocket::SendMsg(kZygoteSocketPairFd, |
| 88 kZygoteHelloMessage, | 87 kZygoteHelloMessage, |
| 89 sizeof(kZygoteHelloMessage), empty); | 88 sizeof(kZygoteHelloMessage), |
| 89 std::vector<int>()); |
| 90 #if defined(OS_CHROMEOS) | 90 #if defined(OS_CHROMEOS) |
| 91 LOG_IF(WARNING, !r) << "Sending zygote magic failed"; | 91 LOG_IF(WARNING, !r) << "Sending zygote magic failed"; |
| 92 // Exit normally on chromeos because session manager may send SIGTERM | 92 // Exit normally on chromeos because session manager may send SIGTERM |
| 93 // right after the process starts and it may fail to send zygote magic | 93 // right after the process starts and it may fail to send zygote magic |
| 94 // number to browser process. | 94 // number to browser process. |
| 95 if (!r) | 95 if (!r) |
| 96 _exit(RESULT_CODE_NORMAL_EXIT); | 96 _exit(RESULT_CODE_NORMAL_EXIT); |
| 97 #else | 97 #else |
| 98 CHECK(r) << "Sending zygote magic failed"; | 98 CHECK(r) << "Sending zygote magic failed"; |
| 99 #endif | 99 #endif |
| (...skipping 197 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 297 int Zygote::ForkWithRealPid(const std::string& process_type, | 297 int Zygote::ForkWithRealPid(const std::string& process_type, |
| 298 const base::GlobalDescriptors::Mapping& fd_mapping, | 298 const base::GlobalDescriptors::Mapping& fd_mapping, |
| 299 const std::string& channel_id, | 299 const std::string& channel_id, |
| 300 std::string* uma_name, | 300 std::string* uma_name, |
| 301 int* uma_sample, | 301 int* uma_sample, |
| 302 int* uma_boundary_value) { | 302 int* uma_boundary_value) { |
| 303 const bool use_helper = (helper_ && helper_->CanHelp(process_type, | 303 const bool use_helper = (helper_ && helper_->CanHelp(process_type, |
| 304 uma_name, | 304 uma_name, |
| 305 uma_sample, | 305 uma_sample, |
| 306 uma_boundary_value)); | 306 uma_boundary_value)); |
| 307 int dummy_fd; | |
| 308 ino_t dummy_inode; | |
| 309 int pipe_fds[2] = { -1, -1 }; | |
| 310 base::ProcessId pid = 0; | |
| 311 | 307 |
| 312 dummy_fd = socket(PF_UNIX, SOCK_DGRAM, 0); | 308 int pipe_fds[2] = {-1, -1}; |
| 313 if (dummy_fd < 0) { | |
| 314 LOG(ERROR) << "Failed to create dummy FD"; | |
| 315 goto error; | |
| 316 } | |
| 317 if (!base::FileDescriptorGetInode(&dummy_inode, dummy_fd)) { | |
| 318 LOG(ERROR) << "Failed to get inode for dummy FD"; | |
| 319 goto error; | |
| 320 } | |
| 321 if (pipe(pipe_fds) != 0) { | 309 if (pipe(pipe_fds) != 0) { |
| 322 LOG(ERROR) << "Failed to create pipe"; | 310 LOG(ERROR) << "Failed to create pipe"; |
| 323 goto error; | 311 return -1; |
| 324 } | 312 } |
| 313 base::ScopedFD read_pipe(pipe_fds[0]); |
| 314 base::ScopedFD write_pipe(pipe_fds[1]); |
| 325 | 315 |
| 316 base::ProcessId pid = -1; |
| 326 if (use_helper) { | 317 if (use_helper) { |
| 327 std::vector<int> fds; | 318 std::vector<int> fds; |
| 328 int ipc_channel_fd = LookUpFd(fd_mapping, kPrimaryIPCChannel); | 319 int ipc_channel_fd = LookUpFd(fd_mapping, kPrimaryIPCChannel); |
| 329 if (ipc_channel_fd < 0) { | 320 if (ipc_channel_fd < 0) { |
| 330 DLOG(ERROR) << "Failed to find kPrimaryIPCChannel in FD mapping"; | 321 DLOG(ERROR) << "Failed to find kPrimaryIPCChannel in FD mapping"; |
| 331 goto error; | 322 return -1; |
| 332 } | 323 } |
| 333 fds.push_back(ipc_channel_fd); // kBrowserFDIndex | 324 fds.push_back(ipc_channel_fd); // kBrowserFDIndex |
| 334 fds.push_back(dummy_fd); // kDummyFDIndex | 325 fds.push_back(write_pipe.get()); // kParentFDIndex |
| 335 fds.push_back(pipe_fds[0]); // kParentFDIndex | |
| 336 pid = helper_->Fork(process_type, fds, channel_id); | 326 pid = helper_->Fork(process_type, fds, channel_id); |
| 337 } else { | 327 } else { |
| 338 pid = fork(); | 328 pid = fork(); |
| 339 } | 329 } |
| 340 if (pid < 0) { | 330 if (pid < 0) { |
| 341 goto error; | 331 return -1; |
| 342 } else if (pid == 0) { | 332 } |
| 333 if (pid == 0) { |
| 343 // In the child process. | 334 // In the child process. |
| 344 close(pipe_fds[1]); | 335 read_pipe.reset(); |
| 336 |
| 345 base::ProcessId real_pid; | 337 base::ProcessId real_pid; |
| 346 // Wait until the parent process has discovered our PID. We | 338 if (!SendRealPidToZygote(write_pipe.get(), &real_pid)) { |
| 347 // should not fork any child processes (which the seccomp | 339 LOG(FATAL) << "Failed to synchronise with parent process"; |
| 348 // sandbox does) until then, because that can interfere with the | |
| 349 // parent's discovery of our PID. | |
| 350 if (!base::ReadFromFD(pipe_fds[0], reinterpret_cast<char*>(&real_pid), | |
| 351 sizeof(real_pid))) { | |
| 352 LOG(FATAL) << "Failed to synchronise with parent zygote process"; | |
| 353 } | 340 } |
| 354 if (real_pid <= 0) { | 341 write_pipe.reset(); |
| 355 LOG(FATAL) << "Invalid pid from parent zygote"; | 342 |
| 356 } | |
| 357 #if defined(OS_LINUX) | 343 #if defined(OS_LINUX) |
| 358 // Sandboxed processes need to send the global, non-namespaced PID when | 344 // Sandboxed processes need to send the global, non-namespaced PID when |
| 359 // setting up an IPC channel to their parent. | 345 // setting up an IPC channel to their parent. |
| 360 IPC::Channel::SetGlobalPid(real_pid); | 346 IPC::Channel::SetGlobalPid(real_pid); |
| 361 // Force the real PID so chrome event data have a PID that corresponds | 347 // Force the real PID so chrome event data have a PID that corresponds |
| 362 // to system trace event data. | 348 // to system trace event data. |
| 363 base::debug::TraceLog::GetInstance()->SetProcessID( | 349 base::debug::TraceLog::GetInstance()->SetProcessID( |
| 364 static_cast<int>(real_pid)); | 350 static_cast<int>(real_pid)); |
| 365 #endif | 351 #endif |
| 366 close(pipe_fds[0]); | |
| 367 close(dummy_fd); | |
| 368 return 0; | 352 return 0; |
| 369 } else { | |
| 370 // In the parent process. | |
| 371 close(dummy_fd); | |
| 372 dummy_fd = -1; | |
| 373 close(pipe_fds[0]); | |
| 374 pipe_fds[0] = -1; | |
| 375 base::ProcessId real_pid; | |
| 376 if (UsingSUIDSandbox()) { | |
| 377 uint8_t reply_buf[512]; | |
| 378 Pickle request; | |
| 379 request.WriteInt(LinuxSandbox::METHOD_GET_CHILD_WITH_INODE); | |
| 380 request.WriteUInt64(dummy_inode); | |
| 381 | |
| 382 const ssize_t r = UnixDomainSocket::SendRecvMsg( | |
| 383 GetSandboxFD(), reply_buf, sizeof(reply_buf), NULL, | |
| 384 request); | |
| 385 if (r == -1) { | |
| 386 LOG(ERROR) << "Failed to get child process's real PID"; | |
| 387 goto error; | |
| 388 } | |
| 389 | |
| 390 Pickle reply(reinterpret_cast<char*>(reply_buf), r); | |
| 391 PickleIterator iter(reply); | |
| 392 if (!reply.ReadInt(&iter, &real_pid)) | |
| 393 goto error; | |
| 394 if (real_pid <= 0) { | |
| 395 // METHOD_GET_CHILD_WITH_INODE failed. Did the child die already? | |
| 396 LOG(ERROR) << "METHOD_GET_CHILD_WITH_INODE failed"; | |
| 397 goto error; | |
| 398 } | |
| 399 } else { | |
| 400 // If no SUID sandbox is involved then no pid translation is | |
| 401 // necessary. | |
| 402 real_pid = pid; | |
| 403 } | |
| 404 | |
| 405 // Now set-up this process to be tracked by the Zygote. | |
| 406 if (process_info_map_.find(real_pid) != process_info_map_.end()) { | |
| 407 LOG(ERROR) << "Already tracking PID " << real_pid; | |
| 408 NOTREACHED(); | |
| 409 } | |
| 410 process_info_map_[real_pid].internal_pid = pid; | |
| 411 process_info_map_[real_pid].started_from_helper = use_helper; | |
| 412 | |
| 413 // If we're using a helper, we still need to let the child process know | |
| 414 // we've discovered its real PID, but we don't actually reveal the PID. | |
| 415 const base::ProcessId pid_for_child = use_helper ? 0 : real_pid; | |
| 416 ssize_t written = | |
| 417 HANDLE_EINTR(write(pipe_fds[1], &pid_for_child, sizeof(pid_for_child))); | |
| 418 if (written != sizeof(pid_for_child)) { | |
| 419 LOG(ERROR) << "Failed to synchronise with child process"; | |
| 420 goto error; | |
| 421 } | |
| 422 close(pipe_fds[1]); | |
| 423 return real_pid; | |
| 424 } | 353 } |
| 425 | 354 |
| 426 error: | 355 // In the parent process. |
| 427 if (pid > 0) { | 356 write_pipe.reset(); |
| 357 |
| 358 base::ProcessId real_pid; |
| 359 if (!base::ReadFromFD(read_pipe.get(), |
| 360 reinterpret_cast<char*>(&real_pid), |
| 361 sizeof(real_pid))) { |
| 362 LOG(ERROR) << "Failed to synchronise with child process"; |
| 363 // TODO(mdempsky): Dispatch to helper |
| 428 if (waitpid(pid, NULL, WNOHANG) == -1) | 364 if (waitpid(pid, NULL, WNOHANG) == -1) |
| 429 LOG(ERROR) << "Failed to wait for process"; | 365 LOG(ERROR) << "Failed to wait for process"; |
| 366 return -1; |
| 430 } | 367 } |
| 431 if (dummy_fd >= 0) | 368 read_pipe.reset(); |
| 432 close(dummy_fd); | 369 |
| 433 if (pipe_fds[0] >= 0) | 370 // Now set-up this process to be tracked by the Zygote. |
| 434 close(pipe_fds[0]); | 371 if (process_info_map_.find(real_pid) != process_info_map_.end()) { |
| 435 if (pipe_fds[1] >= 0) | 372 LOG(ERROR) << "Already tracking PID " << real_pid; |
| 436 close(pipe_fds[1]); | 373 NOTREACHED(); |
| 437 return -1; | 374 } |
| 375 process_info_map_[real_pid].internal_pid = pid; |
| 376 process_info_map_[real_pid].started_from_helper = use_helper; |
| 377 |
| 378 return real_pid; |
| 438 } | 379 } |
| 439 | 380 |
| 440 base::ProcessId Zygote::ReadArgsAndFork(const Pickle& pickle, | 381 base::ProcessId Zygote::ReadArgsAndFork(const Pickle& pickle, |
| 441 PickleIterator iter, | 382 PickleIterator iter, |
| 442 std::vector<int>& fds, | 383 std::vector<int>& fds, |
| 443 std::string* uma_name, | 384 std::string* uma_name, |
| 444 int* uma_sample, | 385 int* uma_sample, |
| 445 int* uma_boundary_value) { | 386 int* uma_boundary_value) { |
| 446 std::vector<std::string> args; | 387 std::vector<std::string> args; |
| 447 int argc = 0; | 388 int argc = 0; |
| (...skipping 34 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 482 static_cast<uint32_t>(kSandboxIPCChannel), GetSandboxFD())); | 423 static_cast<uint32_t>(kSandboxIPCChannel), GetSandboxFD())); |
| 483 | 424 |
| 484 // Returns twice, once per process. | 425 // Returns twice, once per process. |
| 485 base::ProcessId child_pid = ForkWithRealPid(process_type, mapping, channel_id, | 426 base::ProcessId child_pid = ForkWithRealPid(process_type, mapping, channel_id, |
| 486 uma_name, uma_sample, | 427 uma_name, uma_sample, |
| 487 uma_boundary_value); | 428 uma_boundary_value); |
| 488 if (!child_pid) { | 429 if (!child_pid) { |
| 489 // This is the child process. | 430 // This is the child process. |
| 490 | 431 |
| 491 close(kZygoteSocketPairFd); // Our socket from the browser. | 432 close(kZygoteSocketPairFd); // Our socket from the browser. |
| 492 if (UsingSUIDSandbox()) | |
| 493 close(kZygoteIdFd); // Another socket from the browser. | |
| 494 base::GlobalDescriptors::GetInstance()->Reset(mapping); | 433 base::GlobalDescriptors::GetInstance()->Reset(mapping); |
| 495 | 434 |
| 496 // Reset the process-wide command line to our new command line. | 435 // Reset the process-wide command line to our new command line. |
| 497 CommandLine::Reset(); | 436 CommandLine::Reset(); |
| 498 CommandLine::Init(0, NULL); | 437 CommandLine::Init(0, NULL); |
| 499 CommandLine::ForCurrentProcess()->InitFromArgv(args); | 438 CommandLine::ForCurrentProcess()->InitFromArgv(args); |
| 500 | 439 |
| 501 // Update the process title. The argv was already cached by the call to | 440 // Update the process title. The argv was already cached by the call to |
| 502 // SetProcessTitleFromCommandLine in ChromeMain, so we can pass NULL here | 441 // SetProcessTitleFromCommandLine in ChromeMain, so we can pass NULL here |
| 503 // (we don't have the original argv at this point). | 442 // (we don't have the original argv at this point). |
| (...skipping 48 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 552 PickleIterator iter) { | 491 PickleIterator iter) { |
| 553 if (HANDLE_EINTR(write(fd, &sandbox_flags_, sizeof(sandbox_flags_))) != | 492 if (HANDLE_EINTR(write(fd, &sandbox_flags_, sizeof(sandbox_flags_))) != |
| 554 sizeof(sandbox_flags_)) { | 493 sizeof(sandbox_flags_)) { |
| 555 PLOG(ERROR) << "write"; | 494 PLOG(ERROR) << "write"; |
| 556 } | 495 } |
| 557 | 496 |
| 558 return false; | 497 return false; |
| 559 } | 498 } |
| 560 | 499 |
| 561 } // namespace content | 500 } // namespace content |
| OLD | NEW |