diff options
Diffstat (limited to 'chromium/content/browser/zygote_host/zygote_host_impl_linux.cc')
-rw-r--r-- | chromium/content/browser/zygote_host/zygote_host_impl_linux.cc | 191 |
1 files changed, 111 insertions, 80 deletions
diff --git a/chromium/content/browser/zygote_host/zygote_host_impl_linux.cc b/chromium/content/browser/zygote_host/zygote_host_impl_linux.cc index 18e9b693094..81d075688d7 100644 --- a/chromium/content/browser/zygote_host/zygote_host_impl_linux.cc +++ b/chromium/content/browser/zygote_host/zygote_host_impl_linux.cc @@ -4,6 +4,7 @@ #include "content/browser/zygote_host/zygote_host_impl_linux.h" +#include <string.h> #include <sys/socket.h> #include <sys/stat.h> #include <sys/types.h> @@ -14,16 +15,19 @@ #include "base/environment.h" #include "base/file_util.h" #include "base/files/file_enumerator.h" +#include "base/files/scoped_file.h" #include "base/linux_util.h" #include "base/logging.h" #include "base/memory/linked_ptr.h" #include "base/memory/scoped_ptr.h" +#include "base/memory/scoped_vector.h" #include "base/metrics/histogram.h" #include "base/path_service.h" #include "base/posix/eintr_wrapper.h" #include "base/posix/unix_domain_socket_linux.h" #include "base/process/launch.h" #include "base/process/memory.h" +#include "base/process/process_handle.h" #include "base/strings/string_number_conversions.h" #include "base/strings/string_util.h" #include "base/strings/utf_string_conversions.h" @@ -45,6 +49,26 @@ namespace content { +// Receive a fixed message on fd and return the sender's PID. +// Returns true if the message received matches the expected message. +static bool ReceiveFixedMessage(int fd, + const char* expect_msg, + size_t expect_len, + base::ProcessId* sender_pid) { + char buf[expect_len + 1]; + ScopedVector<base::ScopedFD> fds_vec; + + const ssize_t len = UnixDomainSocket::RecvMsgWithPid( + fd, buf, sizeof(buf), &fds_vec, sender_pid); + if (static_cast<size_t>(len) != expect_len) + return false; + if (memcmp(buf, expect_msg, expect_len) != 0) + return false; + if (!fds_vec.empty()) + return false; + return true; +} + // static ZygoteHost* ZygoteHost::GetInstance() { return ZygoteHostImpl::GetInstance(); @@ -81,17 +105,12 @@ void ZygoteHostImpl::Init(const std::string& sandbox_cmd) { cmd_line.AppendSwitchASCII(switches::kProcessType, switches::kZygoteProcess); int fds[2]; -#if defined(OS_FREEBSD) || defined(OS_OPENBSD) - // The BSDs often don't support SOCK_SEQPACKET yet, so fall back to - // SOCK_DGRAM if necessary. - if (socketpair(PF_UNIX, SOCK_SEQPACKET, 0, fds) != 0) - CHECK(socketpair(PF_UNIX, SOCK_DGRAM, 0, fds) == 0); -#else - CHECK(socketpair(PF_UNIX, SOCK_SEQPACKET, 0, fds) == 0); -#endif + CHECK(socketpair(AF_UNIX, SOCK_SEQPACKET, 0, fds) == 0); + CHECK(UnixDomainSocket::EnableReceiveProcessId(fds[0])); base::FileHandleMappingVector fds_to_map; fds_to_map.push_back(std::make_pair(fds[1], kZygoteSocketPairFd)); + base::LaunchOptions options; const CommandLine& browser_command_line = *CommandLine::ForCurrentProcess(); if (browser_command_line.HasSwitch(switches::kZygoteCmdPrefix)) { cmd_line.PrependWrapper( @@ -112,7 +131,6 @@ void ZygoteHostImpl::Init(const std::string& sandbox_cmd) { // Zygote process needs to know what resources to have loaded when it // becomes a renderer process. switches::kForceDeviceScaleFactor, - switches::kTouchOptimizedUI, switches::kNoSandbox, }; @@ -126,79 +144,49 @@ void ZygoteHostImpl::Init(const std::string& sandbox_cmd) { // A non empty sandbox_cmd means we want a SUID sandbox. using_suid_sandbox_ = !sandbox_cmd.empty(); - if (using_suid_sandbox_) { - struct stat st; - if (stat(sandbox_binary_.c_str(), &st) != 0) { - LOG(FATAL) << "The SUID sandbox helper binary is missing: " - << sandbox_binary_ << " Aborting now."; - } - - if (access(sandbox_binary_.c_str(), X_OK) == 0 && - (st.st_uid == 0) && - (st.st_mode & S_ISUID) && - (st.st_mode & S_IXOTH)) { - cmd_line.PrependWrapper(sandbox_binary_); - - scoped_ptr<sandbox::SetuidSandboxClient> - sandbox_client(sandbox::SetuidSandboxClient::Create()); - sandbox_client->SetupLaunchEnvironment(); - } else { - LOG(FATAL) << "The SUID sandbox helper binary was found, but is not " - "configured correctly. Rather than run without sandboxing " - "I'm aborting now. You need to make sure that " - << sandbox_binary_ << " is owned by root and has mode 4755."; - } - } - // Start up the sandbox host process and get the file descriptor for the // renderers to talk to it. const int sfd = RenderSandboxHostLinux::GetInstance()->GetRendererSocket(); fds_to_map.push_back(std::make_pair(sfd, GetSandboxFD())); - int dummy_fd = -1; + base::ScopedFD dummy_fd; if (using_suid_sandbox_) { - dummy_fd = socket(PF_UNIX, SOCK_DGRAM, 0); - CHECK(dummy_fd >= 0); - fds_to_map.push_back(std::make_pair(dummy_fd, kZygoteIdFd)); + scoped_ptr<sandbox::SetuidSandboxClient> + sandbox_client(sandbox::SetuidSandboxClient::Create()); + sandbox_client->PrependWrapper(&cmd_line); + sandbox_client->SetupLaunchOptions(&options, &fds_to_map, &dummy_fd); + sandbox_client->SetupLaunchEnvironment(); } base::ProcessHandle process = -1; - base::LaunchOptions options; options.fds_to_remap = &fds_to_map; base::LaunchProcess(cmd_line.argv(), options, &process); CHECK(process != -1) << "Failed to launch zygote process"; + dummy_fd.reset(); if (using_suid_sandbox_) { - // In the SUID sandbox, the real zygote is forked from the sandbox. - // We need to look for it. - // But first, wait for the zygote to tell us it's running. - // The sending code is in content/browser/zygote_main_linux.cc. - std::vector<int> fds_vec; - const int kExpectedLength = sizeof(kZygoteHelloMessage); - char buf[kExpectedLength]; - const ssize_t len = UnixDomainSocket::RecvMsg(fds[0], buf, sizeof(buf), - &fds_vec); - CHECK(len == kExpectedLength) << "Incorrect zygote magic length"; - CHECK(0 == strcmp(buf, kZygoteHelloMessage)) - << "Incorrect zygote hello"; - - std::string inode_output; - ino_t inode = 0; - // Figure out the inode for |dummy_fd|, close |dummy_fd| on our end, - // and find the zygote process holding |dummy_fd|. - if (base::FileDescriptorGetInode(&inode, dummy_fd)) { - close(dummy_fd); - std::vector<std::string> get_inode_cmdline; - get_inode_cmdline.push_back(sandbox_binary_); - get_inode_cmdline.push_back(base::kFindInodeSwitch); - get_inode_cmdline.push_back(base::Int64ToString(inode)); - CommandLine get_inode_cmd(get_inode_cmdline); - if (base::GetAppOutput(get_inode_cmd, &inode_output)) { - base::StringToInt(inode_output, &pid_); - } - } - CHECK(pid_ > 0) << "Did not find zygote process (using sandbox binary " - << sandbox_binary_ << ")"; + // The SUID sandbox will execute the zygote in a new PID namespace, and + // the main zygote process will then fork from there. Watch now our + // elaborate dance to find and validate the zygote's PID. + + // First we receive a message from the zygote boot process. + base::ProcessId boot_pid; + CHECK(ReceiveFixedMessage( + fds[0], kZygoteBootMessage, sizeof(kZygoteBootMessage), &boot_pid)); + + // Within the PID namespace, the zygote boot process thinks it's PID 1, + // but its real PID can never be 1. This gives us a reliable test that + // the kernel is translating the sender's PID to our namespace. + CHECK_GT(boot_pid, 1) + << "Received invalid process ID for zygote; kernel might be too old? " + "See crbug.com/357670 or try using --" + << switches::kDisableSetuidSandbox << " to workaround."; + + // Now receive the message that the zygote's ready to go, along with the + // main zygote process's ID. + CHECK(ReceiveFixedMessage( + fds[0], kZygoteHelloMessage, sizeof(kZygoteHelloMessage), &pid_)); + CHECK_GT(pid_, 1); if (process != pid_) { // Reap the sandbox. @@ -306,6 +294,12 @@ pid_t ZygoteHostImpl::ForkRequest( DCHECK(init_); Pickle pickle; + int raw_socks[2]; + PCHECK(0 == socketpair(AF_UNIX, SOCK_SEQPACKET, 0, raw_socks)); + base::ScopedFD my_sock(raw_socks[0]); + base::ScopedFD peer_sock(raw_socks[1]); + CHECK(UnixDomainSocket::EnableReceiveProcessId(my_sock.get())); + pickle.WriteInt(kZygoteCommandFork); pickle.WriteString(process_type); pickle.WriteInt(argv.size()); @@ -313,12 +307,19 @@ pid_t ZygoteHostImpl::ForkRequest( i = argv.begin(); i != argv.end(); ++i) pickle.WriteString(*i); - pickle.WriteInt(mapping.size()); + // Fork requests contain one file descriptor for the PID oracle, and one + // more for each file descriptor mapping for the child process. + const size_t num_fds_to_send = 1 + mapping.size(); + pickle.WriteInt(num_fds_to_send); std::vector<int> fds; - // Scoped pointers cannot be stored in containers, so we have to use a - // linked_ptr. - std::vector<linked_ptr<file_util::ScopedFD> > autodelete_fds; + ScopedVector<base::ScopedFD> autoclose_fds; + + // First FD to send is peer_sock. + fds.push_back(peer_sock.get()); + autoclose_fds.push_back(new base::ScopedFD(peer_sock.Pass())); + + // The rest come from mapping. for (std::vector<FileDescriptorInfo>::const_iterator i = mapping.begin(); i != mapping.end(); ++i) { pickle.WriteUInt32(i->id); @@ -326,17 +327,46 @@ pid_t ZygoteHostImpl::ForkRequest( if (i->fd.auto_close) { // Auto-close means we need to close the FDs after they have been passed // to the other process. - linked_ptr<file_util::ScopedFD> ptr( - new file_util::ScopedFD(&(fds.back()))); - autodelete_fds.push_back(ptr); + autoclose_fds.push_back(new base::ScopedFD(i->fd.fd)); } } + // Sanity check that we've populated |fds| correctly. + DCHECK_EQ(num_fds_to_send, fds.size()); + pid_t pid; { base::AutoLock lock(control_lock_); if (!SendMessage(pickle, &fds)) return base::kNullProcessHandle; + autoclose_fds.clear(); + + { + char buf[sizeof(kZygoteChildPingMessage) + 1]; + ScopedVector<base::ScopedFD> recv_fds; + base::ProcessId real_pid; + + ssize_t n = UnixDomainSocket::RecvMsgWithPid( + my_sock.get(), buf, sizeof(buf), &recv_fds, &real_pid); + if (n != sizeof(kZygoteChildPingMessage) || + 0 != memcmp(buf, + kZygoteChildPingMessage, + sizeof(kZygoteChildPingMessage))) { + // Zygote children should still be trustworthy when they're supposed to + // ping us, so something's broken if we don't receive a valid ping. + LOG(ERROR) << "Did not receive ping from zygote child"; + NOTREACHED(); + real_pid = -1; + } + my_sock.reset(); + + // Always send PID back to zygote. + Pickle pid_pickle; + pid_pickle.WriteInt(kZygoteCommandForkRealPID); + pid_pickle.WriteInt(real_pid); + if (!SendMessage(pid_pickle, NULL)) + return base::kNullProcessHandle; + } // Read the reply, which pickles the PID and an optional UMA enumeration. static const unsigned kMaxReplyLength = 2048; @@ -448,7 +478,12 @@ void ZygoteHostImpl::AdjustRendererOOMScore(base::ProcessHandle pid, adj_oom_score_cmdline.push_back(base::IntToString(score)); base::ProcessHandle sandbox_helper_process; - if (base::LaunchProcess(adj_oom_score_cmdline, base::LaunchOptions(), + base::LaunchOptions options; + + // sandbox_helper_process is a setuid binary. + options.allow_new_privs = true; + + if (base::LaunchProcess(adj_oom_score_cmdline, options, &sandbox_helper_process)) { base::EnsureProcessGetsReaped(sandbox_helper_process); } @@ -524,10 +559,6 @@ pid_t ZygoteHostImpl::GetPid() const { return pid_; } -pid_t ZygoteHostImpl::GetSandboxHelperPid() const { - return RenderSandboxHostLinux::GetInstance()->pid(); -} - int ZygoteHostImpl::GetSandboxStatus() const { if (have_read_sandbox_status_word_) return sandbox_status_; |