| 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/browser/zygote_host/zygote_host_impl_linux.h" | 5 #include "content/browser/zygote_host/zygote_host_impl_linux.h" |
| 6 | 6 |
| 7 #include <sys/socket.h> | 7 #include <sys/socket.h> |
| 8 #include <sys/stat.h> | 8 #include <sys/stat.h> |
| 9 #include <sys/types.h> | 9 #include <sys/types.h> |
| 10 #include <unistd.h> | 10 #include <unistd.h> |
| (...skipping 21 matching lines...) Expand all Loading... |
| 32 #include "content/public/common/content_switches.h" | 32 #include "content/public/common/content_switches.h" |
| 33 #include "content/public/common/result_codes.h" | 33 #include "content/public/common/result_codes.h" |
| 34 #include "sandbox/linux/suid/client/setuid_sandbox_client.h" | 34 #include "sandbox/linux/suid/client/setuid_sandbox_client.h" |
| 35 #include "sandbox/linux/suid/common/sandbox.h" | 35 #include "sandbox/linux/suid/common/sandbox.h" |
| 36 #include "ui/base/ui_base_switches.h" | 36 #include "ui/base/ui_base_switches.h" |
| 37 | 37 |
| 38 #if defined(USE_TCMALLOC) | 38 #if defined(USE_TCMALLOC) |
| 39 #include "third_party/tcmalloc/chromium/src/gperftools/heap-profiler.h" | 39 #include "third_party/tcmalloc/chromium/src/gperftools/heap-profiler.h" |
| 40 #endif | 40 #endif |
| 41 | 41 |
| 42 namespace content { |
| 43 |
| 42 // static | 44 // static |
| 43 content::ZygoteHost* content::ZygoteHost::GetInstance() { | 45 ZygoteHost* ZygoteHost::GetInstance() { |
| 44 return ZygoteHostImpl::GetInstance(); | 46 return ZygoteHostImpl::GetInstance(); |
| 45 } | 47 } |
| 46 | 48 |
| 47 ZygoteHostImpl::ZygoteHostImpl() | 49 ZygoteHostImpl::ZygoteHostImpl() |
| 48 : control_fd_(-1), | 50 : control_fd_(-1), |
| 49 pid_(-1), | 51 pid_(-1), |
| 50 init_(false), | 52 init_(false), |
| 51 using_suid_sandbox_(false), | 53 using_suid_sandbox_(false), |
| 52 have_read_sandbox_status_word_(false), | 54 have_read_sandbox_status_word_(false), |
| 53 sandbox_status_(0) {} | 55 sandbox_status_(0) {} |
| (...skipping 21 matching lines...) Expand all Loading... |
| 75 int fds[2]; | 77 int fds[2]; |
| 76 #if defined(OS_FREEBSD) || defined(OS_OPENBSD) | 78 #if defined(OS_FREEBSD) || defined(OS_OPENBSD) |
| 77 // The BSDs often don't support SOCK_SEQPACKET yet, so fall back to | 79 // The BSDs often don't support SOCK_SEQPACKET yet, so fall back to |
| 78 // SOCK_DGRAM if necessary. | 80 // SOCK_DGRAM if necessary. |
| 79 if (socketpair(PF_UNIX, SOCK_SEQPACKET, 0, fds) != 0) | 81 if (socketpair(PF_UNIX, SOCK_SEQPACKET, 0, fds) != 0) |
| 80 CHECK(socketpair(PF_UNIX, SOCK_DGRAM, 0, fds) == 0); | 82 CHECK(socketpair(PF_UNIX, SOCK_DGRAM, 0, fds) == 0); |
| 81 #else | 83 #else |
| 82 CHECK(socketpair(PF_UNIX, SOCK_SEQPACKET, 0, fds) == 0); | 84 CHECK(socketpair(PF_UNIX, SOCK_SEQPACKET, 0, fds) == 0); |
| 83 #endif | 85 #endif |
| 84 base::FileHandleMappingVector fds_to_map; | 86 base::FileHandleMappingVector fds_to_map; |
| 85 fds_to_map.push_back(std::make_pair(fds[1], content::kZygoteSocketPairFd)); | 87 fds_to_map.push_back(std::make_pair(fds[1], kZygoteSocketPairFd)); |
| 86 | 88 |
| 87 const CommandLine& browser_command_line = *CommandLine::ForCurrentProcess(); | 89 const CommandLine& browser_command_line = *CommandLine::ForCurrentProcess(); |
| 88 if (browser_command_line.HasSwitch(switches::kZygoteCmdPrefix)) { | 90 if (browser_command_line.HasSwitch(switches::kZygoteCmdPrefix)) { |
| 89 cmd_line.PrependWrapper( | 91 cmd_line.PrependWrapper( |
| 90 browser_command_line.GetSwitchValueNative(switches::kZygoteCmdPrefix)); | 92 browser_command_line.GetSwitchValueNative(switches::kZygoteCmdPrefix)); |
| 91 } | 93 } |
| 92 // Append any switches from the browser process that need to be forwarded on | 94 // Append any switches from the browser process that need to be forwarded on |
| 93 // to the zygote/renderers. | 95 // to the zygote/renderers. |
| 94 // Should this list be obtained from browser_render_process_host.cc? | 96 // Should this list be obtained from browser_render_process_host.cc? |
| 95 static const char* kForwardSwitches[] = { | 97 static const char* kForwardSwitches[] = { |
| (...skipping 14 matching lines...) Expand all Loading... |
| 110 | 112 |
| 111 switches::kNoSandbox, | 113 switches::kNoSandbox, |
| 112 | 114 |
| 113 #if !defined(OS_CHROMEOS) | 115 #if !defined(OS_CHROMEOS) |
| 114 switches::kEnableTouchEvents, | 116 switches::kEnableTouchEvents, |
| 115 #endif | 117 #endif |
| 116 }; | 118 }; |
| 117 cmd_line.CopySwitchesFrom(browser_command_line, kForwardSwitches, | 119 cmd_line.CopySwitchesFrom(browser_command_line, kForwardSwitches, |
| 118 arraysize(kForwardSwitches)); | 120 arraysize(kForwardSwitches)); |
| 119 | 121 |
| 120 content::GetContentClient()->browser()->AppendExtraCommandLineSwitches( | 122 GetContentClient()->browser()->AppendExtraCommandLineSwitches(&cmd_line, -1); |
| 121 &cmd_line, -1); | |
| 122 | 123 |
| 123 sandbox_binary_ = sandbox_cmd.c_str(); | 124 sandbox_binary_ = sandbox_cmd.c_str(); |
| 124 | 125 |
| 125 if (!sandbox_cmd.empty()) { | 126 if (!sandbox_cmd.empty()) { |
| 126 struct stat st; | 127 struct stat st; |
| 127 if (stat(sandbox_binary_.c_str(), &st) != 0) { | 128 if (stat(sandbox_binary_.c_str(), &st) != 0) { |
| 128 LOG(FATAL) << "The SUID sandbox helper binary is missing: " | 129 LOG(FATAL) << "The SUID sandbox helper binary is missing: " |
| 129 << sandbox_binary_ << " Aborting now."; | 130 << sandbox_binary_ << " Aborting now."; |
| 130 } | 131 } |
| 131 | 132 |
| (...skipping 15 matching lines...) Expand all Loading... |
| 147 } | 148 } |
| 148 } else { | 149 } else { |
| 149 LOG(WARNING) << "Running without the SUID sandbox! See " | 150 LOG(WARNING) << "Running without the SUID sandbox! See " |
| 150 "http://code.google.com/p/chromium/wiki/LinuxSUIDSandboxDevelopment " | 151 "http://code.google.com/p/chromium/wiki/LinuxSUIDSandboxDevelopment " |
| 151 "for more information on developing with the sandbox on."; | 152 "for more information on developing with the sandbox on."; |
| 152 } | 153 } |
| 153 | 154 |
| 154 // Start up the sandbox host process and get the file descriptor for the | 155 // Start up the sandbox host process and get the file descriptor for the |
| 155 // renderers to talk to it. | 156 // renderers to talk to it. |
| 156 const int sfd = RenderSandboxHostLinux::GetInstance()->GetRendererSocket(); | 157 const int sfd = RenderSandboxHostLinux::GetInstance()->GetRendererSocket(); |
| 157 fds_to_map.push_back(std::make_pair(sfd, content::kZygoteRendererSocketFd)); | 158 fds_to_map.push_back(std::make_pair(sfd, kZygoteRendererSocketFd)); |
| 158 | 159 |
| 159 int dummy_fd = -1; | 160 int dummy_fd = -1; |
| 160 if (using_suid_sandbox_) { | 161 if (using_suid_sandbox_) { |
| 161 dummy_fd = socket(PF_UNIX, SOCK_DGRAM, 0); | 162 dummy_fd = socket(PF_UNIX, SOCK_DGRAM, 0); |
| 162 CHECK(dummy_fd >= 0); | 163 CHECK(dummy_fd >= 0); |
| 163 fds_to_map.push_back(std::make_pair(dummy_fd, content::kZygoteIdFd)); | 164 fds_to_map.push_back(std::make_pair(dummy_fd, kZygoteIdFd)); |
| 164 } | 165 } |
| 165 | 166 |
| 166 base::ProcessHandle process = -1; | 167 base::ProcessHandle process = -1; |
| 167 base::LaunchOptions options; | 168 base::LaunchOptions options; |
| 168 options.fds_to_remap = &fds_to_map; | 169 options.fds_to_remap = &fds_to_map; |
| 169 base::LaunchProcess(cmd_line.argv(), options, &process); | 170 base::LaunchProcess(cmd_line.argv(), options, &process); |
| 170 CHECK(process != -1) << "Failed to launch zygote process"; | 171 CHECK(process != -1) << "Failed to launch zygote process"; |
| 171 | 172 |
| 172 if (using_suid_sandbox_) { | 173 if (using_suid_sandbox_) { |
| 173 // In the SUID sandbox, the real zygote is forked from the sandbox. | 174 // In the SUID sandbox, the real zygote is forked from the sandbox. |
| 174 // We need to look for it. | 175 // We need to look for it. |
| 175 // But first, wait for the zygote to tell us it's running. | 176 // But first, wait for the zygote to tell us it's running. |
| 176 // The sending code is in content/browser/zygote_main_linux.cc. | 177 // The sending code is in content/browser/zygote_main_linux.cc. |
| 177 std::vector<int> fds_vec; | 178 std::vector<int> fds_vec; |
| 178 const int kExpectedLength = sizeof(content::kZygoteHelloMessage); | 179 const int kExpectedLength = sizeof(kZygoteHelloMessage); |
| 179 char buf[kExpectedLength]; | 180 char buf[kExpectedLength]; |
| 180 const ssize_t len = UnixDomainSocket::RecvMsg(fds[0], buf, sizeof(buf), | 181 const ssize_t len = UnixDomainSocket::RecvMsg(fds[0], buf, sizeof(buf), |
| 181 &fds_vec); | 182 &fds_vec); |
| 182 CHECK(len == kExpectedLength) << "Incorrect zygote magic length"; | 183 CHECK(len == kExpectedLength) << "Incorrect zygote magic length"; |
| 183 CHECK(0 == strcmp(buf, content::kZygoteHelloMessage)) | 184 CHECK(0 == strcmp(buf, kZygoteHelloMessage)) |
| 184 << "Incorrect zygote hello"; | 185 << "Incorrect zygote hello"; |
| 185 | 186 |
| 186 std::string inode_output; | 187 std::string inode_output; |
| 187 ino_t inode = 0; | 188 ino_t inode = 0; |
| 188 // Figure out the inode for |dummy_fd|, close |dummy_fd| on our end, | 189 // Figure out the inode for |dummy_fd|, close |dummy_fd| on our end, |
| 189 // and find the zygote process holding |dummy_fd|. | 190 // and find the zygote process holding |dummy_fd|. |
| 190 if (base::FileDescriptorGetInode(&inode, dummy_fd)) { | 191 if (base::FileDescriptorGetInode(&inode, dummy_fd)) { |
| 191 close(dummy_fd); | 192 close(dummy_fd); |
| 192 std::vector<std::string> get_inode_cmdline; | 193 std::vector<std::string> get_inode_cmdline; |
| 193 get_inode_cmdline.push_back(sandbox_binary_); | 194 get_inode_cmdline.push_back(sandbox_binary_); |
| (...skipping 13 matching lines...) Expand all Loading... |
| 207 } | 208 } |
| 208 } else { | 209 } else { |
| 209 // Not using the SUID sandbox. | 210 // Not using the SUID sandbox. |
| 210 pid_ = process; | 211 pid_ = process; |
| 211 } | 212 } |
| 212 | 213 |
| 213 close(fds[1]); | 214 close(fds[1]); |
| 214 control_fd_ = fds[0]; | 215 control_fd_ = fds[0]; |
| 215 | 216 |
| 216 Pickle pickle; | 217 Pickle pickle; |
| 217 pickle.WriteInt(content::kZygoteCommandGetSandboxStatus); | 218 pickle.WriteInt(kZygoteCommandGetSandboxStatus); |
| 218 if (!SendMessage(pickle, NULL)) | 219 if (!SendMessage(pickle, NULL)) |
| 219 LOG(FATAL) << "Cannot communicate with zygote"; | 220 LOG(FATAL) << "Cannot communicate with zygote"; |
| 220 // We don't wait for the reply. We'll read it in ReadReply. | 221 // We don't wait for the reply. We'll read it in ReadReply. |
| 221 } | 222 } |
| 222 | 223 |
| 223 bool ZygoteHostImpl::SendMessage(const Pickle& data, | 224 bool ZygoteHostImpl::SendMessage(const Pickle& data, |
| 224 const std::vector<int>* fds) { | 225 const std::vector<int>* fds) { |
| 225 CHECK(data.size() <= content::kZygoteMaxMessageLength) | 226 CHECK(data.size() <= kZygoteMaxMessageLength) |
| 226 << "Trying to send too-large message to zygote (sending " << data.size() | 227 << "Trying to send too-large message to zygote (sending " << data.size() |
| 227 << " bytes, max is " << content::kZygoteMaxMessageLength << ")"; | 228 << " bytes, max is " << kZygoteMaxMessageLength << ")"; |
| 228 CHECK(!fds || fds->size() <= UnixDomainSocket::kMaxFileDescriptors) | 229 CHECK(!fds || fds->size() <= UnixDomainSocket::kMaxFileDescriptors) |
| 229 << "Trying to send message with too many file descriptors to zygote " | 230 << "Trying to send message with too many file descriptors to zygote " |
| 230 << "(sending " << fds->size() << ", max is " | 231 << "(sending " << fds->size() << ", max is " |
| 231 << UnixDomainSocket::kMaxFileDescriptors << ")"; | 232 << UnixDomainSocket::kMaxFileDescriptors << ")"; |
| 232 | 233 |
| 233 return UnixDomainSocket::SendMsg(control_fd_, | 234 return UnixDomainSocket::SendMsg(control_fd_, |
| 234 data.data(), data.size(), | 235 data.data(), data.size(), |
| 235 fds ? *fds : std::vector<int>()); | 236 fds ? *fds : std::vector<int>()); |
| 236 } | 237 } |
| 237 | 238 |
| 238 ssize_t ZygoteHostImpl::ReadReply(void* buf, size_t buf_len) { | 239 ssize_t ZygoteHostImpl::ReadReply(void* buf, size_t buf_len) { |
| 239 // At startup we send a kZygoteCommandGetSandboxStatus request to the zygote, | 240 // At startup we send a kZygoteCommandGetSandboxStatus request to the zygote, |
| 240 // but don't wait for the reply. Thus, the first time that we read from the | 241 // but don't wait for the reply. Thus, the first time that we read from the |
| 241 // zygote, we get the reply to that request. | 242 // zygote, we get the reply to that request. |
| 242 if (!have_read_sandbox_status_word_) { | 243 if (!have_read_sandbox_status_word_) { |
| 243 if (HANDLE_EINTR(read(control_fd_, &sandbox_status_, | 244 if (HANDLE_EINTR(read(control_fd_, &sandbox_status_, |
| 244 sizeof(sandbox_status_))) != | 245 sizeof(sandbox_status_))) != |
| 245 sizeof(sandbox_status_)) { | 246 sizeof(sandbox_status_)) { |
| 246 return -1; | 247 return -1; |
| 247 } | 248 } |
| 248 have_read_sandbox_status_word_ = true; | 249 have_read_sandbox_status_word_ = true; |
| 249 } | 250 } |
| 250 | 251 |
| 251 return HANDLE_EINTR(read(control_fd_, buf, buf_len)); | 252 return HANDLE_EINTR(read(control_fd_, buf, buf_len)); |
| 252 } | 253 } |
| 253 | 254 |
| 254 pid_t ZygoteHostImpl::ForkRequest( | 255 pid_t ZygoteHostImpl::ForkRequest( |
| 255 const std::vector<std::string>& argv, | 256 const std::vector<std::string>& argv, |
| 256 const std::vector<content::FileDescriptorInfo>& mapping, | 257 const std::vector<FileDescriptorInfo>& mapping, |
| 257 const std::string& process_type) { | 258 const std::string& process_type) { |
| 258 DCHECK(init_); | 259 DCHECK(init_); |
| 259 Pickle pickle; | 260 Pickle pickle; |
| 260 | 261 |
| 261 pickle.WriteInt(content::kZygoteCommandFork); | 262 pickle.WriteInt(kZygoteCommandFork); |
| 262 pickle.WriteString(process_type); | 263 pickle.WriteString(process_type); |
| 263 pickle.WriteInt(argv.size()); | 264 pickle.WriteInt(argv.size()); |
| 264 for (std::vector<std::string>::const_iterator | 265 for (std::vector<std::string>::const_iterator |
| 265 i = argv.begin(); i != argv.end(); ++i) | 266 i = argv.begin(); i != argv.end(); ++i) |
| 266 pickle.WriteString(*i); | 267 pickle.WriteString(*i); |
| 267 | 268 |
| 268 pickle.WriteInt(mapping.size()); | 269 pickle.WriteInt(mapping.size()); |
| 269 | 270 |
| 270 std::vector<int> fds; | 271 std::vector<int> fds; |
| 271 // Scoped pointers cannot be stored in containers, so we have to use a | 272 // Scoped pointers cannot be stored in containers, so we have to use a |
| 272 // linked_ptr. | 273 // linked_ptr. |
| 273 std::vector<linked_ptr<file_util::ScopedFD> > autodelete_fds; | 274 std::vector<linked_ptr<file_util::ScopedFD> > autodelete_fds; |
| 274 for (std::vector<content::FileDescriptorInfo>::const_iterator | 275 for (std::vector<FileDescriptorInfo>::const_iterator |
| 275 i = mapping.begin(); i != mapping.end(); ++i) { | 276 i = mapping.begin(); i != mapping.end(); ++i) { |
| 276 pickle.WriteUInt32(i->id); | 277 pickle.WriteUInt32(i->id); |
| 277 fds.push_back(i->fd.fd); | 278 fds.push_back(i->fd.fd); |
| 278 if (i->fd.auto_close) { | 279 if (i->fd.auto_close) { |
| 279 // Auto-close means we need to close the FDs after they habe been passed | 280 // Auto-close means we need to close the FDs after they habe been passed |
| 280 // to the other process. | 281 // to the other process. |
| 281 linked_ptr<file_util::ScopedFD> ptr( | 282 linked_ptr<file_util::ScopedFD> ptr( |
| 282 new file_util::ScopedFD(&(fds.back()))); | 283 new file_util::ScopedFD(&(fds.back()))); |
| 283 autodelete_fds.push_back(ptr); | 284 autodelete_fds.push_back(ptr); |
| 284 } | 285 } |
| (...skipping 154 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 439 // Low memory notification is currently only implemented on ChromeOS. | 440 // Low memory notification is currently only implemented on ChromeOS. |
| 440 NOTREACHED() << "AdjustLowMemoryMargin not implemented"; | 441 NOTREACHED() << "AdjustLowMemoryMargin not implemented"; |
| 441 #endif // defined(OS_CHROMEOS) | 442 #endif // defined(OS_CHROMEOS) |
| 442 } | 443 } |
| 443 | 444 |
| 444 | 445 |
| 445 void ZygoteHostImpl::EnsureProcessTerminated(pid_t process) { | 446 void ZygoteHostImpl::EnsureProcessTerminated(pid_t process) { |
| 446 DCHECK(init_); | 447 DCHECK(init_); |
| 447 Pickle pickle; | 448 Pickle pickle; |
| 448 | 449 |
| 449 pickle.WriteInt(content::kZygoteCommandReap); | 450 pickle.WriteInt(kZygoteCommandReap); |
| 450 pickle.WriteInt(process); | 451 pickle.WriteInt(process); |
| 451 if (!SendMessage(pickle, NULL)) | 452 if (!SendMessage(pickle, NULL)) |
| 452 LOG(ERROR) << "Failed to send Reap message to zygote"; | 453 LOG(ERROR) << "Failed to send Reap message to zygote"; |
| 453 } | 454 } |
| 454 | 455 |
| 455 base::TerminationStatus ZygoteHostImpl::GetTerminationStatus( | 456 base::TerminationStatus ZygoteHostImpl::GetTerminationStatus( |
| 456 base::ProcessHandle handle, | 457 base::ProcessHandle handle, |
| 457 int* exit_code) { | 458 int* exit_code) { |
| 458 DCHECK(init_); | 459 DCHECK(init_); |
| 459 Pickle pickle; | 460 Pickle pickle; |
| 460 pickle.WriteInt(content::kZygoteCommandGetTerminationStatus); | 461 pickle.WriteInt(kZygoteCommandGetTerminationStatus); |
| 461 pickle.WriteInt(handle); | 462 pickle.WriteInt(handle); |
| 462 | 463 |
| 463 // Set this now to handle the early termination cases. | 464 // Set this now to handle the early termination cases. |
| 464 if (exit_code) | 465 if (exit_code) |
| 465 *exit_code = content::RESULT_CODE_NORMAL_EXIT; | 466 *exit_code = RESULT_CODE_NORMAL_EXIT; |
| 466 | 467 |
| 467 static const unsigned kMaxMessageLength = 128; | 468 static const unsigned kMaxMessageLength = 128; |
| 468 char buf[kMaxMessageLength]; | 469 char buf[kMaxMessageLength]; |
| 469 ssize_t len; | 470 ssize_t len; |
| 470 { | 471 { |
| 471 base::AutoLock lock(control_lock_); | 472 base::AutoLock lock(control_lock_); |
| 472 if (!SendMessage(pickle, NULL)) | 473 if (!SendMessage(pickle, NULL)) |
| 473 LOG(ERROR) << "Failed to send GetTerminationStatus message to zygote"; | 474 LOG(ERROR) << "Failed to send GetTerminationStatus message to zygote"; |
| 474 len = ReadReply(buf, sizeof(buf)); | 475 len = ReadReply(buf, sizeof(buf)); |
| 475 } | 476 } |
| (...skipping 27 matching lines...) Expand all Loading... |
| 503 | 504 |
| 504 pid_t ZygoteHostImpl::GetSandboxHelperPid() const { | 505 pid_t ZygoteHostImpl::GetSandboxHelperPid() const { |
| 505 return RenderSandboxHostLinux::GetInstance()->pid(); | 506 return RenderSandboxHostLinux::GetInstance()->pid(); |
| 506 } | 507 } |
| 507 | 508 |
| 508 int ZygoteHostImpl::GetSandboxStatus() const { | 509 int ZygoteHostImpl::GetSandboxStatus() const { |
| 509 if (have_read_sandbox_status_word_) | 510 if (have_read_sandbox_status_word_) |
| 510 return sandbox_status_; | 511 return sandbox_status_; |
| 511 return 0; | 512 return 0; |
| 512 } | 513 } |
| 514 |
| 515 } // namespace content |
| OLD | NEW |