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 | |
| 53 } // namespace | 60 } // namespace |
| 54 | 61 |
| 55 Zygote::Zygote(int sandbox_flags, | 62 Zygote::Zygote(int sandbox_flags, |
| 56 ZygoteForkDelegate* helper) | 63 ZygoteForkDelegate* helper) |
| 57 : sandbox_flags_(sandbox_flags), | 64 : sandbox_flags_(sandbox_flags), |
| 58 helper_(helper), | 65 helper_(helper), |
| 59 initial_uma_sample_(0), | 66 initial_uma_sample_(0), |
| 60 initial_uma_boundary_value_(0) { | 67 initial_uma_boundary_value_(0) { |
| 61 if (helper_) { | 68 if (helper_) { |
| 62 helper_->InitialUMA(&initial_uma_name_, | 69 helper_->InitialUMA(&initial_uma_name_, |
| (...skipping 91 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 154 HandleReapRequest(fd, pickle, iter); | 161 HandleReapRequest(fd, pickle, iter); |
| 155 return false; | 162 return false; |
| 156 case kZygoteCommandGetTerminationStatus: | 163 case kZygoteCommandGetTerminationStatus: |
| 157 if (!fds.empty()) | 164 if (!fds.empty()) |
| 158 break; | 165 break; |
| 159 HandleGetTerminationStatus(fd, pickle, iter); | 166 HandleGetTerminationStatus(fd, pickle, iter); |
| 160 return false; | 167 return false; |
| 161 case kZygoteCommandGetSandboxStatus: | 168 case kZygoteCommandGetSandboxStatus: |
| 162 HandleGetSandboxStatus(fd, pickle, iter); | 169 HandleGetSandboxStatus(fd, pickle, iter); |
| 163 return false; | 170 return false; |
| 171 case kZygoteCommandForkRealPID: | |
| 172 // This shouldn't happen in practice, but some failure paths in | |
| 173 // HandleForkRequest (e.g., if ReadArgsAndFork fails during depickling) | |
| 174 // could leave this command pending on the socket. | |
| 175 LOG(ERROR) << "Unexpected real PID message from browser"; | |
| 176 NOTREACHED(); | |
| 177 return false; | |
| 164 default: | 178 default: |
| 165 NOTREACHED(); | 179 NOTREACHED(); |
| 166 break; | 180 break; |
| 167 } | 181 } |
| 168 } | 182 } |
| 169 | 183 |
| 170 LOG(WARNING) << "Error parsing message from browser"; | 184 LOG(WARNING) << "Error parsing message from browser"; |
| 171 return false; | 185 return false; |
| 172 } | 186 } |
| 173 | 187 |
| (...skipping 114 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 288 write_pickle.WriteInt(exit_code); | 302 write_pickle.WriteInt(exit_code); |
| 289 ssize_t written = | 303 ssize_t written = |
| 290 HANDLE_EINTR(write(fd, write_pickle.data(), write_pickle.size())); | 304 HANDLE_EINTR(write(fd, write_pickle.data(), write_pickle.size())); |
| 291 if (written != static_cast<ssize_t>(write_pickle.size())) | 305 if (written != static_cast<ssize_t>(write_pickle.size())) |
| 292 PLOG(ERROR) << "write"; | 306 PLOG(ERROR) << "write"; |
| 293 } | 307 } |
| 294 | 308 |
| 295 int Zygote::ForkWithRealPid(const std::string& process_type, | 309 int Zygote::ForkWithRealPid(const std::string& process_type, |
| 296 const base::GlobalDescriptors::Mapping& fd_mapping, | 310 const base::GlobalDescriptors::Mapping& fd_mapping, |
| 297 const std::string& channel_id, | 311 const std::string& channel_id, |
| 312 base::ScopedFD pid_oracle, | |
| 298 std::string* uma_name, | 313 std::string* uma_name, |
| 299 int* uma_sample, | 314 int* uma_sample, |
| 300 int* uma_boundary_value) { | 315 int* uma_boundary_value) { |
| 301 const bool use_helper = (helper_ && helper_->CanHelp(process_type, | 316 const bool use_helper = (helper_ && helper_->CanHelp(process_type, |
| 302 uma_name, | 317 uma_name, |
| 303 uma_sample, | 318 uma_sample, |
| 304 uma_boundary_value)); | 319 uma_boundary_value)); |
| 305 int dummy_fd; | 320 |
| 306 ino_t dummy_inode; | 321 base::ScopedFD read_pipe, write_pipe; |
| 307 int pipe_fds[2] = { -1, -1 }; | |
| 308 base::ProcessId pid = 0; | 322 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) { | 323 if (use_helper) { |
| 325 std::vector<int> fds; | |
| 326 int ipc_channel_fd = LookUpFd(fd_mapping, kPrimaryIPCChannel); | 324 int ipc_channel_fd = LookUpFd(fd_mapping, kPrimaryIPCChannel); |
| 327 if (ipc_channel_fd < 0) { | 325 if (ipc_channel_fd < 0) { |
| 328 DLOG(ERROR) << "Failed to find kPrimaryIPCChannel in FD mapping"; | 326 DLOG(ERROR) << "Failed to find kPrimaryIPCChannel in FD mapping"; |
| 329 goto error; | 327 return -1; |
| 330 } | 328 } |
| 329 std::vector<int> fds; | |
| 331 fds.push_back(ipc_channel_fd); // kBrowserFDIndex | 330 fds.push_back(ipc_channel_fd); // kBrowserFDIndex |
| 332 fds.push_back(dummy_fd); // kDummyFDIndex | 331 fds.push_back(pid_oracle.get()); // kPIDOracleFDIndex |
| 333 fds.push_back(pipe_fds[0]); // kParentFDIndex | |
| 334 pid = helper_->Fork(process_type, fds, channel_id); | 332 pid = helper_->Fork(process_type, fds, channel_id); |
| 333 | |
| 334 // Helpers should never return in the child process. | |
| 335 CHECK_NE(pid, 0); | |
| 335 } else { | 336 } else { |
| 337 CreatePipe(&read_pipe, &write_pipe); | |
| 336 pid = fork(); | 338 pid = fork(); |
| 337 } | 339 } |
| 338 if (pid < 0) { | 340 |
| 339 goto error; | 341 if (pid == 0) { |
| 340 } else if (pid == 0) { | |
| 341 // In the child process. | 342 // In the child process. |
| 342 close(pipe_fds[1]); | 343 write_pipe.reset(); |
| 344 | |
| 345 // Ping the PID oracle socket so the browser can find our PID. | |
| 346 CHECK(SendZygoteChildPing(pid_oracle.get())); | |
| 347 | |
| 348 // Now read back our real PID from the zygote. | |
| 343 base::ProcessId real_pid; | 349 base::ProcessId real_pid; |
| 344 // Wait until the parent process has discovered our PID. We | 350 if (!base::ReadFromFD(read_pipe.get(), |
| 345 // should not fork any child processes (which the seccomp | 351 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))) { | 352 sizeof(real_pid))) { |
| 350 LOG(FATAL) << "Failed to synchronise with parent zygote process"; | 353 LOG(FATAL) << "Failed to synchronise with parent zygote process"; |
| 351 } | 354 } |
| 352 if (real_pid <= 0) { | 355 if (real_pid <= 0) { |
| 353 LOG(FATAL) << "Invalid pid from parent zygote"; | 356 LOG(FATAL) << "Invalid pid from parent zygote"; |
| 354 } | 357 } |
| 355 #if defined(OS_LINUX) | 358 #if defined(OS_LINUX) |
| 356 // Sandboxed processes need to send the global, non-namespaced PID when | 359 // Sandboxed processes need to send the global, non-namespaced PID when |
| 357 // setting up an IPC channel to their parent. | 360 // setting up an IPC channel to their parent. |
| 358 IPC::Channel::SetGlobalPid(real_pid); | 361 IPC::Channel::SetGlobalPid(real_pid); |
| 359 // Force the real PID so chrome event data have a PID that corresponds | 362 // Force the real PID so chrome event data have a PID that corresponds |
| 360 // to system trace event data. | 363 // to system trace event data. |
| 361 base::debug::TraceLog::GetInstance()->SetProcessID( | 364 base::debug::TraceLog::GetInstance()->SetProcessID( |
| 362 static_cast<int>(real_pid)); | 365 static_cast<int>(real_pid)); |
| 363 #endif | 366 #endif |
| 364 close(pipe_fds[0]); | |
| 365 close(dummy_fd); | |
| 366 return 0; | 367 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 } | 368 } |
| 423 | 369 |
| 424 error: | 370 // In the parent process. |
| 425 if (pid > 0) { | 371 read_pipe.reset(); |
| 426 if (waitpid(pid, NULL, WNOHANG) == -1) | 372 pid_oracle.reset(); |
| 427 LOG(ERROR) << "Failed to wait for process"; | 373 |
| 374 // Always receive a real PID from the zygote host, though it might | |
| 375 // be invalid (see below). | |
| 376 base::ProcessId real_pid; | |
| 377 { | |
| 378 ScopedVector<base::ScopedFD> recv_fds; | |
| 379 char buf[kZygoteMaxMessageLength]; | |
| 380 const ssize_t len = UnixDomainSocket::RecvMsg( | |
| 381 kZygoteSocketPairFd, buf, sizeof(buf), &recv_fds); | |
| 382 CHECK_GT(len, 0); | |
| 383 CHECK(recv_fds.empty()); | |
| 384 | |
| 385 Pickle pickle(buf, len); | |
| 386 PickleIterator iter(pickle); | |
| 387 | |
| 388 int kind; | |
| 389 CHECK(pickle.ReadInt(&iter, &kind)); | |
| 390 CHECK(kind == kZygoteCommandForkRealPID); | |
| 391 CHECK(pickle.ReadInt(&iter, &real_pid)); | |
| 428 } | 392 } |
| 429 if (dummy_fd >= 0) | 393 |
| 430 close(dummy_fd); | 394 // Fork failed. |
| 431 if (pipe_fds[0] >= 0) | 395 if (pid < 0) { |
| 432 close(pipe_fds[0]); | 396 return -1; |
| 433 if (pipe_fds[1] >= 0) | 397 } |
| 434 close(pipe_fds[1]); | 398 |
| 435 return -1; | 399 // If we successfully forked a child, but it crashed without sending |
| 400 // a message to the browser, the browser won't have found its PID. | |
|
Mark Seaborn
2014/05/06 23:34:13
Won't the browser have crashed with LOG(FATAL) in
mdempsky
2014/05/06 23:56:38
In this version of the patch set, yes. The commen
| |
| 401 if (real_pid < 0) { | |
| 402 LOG(FATAL) << "Failed to find child's real PID"; | |
| 403 } | |
| 404 | |
| 405 // If we're not using a helper, send the PID back to the child process. | |
| 406 if (!use_helper) { | |
| 407 ssize_t written = | |
| 408 HANDLE_EINTR(write(write_pipe.get(), &real_pid, sizeof(real_pid))); | |
| 409 if (written != sizeof(real_pid)) { | |
| 410 LOG(FATAL) << "Failed to synchronize with child process"; | |
| 411 } | |
| 412 } | |
| 413 | |
| 414 // Now set-up this process to be tracked by the Zygote. | |
| 415 if (process_info_map_.find(real_pid) != process_info_map_.end()) { | |
| 416 LOG(ERROR) << "Already tracking PID " << real_pid; | |
| 417 NOTREACHED(); | |
| 418 } | |
| 419 process_info_map_[real_pid].internal_pid = pid; | |
| 420 process_info_map_[real_pid].started_from_helper = use_helper; | |
| 421 | |
| 422 return real_pid; | |
| 436 } | 423 } |
| 437 | 424 |
| 438 base::ProcessId Zygote::ReadArgsAndFork(const Pickle& pickle, | 425 base::ProcessId Zygote::ReadArgsAndFork(const Pickle& pickle, |
| 439 PickleIterator iter, | 426 PickleIterator iter, |
| 440 ScopedVector<base::ScopedFD> fds, | 427 ScopedVector<base::ScopedFD> fds, |
| 441 std::string* uma_name, | 428 std::string* uma_name, |
| 442 int* uma_sample, | 429 int* uma_sample, |
| 443 int* uma_boundary_value) { | 430 int* uma_boundary_value) { |
| 444 std::vector<std::string> args; | 431 std::vector<std::string> args; |
| 445 int argc = 0; | 432 int argc = 0; |
| (...skipping 16 matching lines...) Expand all Loading... | |
| 462 args.push_back(arg); | 449 args.push_back(arg); |
| 463 if (arg.compare(0, channel_id_prefix.length(), channel_id_prefix) == 0) | 450 if (arg.compare(0, channel_id_prefix.length(), channel_id_prefix) == 0) |
| 464 channel_id = arg.substr(channel_id_prefix.length()); | 451 channel_id = arg.substr(channel_id_prefix.length()); |
| 465 } | 452 } |
| 466 | 453 |
| 467 if (!pickle.ReadInt(&iter, &numfds)) | 454 if (!pickle.ReadInt(&iter, &numfds)) |
| 468 return -1; | 455 return -1; |
| 469 if (numfds != static_cast<int>(fds.size())) | 456 if (numfds != static_cast<int>(fds.size())) |
| 470 return -1; | 457 return -1; |
| 471 | 458 |
| 472 for (int i = 0; i < numfds; ++i) { | 459 // First FD is the PID oracle socket. |
| 460 if (fds.size() < 1) | |
| 461 return -1; | |
| 462 base::ScopedFD pid_oracle(fds[0]->Pass()); | |
| 463 | |
| 464 // Remaining FDs are for the global descriptor mapping. | |
| 465 for (int i = 1; i < numfds; ++i) { | |
| 473 base::GlobalDescriptors::Key key; | 466 base::GlobalDescriptors::Key key; |
| 474 if (!pickle.ReadUInt32(&iter, &key)) | 467 if (!pickle.ReadUInt32(&iter, &key)) |
| 475 return -1; | 468 return -1; |
| 476 mapping.push_back(std::make_pair(key, fds[i]->get())); | 469 mapping.push_back(std::make_pair(key, fds[i]->get())); |
| 477 } | 470 } |
| 478 | 471 |
| 479 mapping.push_back(std::make_pair( | 472 mapping.push_back(std::make_pair( |
| 480 static_cast<uint32_t>(kSandboxIPCChannel), GetSandboxFD())); | 473 static_cast<uint32_t>(kSandboxIPCChannel), GetSandboxFD())); |
| 481 | 474 |
| 482 // Returns twice, once per process. | 475 // Returns twice, once per process. |
| 483 base::ProcessId child_pid = ForkWithRealPid(process_type, mapping, channel_id, | 476 base::ProcessId child_pid = ForkWithRealPid(process_type, |
| 484 uma_name, uma_sample, | 477 mapping, |
| 478 channel_id, | |
| 479 pid_oracle.Pass(), | |
| 480 uma_name, | |
| 481 uma_sample, | |
| 485 uma_boundary_value); | 482 uma_boundary_value); |
| 486 if (!child_pid) { | 483 if (!child_pid) { |
| 487 // This is the child process. | 484 // This is the child process. |
| 488 | 485 |
| 489 // Our socket from the browser. | 486 // Our socket from the browser. |
| 490 PCHECK(0 == IGNORE_EINTR(close(kZygoteSocketPairFd))); | 487 PCHECK(0 == IGNORE_EINTR(close(kZygoteSocketPairFd))); |
| 491 | 488 |
| 492 // Pass ownership of file descriptors from fds to GlobalDescriptors. | 489 // Pass ownership of file descriptors from fds to GlobalDescriptors. |
| 493 for (ScopedVector<base::ScopedFD>::iterator i = fds.begin(); i != fds.end(); | 490 for (ScopedVector<base::ScopedFD>::iterator i = fds.begin(); i != fds.end(); |
| 494 ++i) | 491 ++i) |
| (...skipping 55 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 550 PickleIterator iter) { | 547 PickleIterator iter) { |
| 551 if (HANDLE_EINTR(write(fd, &sandbox_flags_, sizeof(sandbox_flags_))) != | 548 if (HANDLE_EINTR(write(fd, &sandbox_flags_, sizeof(sandbox_flags_))) != |
| 552 sizeof(sandbox_flags_)) { | 549 sizeof(sandbox_flags_)) { |
| 553 PLOG(ERROR) << "write"; | 550 PLOG(ERROR) << "write"; |
| 554 } | 551 } |
| 555 | 552 |
| 556 return false; | 553 return false; |
| 557 } | 554 } |
| 558 | 555 |
| 559 } // namespace content | 556 } // namespace content |
| OLD | NEW |