| OLD | NEW |
| 1 // Copyright 2015 The Chromium Authors. All rights reserved. | 1 // Copyright 2015 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/browser/zygote_host/zygote_communication_linux.h" | 5 #include "content/browser/zygote_host/zygote_communication_linux.h" |
| 6 | 6 |
| 7 #include <string.h> | 7 #include <string.h> |
| 8 #include <sys/socket.h> | 8 #include <sys/socket.h> |
| 9 | 9 |
| 10 #include "base/base_switches.h" | 10 #include "base/base_switches.h" |
| 11 #include "base/command_line.h" | 11 #include "base/command_line.h" |
| 12 #include "base/logging.h" | 12 #include "base/logging.h" |
| 13 #include "base/metrics/histogram.h" | 13 #include "base/metrics/histogram.h" |
| 14 #include "base/metrics/sparse_histogram.h" | 14 #include "base/metrics/sparse_histogram.h" |
| 15 #include "base/path_service.h" | 15 #include "base/path_service.h" |
| 16 #include "base/pickle.h" | 16 #include "base/pickle.h" |
| 17 #include "base/posix/eintr_wrapper.h" | 17 #include "base/posix/eintr_wrapper.h" |
| 18 #include "base/posix/unix_domain_socket_linux.h" | 18 #include "base/posix/unix_domain_socket_linux.h" |
| 19 #include "content/browser/renderer_host/render_sandbox_host_linux.h" | |
| 20 #include "content/browser/zygote_host/zygote_host_impl_linux.h" | 19 #include "content/browser/zygote_host/zygote_host_impl_linux.h" |
| 21 #include "content/common/child_process_sandbox_support_impl_linux.h" | |
| 22 #include "content/common/zygote_commands_linux.h" | 20 #include "content/common/zygote_commands_linux.h" |
| 23 #include "content/public/browser/content_browser_client.h" | 21 #include "content/public/browser/content_browser_client.h" |
| 22 #include "content/public/common/content_client.h" |
| 24 #include "content/public/common/content_switches.h" | 23 #include "content/public/common/content_switches.h" |
| 25 #include "content/public/common/result_codes.h" | 24 #include "content/public/common/result_codes.h" |
| 26 #include "sandbox/linux/services/namespace_sandbox.h" | |
| 27 #include "sandbox/linux/suid/client/setuid_sandbox_host.h" | |
| 28 #include "ui/display/display_switches.h" | 25 #include "ui/display/display_switches.h" |
| 29 #include "ui/gfx/switches.h" | |
| 30 | 26 |
| 31 namespace content { | 27 namespace content { |
| 32 | 28 |
| 33 namespace { | |
| 34 | |
| 35 // Receive a fixed message on fd and return the sender's PID. | |
| 36 // Returns true if the message received matches the expected message. | |
| 37 bool ReceiveFixedMessage(int fd, | |
| 38 const char* expect_msg, | |
| 39 size_t expect_len, | |
| 40 base::ProcessId* sender_pid) { | |
| 41 char buf[expect_len + 1]; | |
| 42 std::vector<base::ScopedFD> fds_vec; | |
| 43 | |
| 44 const ssize_t len = base::UnixDomainSocket::RecvMsgWithPid( | |
| 45 fd, buf, sizeof(buf), &fds_vec, sender_pid); | |
| 46 if (static_cast<size_t>(len) != expect_len) | |
| 47 return false; | |
| 48 if (memcmp(buf, expect_msg, expect_len) != 0) | |
| 49 return false; | |
| 50 if (!fds_vec.empty()) | |
| 51 return false; | |
| 52 return true; | |
| 53 } | |
| 54 | |
| 55 } // namespace | |
| 56 | |
| 57 ZygoteCommunication::ZygoteCommunication() | 29 ZygoteCommunication::ZygoteCommunication() |
| 58 : control_fd_(-1), | 30 : control_fd_(), |
| 59 control_lock_(), | 31 control_lock_(), |
| 60 pid_(), | 32 pid_(), |
| 61 list_of_running_zygote_children_(), | 33 list_of_running_zygote_children_(), |
| 62 child_tracking_lock_(), | 34 child_tracking_lock_(), |
| 63 sandbox_status_(0), | 35 sandbox_status_(0), |
| 64 have_read_sandbox_status_word_(false), | 36 have_read_sandbox_status_word_(false), |
| 65 init_(false) {} | 37 init_(false) {} |
| 66 | 38 |
| 67 ZygoteCommunication::~ZygoteCommunication() {} | 39 ZygoteCommunication::~ZygoteCommunication() {} |
| 68 | 40 |
| 69 bool ZygoteCommunication::SendMessage(const base::Pickle& data, | 41 bool ZygoteCommunication::SendMessage(const base::Pickle& data, |
| 70 const std::vector<int>* fds) { | 42 const std::vector<int>* fds) { |
| 71 DCHECK_NE(-1, control_fd_); | 43 DCHECK(control_fd_.is_valid()); |
| 72 CHECK(data.size() <= kZygoteMaxMessageLength) | 44 CHECK(data.size() <= kZygoteMaxMessageLength) |
| 73 << "Trying to send too-large message to zygote (sending " << data.size() | 45 << "Trying to send too-large message to zygote (sending " << data.size() |
| 74 << " bytes, max is " << kZygoteMaxMessageLength << ")"; | 46 << " bytes, max is " << kZygoteMaxMessageLength << ")"; |
| 75 CHECK(!fds || fds->size() <= base::UnixDomainSocket::kMaxFileDescriptors) | 47 CHECK(!fds || fds->size() <= base::UnixDomainSocket::kMaxFileDescriptors) |
| 76 << "Trying to send message with too many file descriptors to zygote " | 48 << "Trying to send message with too many file descriptors to zygote " |
| 77 << "(sending " << fds->size() << ", max is " | 49 << "(sending " << fds->size() << ", max is " |
| 78 << base::UnixDomainSocket::kMaxFileDescriptors << ")"; | 50 << base::UnixDomainSocket::kMaxFileDescriptors << ")"; |
| 79 | 51 |
| 80 return base::UnixDomainSocket::SendMsg(control_fd_, data.data(), data.size(), | 52 return base::UnixDomainSocket::SendMsg(control_fd_.get(), data.data(), |
| 53 data.size(), |
| 81 fds ? *fds : std::vector<int>()); | 54 fds ? *fds : std::vector<int>()); |
| 82 } | 55 } |
| 83 | 56 |
| 84 ssize_t ZygoteCommunication::ReadSandboxStatus() { | 57 ssize_t ZygoteCommunication::ReadSandboxStatus() { |
| 85 DCHECK_NE(-1, control_fd_); | 58 DCHECK(control_fd_.is_valid()); |
| 86 // At startup we send a kZygoteCommandGetSandboxStatus request to the zygote, | 59 // At startup we send a kZygoteCommandGetSandboxStatus request to the zygote, |
| 87 // but don't wait for the reply. Thus, the first time that we read from the | 60 // but don't wait for the reply. Thus, the first time that we read from the |
| 88 // zygote, we get the reply to that request. | 61 // zygote, we get the reply to that request. |
| 89 ssize_t bytes_read = HANDLE_EINTR( | 62 ssize_t bytes_read = HANDLE_EINTR( |
| 90 read(control_fd_, &sandbox_status_, sizeof(sandbox_status_))); | 63 read(control_fd_.get(), &sandbox_status_, sizeof(sandbox_status_))); |
| 91 if (bytes_read != sizeof(sandbox_status_)) { | 64 if (bytes_read != sizeof(sandbox_status_)) { |
| 92 return -1; | 65 return -1; |
| 93 } | 66 } |
| 94 return bytes_read; | 67 return bytes_read; |
| 95 } | 68 } |
| 96 | 69 |
| 97 ssize_t ZygoteCommunication::ReadReply(void* buf, size_t buf_len) { | 70 ssize_t ZygoteCommunication::ReadReply(void* buf, size_t buf_len) { |
| 98 DCHECK_NE(-1, control_fd_); | 71 DCHECK(control_fd_.is_valid()); |
| 99 if (!have_read_sandbox_status_word_) { | 72 if (!have_read_sandbox_status_word_) { |
| 100 if (ReadSandboxStatus() == -1) { | 73 if (ReadSandboxStatus() == -1) { |
| 101 return -1; | 74 return -1; |
| 102 } | 75 } |
| 103 have_read_sandbox_status_word_ = true; | 76 have_read_sandbox_status_word_ = true; |
| 104 UMA_HISTOGRAM_SPARSE_SLOWLY("Linux.SandboxStatus", sandbox_status_); | 77 UMA_HISTOGRAM_SPARSE_SLOWLY("Linux.SandboxStatus", sandbox_status_); |
| 105 } | 78 } |
| 106 | 79 |
| 107 return HANDLE_EINTR(read(control_fd_, buf, buf_len)); | 80 return HANDLE_EINTR(read(control_fd_.get(), buf, buf_len)); |
| 108 } | 81 } |
| 109 | 82 |
| 110 pid_t ZygoteCommunication::ForkRequest( | 83 pid_t ZygoteCommunication::ForkRequest( |
| 111 const std::vector<std::string>& argv, | 84 const std::vector<std::string>& argv, |
| 112 std::unique_ptr<FileDescriptorInfo> mapping, | 85 std::unique_ptr<FileDescriptorInfo> mapping, |
| 113 const std::string& process_type) { | 86 const std::string& process_type) { |
| 114 DCHECK(init_); | 87 DCHECK(init_); |
| 115 | 88 |
| 116 base::Pickle pickle; | 89 base::Pickle pickle; |
| 117 int raw_socks[2]; | 90 int raw_socks[2]; |
| (...skipping 140 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 258 | 231 |
| 259 void ZygoteCommunication::Init() { | 232 void ZygoteCommunication::Init() { |
| 260 CHECK(!init_); | 233 CHECK(!init_); |
| 261 | 234 |
| 262 base::FilePath chrome_path; | 235 base::FilePath chrome_path; |
| 263 CHECK(PathService::Get(base::FILE_EXE, &chrome_path)); | 236 CHECK(PathService::Get(base::FILE_EXE, &chrome_path)); |
| 264 base::CommandLine cmd_line(chrome_path); | 237 base::CommandLine cmd_line(chrome_path); |
| 265 | 238 |
| 266 cmd_line.AppendSwitchASCII(switches::kProcessType, switches::kZygoteProcess); | 239 cmd_line.AppendSwitchASCII(switches::kProcessType, switches::kZygoteProcess); |
| 267 | 240 |
| 268 int fds[2]; | |
| 269 CHECK(socketpair(AF_UNIX, SOCK_SEQPACKET, 0, fds) == 0); | |
| 270 CHECK(base::UnixDomainSocket::EnableReceiveProcessId(fds[0])); | |
| 271 base::FileHandleMappingVector fds_to_map; | |
| 272 fds_to_map.push_back(std::make_pair(fds[1], kZygoteSocketPairFd)); | |
| 273 | |
| 274 base::LaunchOptions options; | |
| 275 const base::CommandLine& browser_command_line = | 241 const base::CommandLine& browser_command_line = |
| 276 *base::CommandLine::ForCurrentProcess(); | 242 *base::CommandLine::ForCurrentProcess(); |
| 277 if (browser_command_line.HasSwitch(switches::kZygoteCmdPrefix)) { | 243 if (browser_command_line.HasSwitch(switches::kZygoteCmdPrefix)) { |
| 278 cmd_line.PrependWrapper( | 244 cmd_line.PrependWrapper( |
| 279 browser_command_line.GetSwitchValueNative(switches::kZygoteCmdPrefix)); | 245 browser_command_line.GetSwitchValueNative(switches::kZygoteCmdPrefix)); |
| 280 } | 246 } |
| 281 // Append any switches from the browser process that need to be forwarded on | 247 // Append any switches from the browser process that need to be forwarded on |
| 282 // to the zygote/renderers. | 248 // to the zygote/renderers. |
| 283 // Should this list be obtained from browser_render_process_host.cc? | 249 // Should this list be obtained from browser_render_process_host.cc? |
| 284 static const char* const kForwardSwitches[] = { | 250 static const char* const kForwardSwitches[] = { |
| 285 switches::kAllowSandboxDebugging, switches::kAndroidFontsPath, | 251 switches::kAllowSandboxDebugging, switches::kAndroidFontsPath, |
| 286 switches::kDisableSeccompFilterSandbox, | 252 switches::kDisableSeccompFilterSandbox, |
| 287 switches::kEnableHeapProfiling, | 253 switches::kEnableHeapProfiling, |
| 288 switches::kEnableLogging, // Support, e.g., --enable-logging=stderr. | 254 switches::kEnableLogging, // Support, e.g., --enable-logging=stderr. |
| 289 // Zygote process needs to know what resources to have loaded when it | 255 // Zygote process needs to know what resources to have loaded when it |
| 290 // becomes a renderer process. | 256 // becomes a renderer process. |
| 291 switches::kForceDeviceScaleFactor, switches::kLoggingLevel, | 257 switches::kForceDeviceScaleFactor, switches::kLoggingLevel, |
| 292 switches::kNoSandbox, switches::kPpapiInProcess, | 258 switches::kNoSandbox, switches::kPpapiInProcess, |
| 293 switches::kRegisterPepperPlugins, switches::kV, switches::kVModule, | 259 switches::kRegisterPepperPlugins, switches::kV, switches::kVModule, |
| 294 }; | 260 }; |
| 295 cmd_line.CopySwitchesFrom(browser_command_line, kForwardSwitches, | 261 cmd_line.CopySwitchesFrom(browser_command_line, kForwardSwitches, |
| 296 arraysize(kForwardSwitches)); | 262 arraysize(kForwardSwitches)); |
| 297 | 263 |
| 298 GetContentClient()->browser()->AppendExtraCommandLineSwitches(&cmd_line, -1); | 264 GetContentClient()->browser()->AppendExtraCommandLineSwitches(&cmd_line, -1); |
| 299 | 265 |
| 300 const bool using_namespace_sandbox = | 266 pid_ = ZygoteHostImpl::GetInstance()->LaunchZygote(&cmd_line, &control_fd_); |
| 301 ZygoteHostImpl::GetInstance()->ShouldUseNamespaceSandbox(); | |
| 302 // A non empty sandbox_cmd means we want a SUID sandbox. | |
| 303 const bool using_suid_sandbox = | |
| 304 !ZygoteHostImpl::GetInstance()->SandboxCommand().empty() && | |
| 305 !using_namespace_sandbox; | |
| 306 | |
| 307 // Start up the sandbox host process and get the file descriptor for the | |
| 308 // renderers to talk to it. | |
| 309 const int sfd = RenderSandboxHostLinux::GetInstance()->GetRendererSocket(); | |
| 310 fds_to_map.push_back(std::make_pair(sfd, GetSandboxFD())); | |
| 311 | |
| 312 base::ScopedFD dummy_fd; | |
| 313 if (using_suid_sandbox) { | |
| 314 std::unique_ptr<sandbox::SetuidSandboxHost> sandbox_host( | |
| 315 sandbox::SetuidSandboxHost::Create()); | |
| 316 sandbox_host->PrependWrapper(&cmd_line); | |
| 317 sandbox_host->SetupLaunchOptions(&options, &fds_to_map, &dummy_fd); | |
| 318 sandbox_host->SetupLaunchEnvironment(); | |
| 319 } | |
| 320 | |
| 321 options.fds_to_remap = &fds_to_map; | |
| 322 base::Process process = | |
| 323 using_namespace_sandbox | |
| 324 ? sandbox::NamespaceSandbox::LaunchProcess(cmd_line, options) | |
| 325 : base::LaunchProcess(cmd_line, options); | |
| 326 CHECK(process.IsValid()) << "Failed to launch zygote process"; | |
| 327 | |
| 328 dummy_fd.reset(); | |
| 329 | |
| 330 if (using_suid_sandbox || using_namespace_sandbox) { | |
| 331 // The SUID sandbox will execute the zygote in a new PID namespace, and | |
| 332 // the main zygote process will then fork from there. Watch now our | |
| 333 // elaborate dance to find and validate the zygote's PID. | |
| 334 | |
| 335 // First we receive a message from the zygote boot process. | |
| 336 base::ProcessId boot_pid; | |
| 337 CHECK(ReceiveFixedMessage(fds[0], kZygoteBootMessage, | |
| 338 sizeof(kZygoteBootMessage), &boot_pid)); | |
| 339 | |
| 340 // Within the PID namespace, the zygote boot process thinks it's PID 1, | |
| 341 // but its real PID can never be 1. This gives us a reliable test that | |
| 342 // the kernel is translating the sender's PID to our namespace. | |
| 343 CHECK_GT(boot_pid, 1) | |
| 344 << "Received invalid process ID for zygote; kernel might be too old? " | |
| 345 "See crbug.com/357670 or try using --" | |
| 346 << switches::kDisableSetuidSandbox << " to workaround."; | |
| 347 | |
| 348 // Now receive the message that the zygote's ready to go, along with the | |
| 349 // main zygote process's ID. | |
| 350 CHECK(ReceiveFixedMessage(fds[0], kZygoteHelloMessage, | |
| 351 sizeof(kZygoteHelloMessage), &pid_)); | |
| 352 CHECK_GT(pid_, 1); | |
| 353 | |
| 354 if (process.Pid() != pid_) { | |
| 355 // Reap the sandbox. | |
| 356 base::EnsureProcessGetsReaped(process.Pid()); | |
| 357 } | |
| 358 } else { | |
| 359 // Not using the SUID sandbox. | |
| 360 // Note that ~base::Process() will reset the internal value, but there's no | |
| 361 // real "handle" on POSIX so that is safe. | |
| 362 pid_ = process.Pid(); | |
| 363 } | |
| 364 | |
| 365 close(fds[1]); | |
| 366 control_fd_ = fds[0]; | |
| 367 | |
| 368 ZygoteHostImpl::GetInstance()->AddZygotePid(pid_); | |
| 369 | 267 |
| 370 base::Pickle pickle; | 268 base::Pickle pickle; |
| 371 pickle.WriteInt(kZygoteCommandGetSandboxStatus); | 269 pickle.WriteInt(kZygoteCommandGetSandboxStatus); |
| 372 if (!SendMessage(pickle, NULL)) | 270 if (!SendMessage(pickle, NULL)) |
| 373 LOG(FATAL) << "Cannot communicate with zygote"; | 271 LOG(FATAL) << "Cannot communicate with zygote"; |
| 374 | 272 |
| 375 init_ = true; | 273 init_ = true; |
| 376 } | 274 } |
| 377 | 275 |
| 378 base::TerminationStatus ZygoteCommunication::GetTerminationStatus( | 276 base::TerminationStatus ZygoteCommunication::GetTerminationStatus( |
| (...skipping 51 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 430 } | 328 } |
| 431 if (ReadSandboxStatus() == -1) { | 329 if (ReadSandboxStatus() == -1) { |
| 432 return 0; | 330 return 0; |
| 433 } | 331 } |
| 434 have_read_sandbox_status_word_ = true; | 332 have_read_sandbox_status_word_ = true; |
| 435 UMA_HISTOGRAM_SPARSE_SLOWLY("Linux.SandboxStatus", sandbox_status_); | 333 UMA_HISTOGRAM_SPARSE_SLOWLY("Linux.SandboxStatus", sandbox_status_); |
| 436 return sandbox_status_; | 334 return sandbox_status_; |
| 437 } | 335 } |
| 438 | 336 |
| 439 } // namespace content | 337 } // namespace content |
| OLD | NEW |