Index: content/browser/zygote_host/zygote_host_impl_linux.cc |
diff --git a/content/browser/zygote_host/zygote_host_impl_linux.cc b/content/browser/zygote_host/zygote_host_impl_linux.cc |
index 0da7da72da7fb67cde88d07b3abd89fcf364fe71..94270674f8a1645dfe18657f43c22e14f2ca4b42 100644 |
--- a/content/browser/zygote_host/zygote_host_impl_linux.cc |
+++ b/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> |
@@ -25,6 +26,7 @@ |
#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" |
@@ -46,6 +48,26 @@ |
namespace content { |
+// Returns true if |proc| is the same process as or a descendent process of |
+// |ancestor|. |
+static bool SameOrDescendantOf(base::ProcessId proc, base::ProcessId ancestor) { |
+ for (unsigned i = 0; i < 100; i++) { |
+ if (proc == ancestor) |
+ return true; |
+ |
+ // Walk up process tree. |
+ base::ProcessHandle handle; |
+ CHECK(base::OpenProcessHandle(proc, &handle)); |
+ proc = base::GetParentProcessId(handle); |
+ base::CloseProcessHandle(handle); |
+ if (proc <= 0) |
+ return false; |
+ } |
+ |
+ NOTREACHED(); |
+ return false; |
+} |
+ |
// static |
ZygoteHost* ZygoteHost::GetInstance() { |
return ZygoteHostImpl::GetInstance(); |
@@ -83,6 +105,7 @@ void ZygoteHostImpl::Init(const std::string& sandbox_cmd) { |
int fds[2]; |
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)); |
@@ -126,58 +149,52 @@ void ZygoteHostImpl::Init(const std::string& sandbox_cmd) { |
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_) { |
scoped_ptr<sandbox::SetuidSandboxClient> |
sandbox_client(sandbox::SetuidSandboxClient::Create()); |
sandbox_client->PrependWrapper(&cmd_line, &options); |
sandbox_client->SetupLaunchEnvironment(); |
+ // We no longer need this dummy socket for discovering the zygote's PID, |
+ // but the sandbox is still hard-coded to expect a file descriptor at |
+ // kZygoteIdFd. Fixing this requires a sandbox API change. :( |
CHECK_EQ(kZygoteIdFd, sandbox_client->GetUniqueToChildFileDescriptor()); |
- dummy_fd = socket(AF_UNIX, SOCK_DGRAM, 0); |
- CHECK(dummy_fd >= 0); |
- fds_to_map.push_back(std::make_pair(dummy_fd, kZygoteIdFd)); |
+ dummy_fd.reset(socket(AF_UNIX, SOCK_DGRAM, 0)); |
+ CHECK_GE(dummy_fd.get(), 0); |
+ fds_to_map.push_back(std::make_pair(dummy_fd.get(), kZygoteIdFd)); |
} |
base::ProcessHandle process = -1; |
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. |
+ // In the SUID sandbox, the real zygote is forked from the sandbox |
+ // and will be executing in another PID namespace. |
+ // Wait for the zygote to tell us it's running, and receive its PID, |
+ // which the kernel will translate to our PID namespace. |
// The sending code is in content/browser/zygote_main_linux.cc. |
std::vector<int> fds_vec; |
- const int kExpectedLength = sizeof(kZygoteHelloMessage); |
+ const size_t kExpectedLength = sizeof(kZygoteHelloMessage); |
char buf[kExpectedLength]; |
- const ssize_t len = UnixDomainSocket::RecvMsg(fds[0], buf, sizeof(buf), |
- &fds_vec); |
- CHECK_EQ(kExpectedLength, len) << "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|. |
- CHECK(base::FileDescriptorGetInode(&inode, dummy_fd)) |
- << "Cannot get inode for dummy_fd " << 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); |
- CHECK(base::GetAppOutput(get_inode_cmd, &inode_output)) |
- << "Find inode command failed for inode " << inode; |
- |
- base::TrimWhitespaceASCII(inode_output, base::TRIM_ALL, &inode_output); |
- CHECK(base::StringToInt(inode_output, &pid_)) |
- << "Invalid find inode output: " << inode_output; |
- CHECK(pid_ > 0) << "Did not find zygote process (using sandbox binary " |
- << sandbox_binary_ << ")"; |
+ const ssize_t len = UnixDomainSocket::RecvMsgWithPid( |
+ fds[0], buf, sizeof(buf), &fds_vec, &pid_); |
+ CHECK_EQ(kExpectedLength, static_cast<size_t>(len)) |
+ << "Incorrect zygote magic length"; |
+ CHECK_EQ(0, memcmp(buf, kZygoteHelloMessage, kExpectedLength)) |
+ << "Incorrect zygote hello"; |
+ CHECK_EQ(0U, fds_vec.size()) |
+ << "Zygote hello should not include file descriptors"; |
+ |
+ if (pid_ <= 0 || !SameOrDescendantOf(pid_, base::GetProcId(process))) { |
+ LOG(FATAL) |
+ << "Received invalid process ID for zygote; kernel might be too old? " |
+ "See crbug.com/357670 or try using --" |
+ << switches::kDisableSetuidSandbox << " to workaround."; |
+ } |
if (process != pid_) { |
// Reap the sandbox. |