Chromium Code Reviews| 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/zygote/zygote_linux.h" | 5 #include "content/zygote/zygote_linux.h" |
| 6 | 6 |
| 7 #include <fcntl.h> | 7 #include <fcntl.h> |
| 8 #include <string.h> | 8 #include <string.h> |
| 9 #include <sys/socket.h> | 9 #include <sys/socket.h> |
| 10 #include <sys/types.h> | 10 #include <sys/types.h> |
| (...skipping 81 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 92 #endif | 92 #endif |
| 93 } | 93 } |
| 94 | 94 |
| 95 for (;;) { | 95 for (;;) { |
| 96 // This function call can return multiple times, once per fork(). | 96 // This function call can return multiple times, once per fork(). |
| 97 if (HandleRequestFromBrowser(kBrowserDescriptor)) | 97 if (HandleRequestFromBrowser(kBrowserDescriptor)) |
| 98 return true; | 98 return true; |
| 99 } | 99 } |
| 100 } | 100 } |
| 101 | 101 |
| 102 bool Zygote::GetProcessInfo(base::ProcessHandle pid, | |
| 103 ZygoteProcessInfo* process_info) { | |
| 104 DCHECK(process_info); | |
| 105 const ZygoteProcessMap::const_iterator it = process_info_map_.find(pid); | |
| 106 if (it == process_info_map_.end()) { | |
| 107 return false; | |
| 108 } | |
| 109 *process_info = it->second; | |
| 110 return true; | |
| 111 } | |
| 112 | |
| 102 bool Zygote::UsingSUIDSandbox() const { | 113 bool Zygote::UsingSUIDSandbox() const { |
| 103 return sandbox_flags_ & kSandboxLinuxSUID; | 114 return sandbox_flags_ & kSandboxLinuxSUID; |
| 104 } | 115 } |
| 105 | 116 |
| 106 bool Zygote::HandleRequestFromBrowser(int fd) { | 117 bool Zygote::HandleRequestFromBrowser(int fd) { |
| 107 std::vector<int> fds; | 118 std::vector<int> fds; |
| 108 char buf[kZygoteMaxMessageLength]; | 119 char buf[kZygoteMaxMessageLength]; |
| 109 const ssize_t len = UnixDomainSocket::RecvMsg(fd, buf, sizeof(buf), &fds); | 120 const ssize_t len = UnixDomainSocket::RecvMsg(fd, buf, sizeof(buf), &fds); |
| 110 | 121 |
| 111 if (len == 0 || (len == -1 && errno == ECONNRESET)) { | 122 if (len == 0 || (len == -1 && errno == ECONNRESET)) { |
| (...skipping 47 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 159 void Zygote::HandleReapRequest(int fd, | 170 void Zygote::HandleReapRequest(int fd, |
| 160 const Pickle& pickle, | 171 const Pickle& pickle, |
| 161 PickleIterator iter) { | 172 PickleIterator iter) { |
| 162 base::ProcessId child; | 173 base::ProcessId child; |
| 163 | 174 |
| 164 if (!pickle.ReadInt(&iter, &child)) { | 175 if (!pickle.ReadInt(&iter, &child)) { |
| 165 LOG(WARNING) << "Error parsing reap request from browser"; | 176 LOG(WARNING) << "Error parsing reap request from browser"; |
| 166 return; | 177 return; |
| 167 } | 178 } |
| 168 | 179 |
| 169 if (process_info_map_.find(child) == process_info_map_.end()) { | 180 ZygoteProcessInfo child_info; |
| 170 // TODO(jln): test on more bots and add a DCHECK. | 181 if (!GetProcessInfo(child, &child_info)) { |
| 171 LOG(ERROR) << "Child not found!"; | 182 LOG(ERROR) << "Child not found!"; |
| 183 NOTREACHED(); | |
| 172 return; | 184 return; |
| 173 } | 185 } |
| 174 const base::ProcessId actual_child = process_info_map_[child].internal_pid; | 186 |
| 175 const bool started_from_helper = | 187 if (!child_info.started_from_helper) { |
| 176 process_info_map_[child].started_from_helper; | |
| 177 if (!started_from_helper) { | |
| 178 // TODO(jln): this old code is completely broken. See crbug.com/274855. | 188 // TODO(jln): this old code is completely broken. See crbug.com/274855. |
| 179 base::EnsureProcessTerminated(actual_child); | 189 base::EnsureProcessTerminated(child_info.internal_pid); |
| 180 } else { | 190 } else { |
| 181 // For processes from the helper, send a GetTerminationStatus request | 191 // For processes from the helper, send a GetTerminationStatus request |
| 182 // with known_dead set to true. | 192 // with known_dead set to true. |
| 183 // This is not perfect, as the process may be killed instantly, but is | 193 // This is not perfect, as the process may be killed instantly, but is |
| 184 // better than ignoring the request. | 194 // better than ignoring the request. |
| 185 base::TerminationStatus status; | 195 base::TerminationStatus status; |
| 186 int exit_code; | 196 int exit_code; |
| 187 bool got_termination_status = | 197 bool got_termination_status = |
| 188 GetTerminationStatus(child, true /* known_dead */, &status, &exit_code); | 198 GetTerminationStatus(child, true /* known_dead */, &status, &exit_code); |
| 189 DCHECK(got_termination_status); | 199 DCHECK(got_termination_status); |
| 190 } | 200 } |
| 191 process_info_map_.erase(child); | 201 process_info_map_.erase(child); |
| 192 } | 202 } |
| 193 | 203 |
| 194 bool Zygote::GetTerminationStatus(base::ProcessHandle real_pid, | 204 bool Zygote::GetTerminationStatus(base::ProcessHandle real_pid, |
| 195 bool known_dead, | 205 bool known_dead, |
| 196 base::TerminationStatus* status, | 206 base::TerminationStatus* status, |
| 197 int* exit_code) { | 207 int* exit_code) { |
| 198 // Do we know about this child? | 208 |
| 199 if (process_info_map_.find(real_pid) == process_info_map_.end()) { | 209 ZygoteProcessInfo child_info; |
| 200 // TODO(jln): test on more bots and add a DCHECK. | 210 if (!GetProcessInfo(real_pid, &child_info)) { |
| 201 LOG(ERROR) << "Zygote::GetTerminationStatus for unknown PID " | 211 LOG(ERROR) << "Zygote::GetTerminationStatus for unknown PID " |
| 202 << real_pid; | 212 << real_pid; |
| 213 NOTREACHED(); | |
| 203 return false; | 214 return false; |
| 204 } | 215 } |
| 205 // We know about |real_pid|. | 216 // We know about |real_pid|. |
| 206 const base::ProcessHandle child = | 217 const base::ProcessHandle child = child_info.internal_pid; |
| 207 process_info_map_[real_pid].internal_pid; | 218 if (child_info.started_from_helper) { |
| 208 const bool started_from_helper = | |
| 209 process_info_map_[real_pid].started_from_helper; | |
| 210 if (started_from_helper) { | |
| 211 // Let the helper handle the request. | 219 // Let the helper handle the request. |
| 212 DCHECK(helper_); | 220 DCHECK(helper_); |
| 213 if (!helper_->GetTerminationStatus(child, known_dead, status, exit_code)) { | 221 if (!helper_->GetTerminationStatus(child, known_dead, status, exit_code)) { |
| 214 return false; | 222 return false; |
| 215 } | 223 } |
| 216 } else { | 224 } else { |
| 217 // Handle the request directly. | 225 // Handle the request directly. |
| 218 if (known_dead) { | 226 if (known_dead) { |
| 219 // If we know that the process is already dead and the kernel is cleaning | 227 // If we know that the process is already dead and the kernel is cleaning |
| 220 // it up, we do want to wait until it becomes a zombie and not risk | 228 // it up, we do want to wait until it becomes a zombie and not risk |
| (...skipping 33 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 254 } | 262 } |
| 255 | 263 |
| 256 base::TerminationStatus status; | 264 base::TerminationStatus status; |
| 257 int exit_code; | 265 int exit_code; |
| 258 | 266 |
| 259 bool got_termination_status = | 267 bool got_termination_status = |
| 260 GetTerminationStatus(child_requested, known_dead, &status, &exit_code); | 268 GetTerminationStatus(child_requested, known_dead, &status, &exit_code); |
| 261 if (!got_termination_status) { | 269 if (!got_termination_status) { |
| 262 // Assume that if we can't find the child in the sandbox, then | 270 // Assume that if we can't find the child in the sandbox, then |
| 263 // it terminated normally. | 271 // it terminated normally. |
| 264 // TODO(jln): add a DCHECK. | 272 NOTREACHED(); |
| 265 status = base::TERMINATION_STATUS_NORMAL_TERMINATION; | 273 status = base::TERMINATION_STATUS_NORMAL_TERMINATION; |
| 266 exit_code = RESULT_CODE_NORMAL_EXIT; | 274 exit_code = RESULT_CODE_NORMAL_EXIT; |
| 267 } | 275 } |
| 268 | 276 |
| 269 Pickle write_pickle; | 277 Pickle write_pickle; |
| 270 write_pickle.WriteInt(static_cast<int>(status)); | 278 write_pickle.WriteInt(static_cast<int>(status)); |
| 271 write_pickle.WriteInt(exit_code); | 279 write_pickle.WriteInt(exit_code); |
| 272 ssize_t written = | 280 ssize_t written = |
| 273 HANDLE_EINTR(write(fd, write_pickle.data(), write_pickle.size())); | 281 HANDLE_EINTR(write(fd, write_pickle.data(), write_pickle.size())); |
| 274 if (written != static_cast<ssize_t>(write_pickle.size())) | 282 if (written != static_cast<ssize_t>(write_pickle.size())) |
| 275 PLOG(ERROR) << "write"; | 283 PLOG(ERROR) << "write"; |
| 276 } | 284 } |
| 277 | 285 |
| 278 int Zygote::ForkWithRealPid(const std::string& process_type, | 286 int Zygote::ForkWithRealPid(const std::string& process_type, |
| 279 std::vector<int>& fds, | 287 std::vector<int>& fds, |
| 280 const std::string& channel_switch, | 288 const std::string& channel_switch, |
| 281 std::string* uma_name, | 289 std::string* uma_name, |
| 282 int* uma_sample, | 290 int* uma_sample, |
| 283 int* uma_boundary_value) { | 291 int* uma_boundary_value) { |
| 284 const bool use_helper = (helper_ && helper_->CanHelp(process_type, | 292 const bool use_helper = (helper_ && helper_->CanHelp(process_type, |
| 285 uma_name, | 293 uma_name, |
| 286 uma_sample, | 294 uma_sample, |
| 287 uma_boundary_value)); | 295 uma_boundary_value)); |
| 288 // TODO(jln): this shortcut is silly and does nothing but make the code | |
| 289 // harder to follow and to test. Get rid of it. | |
| 290 if (!(use_helper || UsingSUIDSandbox())) { | |
|
Mark Seaborn
2013/08/21 17:49:33
I assume you've tested the removal of this by runn
jln (very slow on Chromium)
2013/08/21 19:22:32
Yes, that was tested. We still have a few bots wit
| |
| 291 pid_t pid = fork(); | |
| 292 if (pid > 0) { | |
| 293 process_info_map_[pid].internal_pid = pid; | |
| 294 process_info_map_[pid].started_from_helper = use_helper; | |
| 295 } | |
| 296 return pid; | |
| 297 } | |
| 298 | |
| 299 int dummy_fd; | 296 int dummy_fd; |
| 300 ino_t dummy_inode; | 297 ino_t dummy_inode; |
| 301 int pipe_fds[2] = { -1, -1 }; | 298 int pipe_fds[2] = { -1, -1 }; |
| 302 base::ProcessId pid = 0; | 299 base::ProcessId pid = 0; |
| 303 | 300 |
| 304 dummy_fd = socket(PF_UNIX, SOCK_DGRAM, 0); | 301 dummy_fd = socket(PF_UNIX, SOCK_DGRAM, 0); |
| 305 if (dummy_fd < 0) { | 302 if (dummy_fd < 0) { |
| 306 LOG(ERROR) << "Failed to create dummy FD"; | 303 LOG(ERROR) << "Failed to create dummy FD"; |
| 307 goto error; | 304 goto error; |
| 308 } | 305 } |
| (...skipping 74 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 383 goto error; | 380 goto error; |
| 384 } | 381 } |
| 385 } else { | 382 } else { |
| 386 // If no SUID sandbox is involved then no pid translation is | 383 // If no SUID sandbox is involved then no pid translation is |
| 387 // necessary. | 384 // necessary. |
| 388 real_pid = pid; | 385 real_pid = pid; |
| 389 } | 386 } |
| 390 | 387 |
| 391 // Now set-up this process to be tracked by the Zygote. | 388 // Now set-up this process to be tracked by the Zygote. |
| 392 if (process_info_map_.find(real_pid) != process_info_map_.end()) { | 389 if (process_info_map_.find(real_pid) != process_info_map_.end()) { |
| 393 // TODO(jln): add DCHECK. | |
| 394 LOG(ERROR) << "Already tracking PID " << real_pid; | 390 LOG(ERROR) << "Already tracking PID " << real_pid; |
| 391 NOTREACHED(); | |
| 395 } | 392 } |
| 396 process_info_map_[real_pid].internal_pid = pid; | 393 process_info_map_[real_pid].internal_pid = pid; |
| 397 process_info_map_[real_pid].started_from_helper = use_helper; | 394 process_info_map_[real_pid].started_from_helper = use_helper; |
| 398 | 395 |
| 399 if (use_helper) { | 396 if (use_helper) { |
| 400 if (!helper_->AckChild(pipe_fds[1], channel_switch)) { | 397 if (!helper_->AckChild(pipe_fds[1], channel_switch)) { |
| 401 LOG(ERROR) << "Failed to synchronise with zygote fork helper"; | 398 LOG(ERROR) << "Failed to synchronise with zygote fork helper"; |
| 402 goto error; | 399 goto error; |
| 403 } | 400 } |
| 404 } else { | 401 } else { |
| (...skipping 137 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 542 PickleIterator iter) { | 539 PickleIterator iter) { |
| 543 if (HANDLE_EINTR(write(fd, &sandbox_flags_, sizeof(sandbox_flags_))) != | 540 if (HANDLE_EINTR(write(fd, &sandbox_flags_, sizeof(sandbox_flags_))) != |
| 544 sizeof(sandbox_flags_)) { | 541 sizeof(sandbox_flags_)) { |
| 545 PLOG(ERROR) << "write"; | 542 PLOG(ERROR) << "write"; |
| 546 } | 543 } |
| 547 | 544 |
| 548 return false; | 545 return false; |
| 549 } | 546 } |
| 550 | 547 |
| 551 } // namespace content | 548 } // namespace content |
| OLD | NEW |