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 32 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 43 } | 43 } |
| 44 | 44 |
| 45 int LookUpFd(const base::GlobalDescriptors::Mapping& fd_mapping, uint32_t key) { | 45 int LookUpFd(const base::GlobalDescriptors::Mapping& fd_mapping, uint32_t key) { |
| 46 for (size_t index = 0; index < fd_mapping.size(); ++index) { | 46 for (size_t index = 0; index < fd_mapping.size(); ++index) { |
| 47 if (fd_mapping[index].first == key) | 47 if (fd_mapping[index].first == key) |
| 48 return fd_mapping[index].second; | 48 return fd_mapping[index].second; |
| 49 } | 49 } |
| 50 return -1; | 50 return -1; |
| 51 } | 51 } |
| 52 | 52 |
| 53 void CreatePipe(base::ScopedFD* read_pipe, base::ScopedFD* write_pipe) { | |
| 54 int raw_pipe[2]; | |
| 55 PCHECK(0 == pipe(raw_pipe)); | |
| 56 read_pipe->reset(raw_pipe[0]); | |
| 57 write_pipe->reset(raw_pipe[1]); | |
| 58 } | |
| 59 | |
| 60 void KillAndReap(pid_t pid, bool use_helper) { | |
| 61 if (use_helper) { | |
| 62 // Helper children may be forked in another PID namespace, so |pid| might | |
| 63 // be meaningless to us; or we just might not be able to directly send it | |
| 64 // signals. So we can't kill it. | |
| 65 // Additionally, we're not its parent, so we can't reap it anyway. | |
| 66 LOG(WARNING) << "Unable to kill or reap helper children"; | |
| 67 return; | |
| 68 } | |
| 69 | |
| 70 // Kill the child process in case it's not already dead, so we can safely | |
| 71 // perform a blocking wait. | |
| 72 PCHECK(0 == kill(pid, SIGKILL)); | |
| 73 PCHECK(pid == waitpid(pid, NULL, 0)); | |
|
jln (very slow on Chromium)
2014/05/05 19:01:42
HANDLE_EINTR()
mdempsky
2014/05/05 21:38:18
Removed KillAndReap().
| |
| 74 } | |
| 75 | |
| 53 } // namespace | 76 } // namespace |
| 54 | 77 |
| 55 Zygote::Zygote(int sandbox_flags, | 78 Zygote::Zygote(int sandbox_flags, |
| 56 ZygoteForkDelegate* helper) | 79 ZygoteForkDelegate* helper) |
| 57 : sandbox_flags_(sandbox_flags), | 80 : sandbox_flags_(sandbox_flags), |
| 58 helper_(helper), | 81 helper_(helper), |
| 59 initial_uma_sample_(0), | 82 initial_uma_sample_(0), |
| 60 initial_uma_boundary_value_(0) { | 83 initial_uma_boundary_value_(0) { |
| 61 if (helper_) { | 84 if (helper_) { |
| 62 helper_->InitialUMA(&initial_uma_name_, | 85 helper_->InitialUMA(&initial_uma_name_, |
| (...skipping 91 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 154 HandleReapRequest(fd, pickle, iter); | 177 HandleReapRequest(fd, pickle, iter); |
| 155 return false; | 178 return false; |
| 156 case kZygoteCommandGetTerminationStatus: | 179 case kZygoteCommandGetTerminationStatus: |
| 157 if (!fds.empty()) | 180 if (!fds.empty()) |
| 158 break; | 181 break; |
| 159 HandleGetTerminationStatus(fd, pickle, iter); | 182 HandleGetTerminationStatus(fd, pickle, iter); |
| 160 return false; | 183 return false; |
| 161 case kZygoteCommandGetSandboxStatus: | 184 case kZygoteCommandGetSandboxStatus: |
| 162 HandleGetSandboxStatus(fd, pickle, iter); | 185 HandleGetSandboxStatus(fd, pickle, iter); |
| 163 return false; | 186 return false; |
| 187 case kZygoteCommandForkRealPID: | |
| 188 // This shouldn't happen in practice, but some failure paths in | |
| 189 // HandleForkRequest (e.g., if ReadArgsAndFork fails during depickling) | |
| 190 // could leave this command pending on the socket. | |
| 191 LOG(WARNING) << "Unexpected real PID message from browser"; | |
|
jln (very slow on Chromium)
2014/05/05 19:01:42
Let's add NOTREACHED() then?
mdempsky
2014/05/05 21:38:18
Done.
| |
| 192 return false; | |
| 164 default: | 193 default: |
| 165 NOTREACHED(); | 194 NOTREACHED(); |
| 166 break; | 195 break; |
| 167 } | 196 } |
| 168 } | 197 } |
| 169 | 198 |
| 170 LOG(WARNING) << "Error parsing message from browser"; | 199 LOG(WARNING) << "Error parsing message from browser"; |
| 171 return false; | 200 return false; |
| 172 } | 201 } |
| 173 | 202 |
| (...skipping 114 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 288 write_pickle.WriteInt(exit_code); | 317 write_pickle.WriteInt(exit_code); |
| 289 ssize_t written = | 318 ssize_t written = |
| 290 HANDLE_EINTR(write(fd, write_pickle.data(), write_pickle.size())); | 319 HANDLE_EINTR(write(fd, write_pickle.data(), write_pickle.size())); |
| 291 if (written != static_cast<ssize_t>(write_pickle.size())) | 320 if (written != static_cast<ssize_t>(write_pickle.size())) |
| 292 PLOG(ERROR) << "write"; | 321 PLOG(ERROR) << "write"; |
| 293 } | 322 } |
| 294 | 323 |
| 295 int Zygote::ForkWithRealPid(const std::string& process_type, | 324 int Zygote::ForkWithRealPid(const std::string& process_type, |
| 296 const base::GlobalDescriptors::Mapping& fd_mapping, | 325 const base::GlobalDescriptors::Mapping& fd_mapping, |
| 297 const std::string& channel_id, | 326 const std::string& channel_id, |
| 327 base::ScopedFD pid_oracle, | |
| 298 std::string* uma_name, | 328 std::string* uma_name, |
| 299 int* uma_sample, | 329 int* uma_sample, |
| 300 int* uma_boundary_value) { | 330 int* uma_boundary_value) { |
| 301 const bool use_helper = (helper_ && helper_->CanHelp(process_type, | 331 const bool use_helper = (helper_ && helper_->CanHelp(process_type, |
| 302 uma_name, | 332 uma_name, |
| 303 uma_sample, | 333 uma_sample, |
| 304 uma_boundary_value)); | 334 uma_boundary_value)); |
| 305 int dummy_fd; | 335 |
| 306 ino_t dummy_inode; | 336 base::ScopedFD read_pipe, write_pipe; |
| 307 int pipe_fds[2] = { -1, -1 }; | |
| 308 base::ProcessId pid = 0; | 337 base::ProcessId pid = 0; |
| 309 | |
| 310 dummy_fd = socket(PF_UNIX, SOCK_DGRAM, 0); | |
| 311 if (dummy_fd < 0) { | |
| 312 LOG(ERROR) << "Failed to create dummy FD"; | |
| 313 goto error; | |
| 314 } | |
| 315 if (!base::FileDescriptorGetInode(&dummy_inode, dummy_fd)) { | |
| 316 LOG(ERROR) << "Failed to get inode for dummy FD"; | |
| 317 goto error; | |
| 318 } | |
| 319 if (pipe(pipe_fds) != 0) { | |
| 320 LOG(ERROR) << "Failed to create pipe"; | |
| 321 goto error; | |
| 322 } | |
| 323 | |
| 324 if (use_helper) { | 338 if (use_helper) { |
| 325 std::vector<int> fds; | |
| 326 int ipc_channel_fd = LookUpFd(fd_mapping, kPrimaryIPCChannel); | 339 int ipc_channel_fd = LookUpFd(fd_mapping, kPrimaryIPCChannel); |
| 327 if (ipc_channel_fd < 0) { | 340 if (ipc_channel_fd < 0) { |
| 328 DLOG(ERROR) << "Failed to find kPrimaryIPCChannel in FD mapping"; | 341 DLOG(ERROR) << "Failed to find kPrimaryIPCChannel in FD mapping"; |
| 329 goto error; | 342 return -1; |
| 330 } | 343 } |
| 344 std::vector<int> fds; | |
| 331 fds.push_back(ipc_channel_fd); // kBrowserFDIndex | 345 fds.push_back(ipc_channel_fd); // kBrowserFDIndex |
| 332 fds.push_back(dummy_fd); // kDummyFDIndex | 346 fds.push_back(pid_oracle.get()); // kPIDOracleFDIndex |
| 333 fds.push_back(pipe_fds[0]); // kParentFDIndex | |
| 334 pid = helper_->Fork(process_type, fds, channel_id); | 347 pid = helper_->Fork(process_type, fds, channel_id); |
| 348 | |
| 349 // Helpers should never return in the child process. | |
| 350 CHECK_NE(pid, 0); | |
| 335 } else { | 351 } else { |
| 352 CreatePipe(&read_pipe, &write_pipe); | |
| 336 pid = fork(); | 353 pid = fork(); |
| 337 } | 354 } |
| 338 if (pid < 0) { | 355 |
| 339 goto error; | 356 if (pid == 0) { |
| 340 } else if (pid == 0) { | |
| 341 // In the child process. | 357 // In the child process. |
| 342 close(pipe_fds[1]); | 358 write_pipe.reset(); |
| 359 | |
| 360 // Ping the PID oracle socket so the browser can find our PID. | |
| 361 CHECK(SendZygoteChildPing(pid_oracle.get())); | |
| 362 | |
| 363 // Now read back our real PID from the zygote. | |
| 343 base::ProcessId real_pid; | 364 base::ProcessId real_pid; |
| 344 // Wait until the parent process has discovered our PID. We | 365 if (!base::ReadFromFD(read_pipe.get(), |
| 345 // should not fork any child processes (which the seccomp | 366 reinterpret_cast<char*>(&real_pid), |
| 346 // sandbox does) until then, because that can interfere with the | |
| 347 // parent's discovery of our PID. | |
| 348 if (!base::ReadFromFD(pipe_fds[0], reinterpret_cast<char*>(&real_pid), | |
| 349 sizeof(real_pid))) { | 367 sizeof(real_pid))) { |
| 350 LOG(FATAL) << "Failed to synchronise with parent zygote process"; | 368 LOG(FATAL) << "Failed to synchronise with parent zygote process"; |
| 351 } | 369 } |
| 352 if (real_pid <= 0) { | 370 if (real_pid <= 0) { |
| 353 LOG(FATAL) << "Invalid pid from parent zygote"; | 371 LOG(FATAL) << "Invalid pid from parent zygote"; |
| 354 } | 372 } |
| 355 #if defined(OS_LINUX) | 373 #if defined(OS_LINUX) |
| 356 // Sandboxed processes need to send the global, non-namespaced PID when | 374 // Sandboxed processes need to send the global, non-namespaced PID when |
| 357 // setting up an IPC channel to their parent. | 375 // setting up an IPC channel to their parent. |
| 358 IPC::Channel::SetGlobalPid(real_pid); | 376 IPC::Channel::SetGlobalPid(real_pid); |
| 359 // Force the real PID so chrome event data have a PID that corresponds | 377 // Force the real PID so chrome event data have a PID that corresponds |
| 360 // to system trace event data. | 378 // to system trace event data. |
| 361 base::debug::TraceLog::GetInstance()->SetProcessID( | 379 base::debug::TraceLog::GetInstance()->SetProcessID( |
| 362 static_cast<int>(real_pid)); | 380 static_cast<int>(real_pid)); |
| 363 #endif | 381 #endif |
| 364 close(pipe_fds[0]); | |
| 365 close(dummy_fd); | |
| 366 return 0; | 382 return 0; |
| 367 } else { | |
| 368 // In the parent process. | |
| 369 close(dummy_fd); | |
| 370 dummy_fd = -1; | |
| 371 close(pipe_fds[0]); | |
| 372 pipe_fds[0] = -1; | |
| 373 base::ProcessId real_pid; | |
| 374 if (UsingSUIDSandbox()) { | |
| 375 uint8_t reply_buf[512]; | |
| 376 Pickle request; | |
| 377 request.WriteInt(LinuxSandbox::METHOD_GET_CHILD_WITH_INODE); | |
| 378 request.WriteUInt64(dummy_inode); | |
| 379 | |
| 380 const ssize_t r = UnixDomainSocket::SendRecvMsg( | |
| 381 GetSandboxFD(), reply_buf, sizeof(reply_buf), NULL, | |
| 382 request); | |
| 383 if (r == -1) { | |
| 384 LOG(ERROR) << "Failed to get child process's real PID"; | |
| 385 goto error; | |
| 386 } | |
| 387 | |
| 388 Pickle reply(reinterpret_cast<char*>(reply_buf), r); | |
| 389 PickleIterator iter(reply); | |
| 390 if (!reply.ReadInt(&iter, &real_pid)) | |
| 391 goto error; | |
| 392 if (real_pid <= 0) { | |
| 393 // METHOD_GET_CHILD_WITH_INODE failed. Did the child die already? | |
| 394 LOG(ERROR) << "METHOD_GET_CHILD_WITH_INODE failed"; | |
| 395 goto error; | |
| 396 } | |
| 397 } else { | |
| 398 // If no SUID sandbox is involved then no pid translation is | |
| 399 // necessary. | |
| 400 real_pid = pid; | |
| 401 } | |
| 402 | |
| 403 // Now set-up this process to be tracked by the Zygote. | |
| 404 if (process_info_map_.find(real_pid) != process_info_map_.end()) { | |
| 405 LOG(ERROR) << "Already tracking PID " << real_pid; | |
| 406 NOTREACHED(); | |
| 407 } | |
| 408 process_info_map_[real_pid].internal_pid = pid; | |
| 409 process_info_map_[real_pid].started_from_helper = use_helper; | |
| 410 | |
| 411 // If we're using a helper, we still need to let the child process know | |
| 412 // we've discovered its real PID, but we don't actually reveal the PID. | |
| 413 const base::ProcessId pid_for_child = use_helper ? 0 : real_pid; | |
| 414 ssize_t written = | |
| 415 HANDLE_EINTR(write(pipe_fds[1], &pid_for_child, sizeof(pid_for_child))); | |
| 416 if (written != sizeof(pid_for_child)) { | |
| 417 LOG(ERROR) << "Failed to synchronise with child process"; | |
| 418 goto error; | |
| 419 } | |
| 420 close(pipe_fds[1]); | |
| 421 return real_pid; | |
| 422 } | 383 } |
| 423 | 384 |
| 424 error: | 385 // In the parent process. |
| 425 if (pid > 0) { | 386 read_pipe.reset(); |
| 426 if (waitpid(pid, NULL, WNOHANG) == -1) | 387 pid_oracle.reset(); |
| 427 LOG(ERROR) << "Failed to wait for process"; | 388 |
| 389 // Always receive a real PID from the zygote host, though it might | |
| 390 // be invalid (see below). | |
| 391 base::ProcessId real_pid; | |
| 392 { | |
| 393 ScopedVector<base::ScopedFD> recv_fds; | |
| 394 char buf[kZygoteMaxMessageLength]; | |
| 395 const ssize_t len = UnixDomainSocket::RecvMsg( | |
| 396 kZygoteSocketPairFd, buf, sizeof(buf), &recv_fds); | |
| 397 CHECK_GT(len, 0); | |
| 398 CHECK(recv_fds.empty()); | |
| 399 | |
| 400 Pickle pickle(buf, len); | |
| 401 PickleIterator iter(pickle); | |
| 402 | |
| 403 int kind; | |
| 404 CHECK(pickle.ReadInt(&iter, &kind)); | |
| 405 CHECK(kind == kZygoteCommandForkRealPID); | |
| 406 CHECK(pickle.ReadInt(&iter, &real_pid)); | |
| 428 } | 407 } |
| 429 if (dummy_fd >= 0) | 408 |
| 430 close(dummy_fd); | 409 // Fork failed. |
| 431 if (pipe_fds[0] >= 0) | 410 if (pid < 0) |
| 432 close(pipe_fds[0]); | 411 return -1; |
| 433 if (pipe_fds[1] >= 0) | 412 |
| 434 close(pipe_fds[1]); | 413 // If we successfully forked a child, but it crashed without sending |
| 435 return -1; | 414 // a message to the browser, the browser won't have found its PID. |
| 415 if (real_pid < 0) { | |
| 416 KillAndReap(pid, use_helper); | |
|
jln (very slow on Chromium)
2014/05/05 19:01:42
Since the children are trusted at this point, what
mdempsky
2014/05/05 21:38:18
Works for me, done.
| |
| 417 return -1; | |
| 418 } | |
| 419 | |
| 420 // If we're not using a helper, send the PID back to the child process. | |
| 421 if (!use_helper) { | |
| 422 ssize_t written = | |
| 423 HANDLE_EINTR(write(write_pipe.get(), &real_pid, sizeof(real_pid))); | |
| 424 if (written != sizeof(real_pid)) { | |
| 425 LOG(ERROR) << "Failed to synchronise with child process"; | |
| 426 KillAndReap(pid, use_helper); | |
|
jln (very slow on Chromium)
2014/05/05 19:01:42
Same remark, how about just LOG(FATAL)? KillAndRea
mdempsky
2014/05/05 21:38:18
Done.
| |
| 427 return -1; | |
| 428 } | |
| 429 } | |
| 430 | |
| 431 // Now set-up this process to be tracked by the Zygote. | |
| 432 if (process_info_map_.find(real_pid) != process_info_map_.end()) { | |
| 433 LOG(ERROR) << "Already tracking PID " << real_pid; | |
| 434 NOTREACHED(); | |
| 435 } | |
| 436 process_info_map_[real_pid].internal_pid = pid; | |
| 437 process_info_map_[real_pid].started_from_helper = use_helper; | |
| 438 | |
| 439 return real_pid; | |
| 436 } | 440 } |
| 437 | 441 |
| 438 base::ProcessId Zygote::ReadArgsAndFork(const Pickle& pickle, | 442 base::ProcessId Zygote::ReadArgsAndFork(const Pickle& pickle, |
| 439 PickleIterator iter, | 443 PickleIterator iter, |
| 440 ScopedVector<base::ScopedFD> fds, | 444 ScopedVector<base::ScopedFD> fds, |
| 441 std::string* uma_name, | 445 std::string* uma_name, |
| 442 int* uma_sample, | 446 int* uma_sample, |
| 443 int* uma_boundary_value) { | 447 int* uma_boundary_value) { |
| 444 std::vector<std::string> args; | 448 std::vector<std::string> args; |
| 445 int argc = 0; | 449 int argc = 0; |
| (...skipping 16 matching lines...) Expand all Loading... | |
| 462 args.push_back(arg); | 466 args.push_back(arg); |
| 463 if (arg.compare(0, channel_id_prefix.length(), channel_id_prefix) == 0) | 467 if (arg.compare(0, channel_id_prefix.length(), channel_id_prefix) == 0) |
| 464 channel_id = arg.substr(channel_id_prefix.length()); | 468 channel_id = arg.substr(channel_id_prefix.length()); |
| 465 } | 469 } |
| 466 | 470 |
| 467 if (!pickle.ReadInt(&iter, &numfds)) | 471 if (!pickle.ReadInt(&iter, &numfds)) |
| 468 return -1; | 472 return -1; |
| 469 if (numfds != static_cast<int>(fds.size())) | 473 if (numfds != static_cast<int>(fds.size())) |
| 470 return -1; | 474 return -1; |
| 471 | 475 |
| 472 for (int i = 0; i < numfds; ++i) { | 476 // First FD is the PID oracle socket. |
| 477 if (fds.size() < 1) | |
| 478 return -1; | |
| 479 base::ScopedFD pid_oracle(fds[0]->Pass()); | |
| 480 | |
| 481 // Remaining FDs are for the global descriptor mapping. | |
| 482 for (int i = 1; i < numfds; ++i) { | |
| 473 base::GlobalDescriptors::Key key; | 483 base::GlobalDescriptors::Key key; |
| 474 if (!pickle.ReadUInt32(&iter, &key)) | 484 if (!pickle.ReadUInt32(&iter, &key)) |
| 475 return -1; | 485 return -1; |
| 476 mapping.push_back(std::make_pair(key, fds[i]->get())); | 486 mapping.push_back(std::make_pair(key, fds[i]->get())); |
| 477 } | 487 } |
| 478 | 488 |
| 479 mapping.push_back(std::make_pair( | 489 mapping.push_back(std::make_pair( |
| 480 static_cast<uint32_t>(kSandboxIPCChannel), GetSandboxFD())); | 490 static_cast<uint32_t>(kSandboxIPCChannel), GetSandboxFD())); |
| 481 | 491 |
| 482 // Returns twice, once per process. | 492 // Returns twice, once per process. |
| 483 base::ProcessId child_pid = ForkWithRealPid(process_type, mapping, channel_id, | 493 base::ProcessId child_pid = ForkWithRealPid(process_type, |
| 484 uma_name, uma_sample, | 494 mapping, |
| 495 channel_id, | |
| 496 pid_oracle.Pass(), | |
| 497 uma_name, | |
| 498 uma_sample, | |
| 485 uma_boundary_value); | 499 uma_boundary_value); |
| 486 if (!child_pid) { | 500 if (!child_pid) { |
| 487 // This is the child process. | 501 // This is the child process. |
| 488 | 502 |
| 489 // Our socket from the browser. | 503 // Our socket from the browser. |
| 490 PCHECK(0 == IGNORE_EINTR(close(kZygoteSocketPairFd))); | 504 PCHECK(0 == IGNORE_EINTR(close(kZygoteSocketPairFd))); |
| 491 | 505 |
| 492 // Pass ownership of file descriptors from fds to GlobalDescriptors. | 506 // Pass ownership of file descriptors from fds to GlobalDescriptors. |
| 493 for (ScopedVector<base::ScopedFD>::iterator i = fds.begin(); i != fds.end(); | 507 for (ScopedVector<base::ScopedFD>::iterator i = fds.begin(); i != fds.end(); |
| 494 ++i) | 508 ++i) |
| (...skipping 55 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 550 PickleIterator iter) { | 564 PickleIterator iter) { |
| 551 if (HANDLE_EINTR(write(fd, &sandbox_flags_, sizeof(sandbox_flags_))) != | 565 if (HANDLE_EINTR(write(fd, &sandbox_flags_, sizeof(sandbox_flags_))) != |
| 552 sizeof(sandbox_flags_)) { | 566 sizeof(sandbox_flags_)) { |
| 553 PLOG(ERROR) << "write"; | 567 PLOG(ERROR) << "write"; |
| 554 } | 568 } |
| 555 | 569 |
| 556 return false; | 570 return false; |
| 557 } | 571 } |
| 558 | 572 |
| 559 } // namespace content | 573 } // namespace content |
| OLD | NEW |