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 |