summaryrefslogtreecommitdiffstats
path: root/chromium/content/browser/zygote_host/zygote_host_impl_linux.cc
diff options
context:
space:
mode:
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.cc191
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_;