Chromium Code Reviews| 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_main.h" | 5 #include "content/zygote/zygote_main.h" |
| 6 | 6 |
| 7 #include <dlfcn.h> | 7 #include <dlfcn.h> |
| 8 #include <fcntl.h> | 8 #include <fcntl.h> |
| 9 #include <pthread.h> | 9 #include <pthread.h> |
| 10 #include <signal.h> | 10 #include <signal.h> |
| (...skipping 21 matching lines...) Expand all Loading... | |
| 32 #include "content/common/child_process_sandbox_support_impl_linux.h" | 32 #include "content/common/child_process_sandbox_support_impl_linux.h" |
| 33 #include "content/common/font_config_ipc_linux.h" | 33 #include "content/common/font_config_ipc_linux.h" |
| 34 #include "content/common/sandbox_linux/sandbox_linux.h" | 34 #include "content/common/sandbox_linux/sandbox_linux.h" |
| 35 #include "content/common/zygote_commands_linux.h" | 35 #include "content/common/zygote_commands_linux.h" |
| 36 #include "content/public/common/content_switches.h" | 36 #include "content/public/common/content_switches.h" |
| 37 #include "content/public/common/main_function_params.h" | 37 #include "content/public/common/main_function_params.h" |
| 38 #include "content/public/common/sandbox_linux.h" | 38 #include "content/public/common/sandbox_linux.h" |
| 39 #include "content/public/common/zygote_fork_delegate_linux.h" | 39 #include "content/public/common/zygote_fork_delegate_linux.h" |
| 40 #include "content/zygote/zygote_linux.h" | 40 #include "content/zygote/zygote_linux.h" |
| 41 #include "crypto/nss_util.h" | 41 #include "crypto/nss_util.h" |
| 42 #include "sandbox/linux/services/credentials.h" | |
| 42 #include "sandbox/linux/services/init_process_reaper.h" | 43 #include "sandbox/linux/services/init_process_reaper.h" |
| 43 #include "sandbox/linux/services/libc_urandom_override.h" | 44 #include "sandbox/linux/services/libc_urandom_override.h" |
| 45 #include "sandbox/linux/services/namespace_sandbox.h" | |
| 44 #include "sandbox/linux/suid/client/setuid_sandbox_client.h" | 46 #include "sandbox/linux/suid/client/setuid_sandbox_client.h" |
| 45 #include "third_party/icu/source/i18n/unicode/timezone.h" | 47 #include "third_party/icu/source/i18n/unicode/timezone.h" |
| 46 #include "third_party/skia/include/ports/SkFontConfigInterface.h" | 48 #include "third_party/skia/include/ports/SkFontConfigInterface.h" |
| 47 | 49 |
| 48 #if defined(OS_LINUX) | 50 #if defined(OS_LINUX) |
| 49 #include <sys/prctl.h> | 51 #include <sys/prctl.h> |
| 50 #endif | 52 #endif |
| 51 | 53 |
| 52 #if defined(USE_OPENSSL) | 54 #if defined(USE_OPENSSL) |
| 53 #include <openssl/rand.h> | 55 #include <openssl/rand.h> |
| (...skipping 338 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 392 // newly created process. | 394 // newly created process. |
| 393 const bool init_created = | 395 const bool init_created = |
| 394 sandbox::CreateInitProcessReaper(post_fork_parent_callback); | 396 sandbox::CreateInitProcessReaper(post_fork_parent_callback); |
| 395 if (!init_created) { | 397 if (!init_created) { |
| 396 LOG(ERROR) << "Error creating an init process to reap zombies"; | 398 LOG(ERROR) << "Error creating an init process to reap zombies"; |
| 397 return false; | 399 return false; |
| 398 } | 400 } |
| 399 return true; | 401 return true; |
| 400 } | 402 } |
| 401 | 403 |
| 404 static bool MaybeSetProcessNonDumpable() { | |
| 405 // Previously, we required that the binary be non-readable. This causes the | |
|
jln (very slow on Chromium)
2015/02/06 02:17:04
You could remove this ancient comment as part of t
rickyz (no longer on Chrome)
2015/02/06 03:01:02
Done.
| |
| 406 // kernel to mark the process as non-dumpable at startup. The thinking was | |
| 407 // that, although we were putting the renderers into a PID namespace (with | |
| 408 // the SUID sandbox), they would nonetheless be in the /same/ PID | |
| 409 // namespace. So they could ptrace each other unless they were non-dumpable. | |
| 410 // | |
| 411 // If the binary was readable, then there would be a window between process | |
| 412 // startup and the point where we set the non-dumpable flag in which a | |
| 413 // compromised renderer could ptrace attach. | |
| 414 // | |
| 415 // However, now that we have a zygote model, only the (trusted) zygote | |
| 416 // exists at this point and we can set the non-dumpable flag which is | |
| 417 // inherited by all our renderer children. | |
| 418 // | |
| 419 // Note: a non-dumpable process can't be debugged. To debug sandbox-related | |
| 420 // issues, one can specify --allow-sandbox-debugging to let the process be | |
| 421 // dumpable. | |
| 422 const base::CommandLine& command_line = | |
| 423 *base::CommandLine::ForCurrentProcess(); | |
| 424 if (command_line.HasSwitch(switches::kAllowSandboxDebugging)) { | |
| 425 // If sandbox debugging is allowed, install a handler for sandbox-related | |
| 426 // crash testing. | |
| 427 InstallSandboxCrashTestHandler(); | |
| 428 return true; | |
| 429 } | |
| 430 | |
| 431 if (prctl(PR_SET_DUMPABLE, 0) != 0) { | |
| 432 PLOG(ERROR) << "Failed to set non-dumpable flag"; | |
| 433 return false; | |
| 434 } | |
| 435 | |
| 436 return prctl(PR_GET_DUMPABLE) == 0; | |
| 437 } | |
| 438 | |
| 402 // Enter the setuid sandbox. This requires the current process to have been | 439 // Enter the setuid sandbox. This requires the current process to have been |
| 403 // created through the setuid sandbox. | 440 // created through the setuid sandbox. |
| 404 static bool EnterSuidSandbox(sandbox::SetuidSandboxClient* setuid_sandbox, | 441 static bool EnterSuidSandbox(sandbox::SetuidSandboxClient* setuid_sandbox, |
| 405 base::Closure* post_fork_parent_callback) { | 442 base::Closure* post_fork_parent_callback) { |
| 406 DCHECK(setuid_sandbox); | 443 DCHECK(setuid_sandbox); |
| 407 DCHECK(setuid_sandbox->IsSuidSandboxChild()); | 444 DCHECK(setuid_sandbox->IsSuidSandboxChild()); |
| 408 | 445 |
| 409 // Use the SUID sandbox. This still allows the seccomp sandbox to | 446 // Use the SUID sandbox. This still allows the seccomp sandbox to |
| 410 // be enabled by the process later. | 447 // be enabled by the process later. |
| 411 | 448 |
| (...skipping 14 matching lines...) Expand all Loading... | |
| 426 "is not the init process. Please, make sure the SUID " | 463 "is not the init process. Please, make sure the SUID " |
| 427 "binary is up to date."; | 464 "binary is up to date."; |
| 428 } | 465 } |
| 429 | 466 |
| 430 if (getpid() == 1) { | 467 if (getpid() == 1) { |
| 431 // The setuid sandbox has created a new PID namespace and we need | 468 // The setuid sandbox has created a new PID namespace and we need |
| 432 // to assume the role of init. | 469 // to assume the role of init. |
| 433 CHECK(CreateInitProcessReaper(post_fork_parent_callback)); | 470 CHECK(CreateInitProcessReaper(post_fork_parent_callback)); |
| 434 } | 471 } |
| 435 | 472 |
| 436 #if !defined(OS_OPENBSD) | 473 CHECK(MaybeSetProcessNonDumpable()); |
| 437 // Previously, we required that the binary be non-readable. This causes the | |
| 438 // kernel to mark the process as non-dumpable at startup. The thinking was | |
| 439 // that, although we were putting the renderers into a PID namespace (with | |
| 440 // the SUID sandbox), they would nonetheless be in the /same/ PID | |
| 441 // namespace. So they could ptrace each other unless they were non-dumpable. | |
| 442 // | |
| 443 // If the binary was readable, then there would be a window between process | |
| 444 // startup and the point where we set the non-dumpable flag in which a | |
| 445 // compromised renderer could ptrace attach. | |
| 446 // | |
| 447 // However, now that we have a zygote model, only the (trusted) zygote | |
| 448 // exists at this point and we can set the non-dumpable flag which is | |
| 449 // inherited by all our renderer children. | |
| 450 // | |
| 451 // Note: a non-dumpable process can't be debugged. To debug sandbox-related | |
| 452 // issues, one can specify --allow-sandbox-debugging to let the process be | |
| 453 // dumpable. | |
| 454 const base::CommandLine& command_line = | |
| 455 *base::CommandLine::ForCurrentProcess(); | |
| 456 if (!command_line.HasSwitch(switches::kAllowSandboxDebugging)) { | |
| 457 prctl(PR_SET_DUMPABLE, 0, 0, 0, 0); | |
| 458 if (prctl(PR_GET_DUMPABLE, 0, 0, 0, 0)) { | |
| 459 LOG(ERROR) << "Failed to set non-dumpable flag"; | |
| 460 return false; | |
| 461 } | |
| 462 } else { | |
| 463 // If sandbox debugging is allowed, install a handler for sandbox-related | |
| 464 // crash testing. | |
| 465 InstallSandboxCrashTestHandler(); | |
| 466 } | |
| 467 | |
| 468 #endif | |
| 469 | |
| 470 return true; | 474 return true; |
| 471 } | 475 } |
| 472 | 476 |
| 477 static void EnterNamespaceSandbox(base::Closure* post_fork_parent_callback) { | |
| 478 pid_t pid = getpid(); | |
| 479 if (sandbox::NamespaceSandbox::InNewPidNamespace()) { | |
| 480 CHECK_EQ(1, pid); | |
| 481 } | |
| 482 | |
| 483 CHECK(sandbox::Credentials::MoveToNewUserNS()); | |
| 484 CHECK(sandbox::Credentials::DropFileSystemAccess()); | |
| 485 CHECK(sandbox::Credentials::DropAllCapabilities()); | |
| 486 | |
| 487 // This needs to happen after moving to a new user NS, since doing so involves | |
| 488 // writing the UID/GID map. | |
|
jln (very slow on Chromium)
2015/02/06 02:17:04
That shouldn't be the case. What's happening?
rickyz (no longer on Chrome)
2015/02/06 03:01:02
I think we're hitting the normal uid/gid checks, s
| |
| 489 CHECK(MaybeSetProcessNonDumpable()); | |
| 490 | |
| 491 if (pid == 1) { | |
| 492 CHECK(CreateInitProcessReaper(post_fork_parent_callback)); | |
| 493 } | |
| 494 } | |
| 495 | |
| 473 #if defined(ADDRESS_SANITIZER) | 496 #if defined(ADDRESS_SANITIZER) |
| 474 const size_t kSanitizerMaxMessageLength = 1 * 1024 * 1024; | 497 const size_t kSanitizerMaxMessageLength = 1 * 1024 * 1024; |
| 475 | 498 |
| 476 // A helper process which collects code coverage data from the renderers over a | 499 // A helper process which collects code coverage data from the renderers over a |
| 477 // socket and dumps it to a file. See http://crbug.com/336212 for discussion. | 500 // socket and dumps it to a file. See http://crbug.com/336212 for discussion. |
| 478 static void SanitizerCoverageHelper(int socket_fd, int file_fd) { | 501 static void SanitizerCoverageHelper(int socket_fd, int file_fd) { |
| 479 scoped_ptr<char[]> buffer(new char[kSanitizerMaxMessageLength]); | 502 scoped_ptr<char[]> buffer(new char[kSanitizerMaxMessageLength]); |
| 480 while (true) { | 503 while (true) { |
| 481 ssize_t received_size = HANDLE_EINTR( | 504 ssize_t received_size = HANDLE_EINTR( |
| 482 recv(socket_fd, buffer.get(), kSanitizerMaxMessageLength, 0)); | 505 recv(socket_fd, buffer.get(), kSanitizerMaxMessageLength, 0)); |
| (...skipping 36 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 519 _exit(0); | 542 _exit(0); |
| 520 } else { | 543 } else { |
| 521 // In the parent. | 544 // In the parent. |
| 522 PCHECK(0 == IGNORE_EINTR(close(child_fd))); | 545 PCHECK(0 == IGNORE_EINTR(close(child_fd))); |
| 523 return pid; | 546 return pid; |
| 524 } | 547 } |
| 525 } | 548 } |
| 526 | 549 |
| 527 #endif // defined(ADDRESS_SANITIZER) | 550 #endif // defined(ADDRESS_SANITIZER) |
| 528 | 551 |
| 529 // If |is_suid_sandbox_child|, then make sure that the setuid sandbox is | |
| 530 // engaged. | |
| 531 static void EnterLayerOneSandbox(LinuxSandbox* linux_sandbox, | 552 static void EnterLayerOneSandbox(LinuxSandbox* linux_sandbox, |
| 532 bool is_suid_sandbox_child, | |
| 533 base::Closure* post_fork_parent_callback) { | 553 base::Closure* post_fork_parent_callback) { |
| 534 DCHECK(linux_sandbox); | 554 DCHECK(linux_sandbox); |
| 535 | 555 |
| 536 ZygotePreSandboxInit(); | 556 ZygotePreSandboxInit(); |
| 537 | 557 |
| 538 // Check that the pre-sandbox initialization didn't spawn threads. | 558 // Check that the pre-sandbox initialization didn't spawn threads. |
| 539 #if !defined(THREAD_SANITIZER) | 559 #if !defined(THREAD_SANITIZER) |
| 540 DCHECK(linux_sandbox->IsSingleThreaded()); | 560 DCHECK(linux_sandbox->IsSingleThreaded()); |
| 541 #endif | 561 #endif |
| 542 | 562 |
| 543 sandbox::SetuidSandboxClient* setuid_sandbox = | 563 sandbox::SetuidSandboxClient* setuid_sandbox = |
| 544 linux_sandbox->setuid_sandbox_client(); | 564 linux_sandbox->setuid_sandbox_client(); |
| 545 | 565 if (setuid_sandbox->IsSuidSandboxChild()) { |
| 546 if (is_suid_sandbox_child) { | |
| 547 CHECK(EnterSuidSandbox(setuid_sandbox, post_fork_parent_callback)) | 566 CHECK(EnterSuidSandbox(setuid_sandbox, post_fork_parent_callback)) |
| 548 << "Failed to enter setuid sandbox"; | 567 << "Failed to enter setuid sandbox"; |
| 568 } else if (sandbox::NamespaceSandbox::InNewUserNamespace()) { | |
| 569 EnterNamespaceSandbox(post_fork_parent_callback); | |
| 549 } | 570 } |
| 550 } | 571 } |
| 551 | 572 |
| 552 bool ZygoteMain(const MainFunctionParams& params, | 573 bool ZygoteMain(const MainFunctionParams& params, |
| 553 ScopedVector<ZygoteForkDelegate> fork_delegates) { | 574 ScopedVector<ZygoteForkDelegate> fork_delegates) { |
| 554 g_am_zygote_or_renderer = true; | 575 g_am_zygote_or_renderer = true; |
| 555 sandbox::InitLibcUrandomOverrides(); | 576 sandbox::InitLibcUrandomOverrides(); |
| 556 | 577 |
| 557 std::vector<int> fds_to_close_post_fork; | 578 std::vector<int> fds_to_close_post_fork; |
| 558 | 579 |
| (...skipping 17 matching lines...) Expand all Loading... | |
| 576 fds_to_close_post_fork.push_back(sancov_socket_fds[1]); | 597 fds_to_close_post_fork.push_back(sancov_socket_fds[1]); |
| 577 #endif | 598 #endif |
| 578 | 599 |
| 579 // Skip pre-initializing sandbox under --no-sandbox for crbug.com/444900. | 600 // Skip pre-initializing sandbox under --no-sandbox for crbug.com/444900. |
| 580 if (!base::CommandLine::ForCurrentProcess()->HasSwitch( | 601 if (!base::CommandLine::ForCurrentProcess()->HasSwitch( |
| 581 switches::kNoSandbox)) { | 602 switches::kNoSandbox)) { |
| 582 // This will pre-initialize the various sandboxes that need it. | 603 // This will pre-initialize the various sandboxes that need it. |
| 583 linux_sandbox->PreinitializeSandbox(); | 604 linux_sandbox->PreinitializeSandbox(); |
| 584 } | 605 } |
| 585 | 606 |
| 586 const bool must_enable_setuid_sandbox = | 607 const bool using_setuid_sandbox = |
| 587 linux_sandbox->setuid_sandbox_client()->IsSuidSandboxChild(); | 608 linux_sandbox->setuid_sandbox_client()->IsSuidSandboxChild(); |
| 588 if (must_enable_setuid_sandbox) { | 609 const bool using_namespace_sandbox = |
| 610 sandbox::NamespaceSandbox::InNewUserNamespace(); | |
| 611 | |
| 612 if (using_setuid_sandbox) { | |
| 589 linux_sandbox->setuid_sandbox_client()->CloseDummyFile(); | 613 linux_sandbox->setuid_sandbox_client()->CloseDummyFile(); |
| 590 | 614 |
| 591 // Let the ZygoteHost know we're booting up. | 615 // Let the ZygoteHost know we're booting up. |
| 592 CHECK(UnixDomainSocket::SendMsg(kZygoteSocketPairFd, | 616 CHECK(UnixDomainSocket::SendMsg(kZygoteSocketPairFd, |
| 593 kZygoteBootMessage, | 617 kZygoteBootMessage, |
| 594 sizeof(kZygoteBootMessage), | 618 sizeof(kZygoteBootMessage), |
| 595 std::vector<int>())); | 619 std::vector<int>())); |
| 596 } | 620 } |
| 597 | 621 |
| 598 VLOG(1) << "ZygoteMain: initializing " << fork_delegates.size() | 622 VLOG(1) << "ZygoteMain: initializing " << fork_delegates.size() |
| 599 << " fork delegates"; | 623 << " fork delegates"; |
| 600 for (ScopedVector<ZygoteForkDelegate>::iterator i = fork_delegates.begin(); | 624 const bool using_layer1_sandbox = |
| 601 i != fork_delegates.end(); | 625 using_setuid_sandbox || using_namespace_sandbox; |
| 602 ++i) { | 626 for (ZygoteForkDelegate* fork_delegate : fork_delegates) { |
| 603 (*i)->Init(GetSandboxFD(), must_enable_setuid_sandbox); | 627 fork_delegate->Init(GetSandboxFD(), using_layer1_sandbox); |
| 604 } | 628 } |
| 605 | 629 |
| 606 const std::vector<int> sandbox_fds_to_close_post_fork = | 630 const std::vector<int> sandbox_fds_to_close_post_fork = |
| 607 linux_sandbox->GetFileDescriptorsToClose(); | 631 linux_sandbox->GetFileDescriptorsToClose(); |
| 608 | 632 |
| 609 fds_to_close_post_fork.insert(fds_to_close_post_fork.end(), | 633 fds_to_close_post_fork.insert(fds_to_close_post_fork.end(), |
| 610 sandbox_fds_to_close_post_fork.begin(), | 634 sandbox_fds_to_close_post_fork.begin(), |
| 611 sandbox_fds_to_close_post_fork.end()); | 635 sandbox_fds_to_close_post_fork.end()); |
| 612 base::Closure post_fork_parent_callback = | 636 base::Closure post_fork_parent_callback = |
| 613 base::Bind(&CloseFds, fds_to_close_post_fork); | 637 base::Bind(&CloseFds, fds_to_close_post_fork); |
| 614 | 638 |
| 615 // Turn on the first layer of the sandbox if the configuration warrants it. | 639 // Turn on the first layer of the sandbox if the configuration warrants it. |
| 616 EnterLayerOneSandbox(linux_sandbox, must_enable_setuid_sandbox, | 640 EnterLayerOneSandbox(linux_sandbox, &post_fork_parent_callback); |
|
jln (very slow on Chromium)
2015/02/06 02:17:04
The reason for passing this flag (rather than rely
rickyz (no longer on Chrome)
2015/02/06 03:01:02
Yeah, I'm not a fan of the environment stuff eithe
| |
| 617 &post_fork_parent_callback); | |
| 618 | 641 |
| 619 // Extra children and file descriptors created that the Zygote must have | 642 // Extra children and file descriptors created that the Zygote must have |
| 620 // knowledge of. | 643 // knowledge of. |
| 621 std::vector<pid_t> extra_children; | 644 std::vector<pid_t> extra_children; |
| 622 std::vector<int> extra_fds; | 645 std::vector<int> extra_fds; |
| 623 | 646 |
| 624 #if defined(ADDRESS_SANITIZER) | 647 #if defined(ADDRESS_SANITIZER) |
| 625 pid_t sancov_helper_pid = ForkSanitizerCoverageHelper( | 648 pid_t sancov_helper_pid = ForkSanitizerCoverageHelper( |
| 626 sancov_socket_fds[0], sancov_socket_fds[1], sancov_file_fd.Pass(), | 649 sancov_socket_fds[0], sancov_socket_fds[1], sancov_file_fd.Pass(), |
| 627 sandbox_fds_to_close_post_fork); | 650 sandbox_fds_to_close_post_fork); |
| 628 // It's important that the zygote reaps the helper before dying. Otherwise, | 651 // It's important that the zygote reaps the helper before dying. Otherwise, |
| 629 // the destruction of the PID namespace could kill the helper before it | 652 // the destruction of the PID namespace could kill the helper before it |
| 630 // completes its I/O tasks. |sancov_helper_pid| will exit once the last | 653 // completes its I/O tasks. |sancov_helper_pid| will exit once the last |
| 631 // renderer holding the write end of |sancov_socket_fds| closes it. | 654 // renderer holding the write end of |sancov_socket_fds| closes it. |
| 632 extra_children.push_back(sancov_helper_pid); | 655 extra_children.push_back(sancov_helper_pid); |
| 633 // Sanitizer code in the renderers will inherit the write end of the socket | 656 // Sanitizer code in the renderers will inherit the write end of the socket |
| 634 // from the zygote. We must keep it open until the very end of the zygote's | 657 // from the zygote. We must keep it open until the very end of the zygote's |
| 635 // lifetime, even though we don't explicitly use it. | 658 // lifetime, even though we don't explicitly use it. |
| 636 extra_fds.push_back(sancov_socket_fds[1]); | 659 extra_fds.push_back(sancov_socket_fds[1]); |
| 637 #endif | 660 #endif |
| 638 | 661 |
| 639 int sandbox_flags = linux_sandbox->GetStatus(); | 662 int sandbox_flags = linux_sandbox->GetStatus(); |
| 640 bool setuid_sandbox_engaged = sandbox_flags & kSandboxLinuxSUID; | 663 bool setuid_sandbox_engaged = sandbox_flags & kSandboxLinuxSUID; |
| 641 CHECK_EQ(must_enable_setuid_sandbox, setuid_sandbox_engaged); | 664 CHECK_EQ(using_setuid_sandbox, setuid_sandbox_engaged); |
|
jln (very slow on Chromium)
2015/02/06 02:17:04
Want do add the same check for the namespace sandb
rickyz (no longer on Chrome)
2015/02/06 03:01:02
Once the about:sandbox change goes in, yes, though
| |
| 642 | 665 |
| 643 Zygote zygote(sandbox_flags, fork_delegates.Pass(), extra_children, | 666 Zygote zygote(sandbox_flags, fork_delegates.Pass(), extra_children, |
| 644 extra_fds); | 667 extra_fds); |
| 645 // This function call can return multiple times, once per fork(). | 668 // This function call can return multiple times, once per fork(). |
| 646 return zygote.ProcessRequests(); | 669 return zygote.ProcessRequests(); |
| 647 } | 670 } |
| 648 | 671 |
| 649 } // namespace content | 672 } // namespace content |
| OLD | NEW |