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 bool CreatePipe(base::ScopedFD* read_pipe, base::ScopedFD* write_pipe) { | |
| 54 int raw_pipe[2]; | |
| 55 if (pipe(raw_pipe) != 0) | |
|
jln (very slow on Chromium)
2014/05/02 18:25:00
Feel free to PCHECK and return void, as you prefer
mdempsky
2014/05/02 23:36:01
Done.
| |
| 56 return false; | |
| 57 read_pipe->reset(raw_pipe[0]); | |
| 58 write_pipe->reset(raw_pipe[1]); | |
| 59 return true; | |
| 60 } | |
| 61 | |
| 53 } // namespace | 62 } // namespace |
| 54 | 63 |
| 55 Zygote::Zygote(int sandbox_flags, | 64 Zygote::Zygote(int sandbox_flags, |
| 56 ZygoteForkDelegate* helper) | 65 ZygoteForkDelegate* helper) |
| 57 : sandbox_flags_(sandbox_flags), | 66 : sandbox_flags_(sandbox_flags), |
| 58 helper_(helper), | 67 helper_(helper), |
| 59 initial_uma_sample_(0), | 68 initial_uma_sample_(0), |
| 60 initial_uma_boundary_value_(0) { | 69 initial_uma_boundary_value_(0) { |
| 61 if (helper_) { | 70 if (helper_) { |
| 62 helper_->InitialUMA(&initial_uma_name_, | 71 helper_->InitialUMA(&initial_uma_name_, |
| (...skipping 225 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 288 write_pickle.WriteInt(exit_code); | 297 write_pickle.WriteInt(exit_code); |
| 289 ssize_t written = | 298 ssize_t written = |
| 290 HANDLE_EINTR(write(fd, write_pickle.data(), write_pickle.size())); | 299 HANDLE_EINTR(write(fd, write_pickle.data(), write_pickle.size())); |
| 291 if (written != static_cast<ssize_t>(write_pickle.size())) | 300 if (written != static_cast<ssize_t>(write_pickle.size())) |
| 292 PLOG(ERROR) << "write"; | 301 PLOG(ERROR) << "write"; |
| 293 } | 302 } |
| 294 | 303 |
| 295 int Zygote::ForkWithRealPid(const std::string& process_type, | 304 int Zygote::ForkWithRealPid(const std::string& process_type, |
| 296 const base::GlobalDescriptors::Mapping& fd_mapping, | 305 const base::GlobalDescriptors::Mapping& fd_mapping, |
| 297 const std::string& channel_id, | 306 const std::string& channel_id, |
| 307 base::ScopedFD pid_oracle, | |
| 298 std::string* uma_name, | 308 std::string* uma_name, |
| 299 int* uma_sample, | 309 int* uma_sample, |
| 300 int* uma_boundary_value) { | 310 int* uma_boundary_value) { |
| 301 const bool use_helper = (helper_ && helper_->CanHelp(process_type, | 311 const bool use_helper = (helper_ && helper_->CanHelp(process_type, |
| 302 uma_name, | 312 uma_name, |
| 303 uma_sample, | 313 uma_sample, |
| 304 uma_boundary_value)); | 314 uma_boundary_value)); |
| 305 int dummy_fd; | 315 |
| 306 ino_t dummy_inode; | 316 base::ScopedFD read_pipe, write_pipe; |
| 307 int pipe_fds[2] = { -1, -1 }; | |
| 308 base::ProcessId pid = 0; | 317 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) { | 318 if (use_helper) { |
| 325 std::vector<int> fds; | |
| 326 int ipc_channel_fd = LookUpFd(fd_mapping, kPrimaryIPCChannel); | 319 int ipc_channel_fd = LookUpFd(fd_mapping, kPrimaryIPCChannel); |
| 327 if (ipc_channel_fd < 0) { | 320 if (ipc_channel_fd < 0) { |
| 328 DLOG(ERROR) << "Failed to find kPrimaryIPCChannel in FD mapping"; | 321 DLOG(ERROR) << "Failed to find kPrimaryIPCChannel in FD mapping"; |
| 329 goto error; | 322 return -1; |
| 330 } | 323 } |
| 324 std::vector<int> fds; | |
| 331 fds.push_back(ipc_channel_fd); // kBrowserFDIndex | 325 fds.push_back(ipc_channel_fd); // kBrowserFDIndex |
| 332 fds.push_back(dummy_fd); // kDummyFDIndex | 326 fds.push_back(pid_oracle.get()); // kPIDOracleFDIndex |
| 333 fds.push_back(pipe_fds[0]); // kParentFDIndex | |
| 334 pid = helper_->Fork(process_type, fds, channel_id); | 327 pid = helper_->Fork(process_type, fds, channel_id); |
| 328 | |
| 329 // Helpers should never return in the child process. | |
| 330 CHECK_NE(pid, 0); | |
| 335 } else { | 331 } else { |
| 332 if (!CreatePipe(&read_pipe, &write_pipe)) { | |
| 333 LOG(ERROR) << "Failed to create pipe"; | |
| 334 return -1; | |
| 335 } | |
| 336 pid = fork(); | 336 pid = fork(); |
| 337 } | 337 } |
| 338 if (pid < 0) { | 338 |
| 339 goto error; | 339 if (pid == 0) { |
| 340 } else if (pid == 0) { | |
| 341 // In the child process. | 340 // In the child process. |
| 342 close(pipe_fds[1]); | 341 write_pipe.reset(); |
| 342 | |
| 343 // Ping the PID oracle socket so the browser can find our PID. | |
| 344 CHECK(UnixDomainSocket::SendMsg( | |
| 345 pid_oracle.get(), "x", 1, std::vector<int>())); | |
|
jln (very slow on Chromium)
2014/05/02 18:25:00
Do you want to make it a defined constant and chec
mdempsky
2014/05/02 23:36:01
Done.
| |
| 346 | |
| 347 // Now read back our real PID from the zygote. | |
| 343 base::ProcessId real_pid; | 348 base::ProcessId real_pid; |
| 344 // Wait until the parent process has discovered our PID. We | 349 if (!base::ReadFromFD(read_pipe.get(), |
|
jln (very slow on Chromium)
2014/05/02 18:25:00
I would much rather not add more of these without
mdempsky
2014/05/02 20:23:09
I'll look into this, and defer if I think it'll ma
mdempsky
2014/05/02 23:36:01
I'm going to keep the pipe using just plain read/w
| |
| 345 // should not fork any child processes (which the seccomp | 350 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))) { | 351 sizeof(real_pid))) { |
| 350 LOG(FATAL) << "Failed to synchronise with parent zygote process"; | 352 LOG(FATAL) << "Failed to synchronise with parent zygote process"; |
| 351 } | 353 } |
| 352 if (real_pid <= 0) { | 354 if (real_pid <= 0) { |
| 353 LOG(FATAL) << "Invalid pid from parent zygote"; | 355 LOG(FATAL) << "Invalid pid from parent zygote"; |
| 354 } | 356 } |
| 355 #if defined(OS_LINUX) | 357 #if defined(OS_LINUX) |
| 356 // Sandboxed processes need to send the global, non-namespaced PID when | 358 // Sandboxed processes need to send the global, non-namespaced PID when |
| 357 // setting up an IPC channel to their parent. | 359 // setting up an IPC channel to their parent. |
| 358 IPC::Channel::SetGlobalPid(real_pid); | 360 IPC::Channel::SetGlobalPid(real_pid); |
| 359 // Force the real PID so chrome event data have a PID that corresponds | 361 // Force the real PID so chrome event data have a PID that corresponds |
| 360 // to system trace event data. | 362 // to system trace event data. |
| 361 base::debug::TraceLog::GetInstance()->SetProcessID( | 363 base::debug::TraceLog::GetInstance()->SetProcessID( |
| 362 static_cast<int>(real_pid)); | 364 static_cast<int>(real_pid)); |
| 363 #endif | 365 #endif |
| 364 close(pipe_fds[0]); | |
| 365 close(dummy_fd); | |
| 366 return 0; | 366 return 0; |
| 367 } else { | 367 } |
| 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 | 368 |
| 380 const ssize_t r = UnixDomainSocket::SendRecvMsg( | 369 // In the parent process. |
| 381 GetSandboxFD(), reply_buf, sizeof(reply_buf), NULL, | 370 read_pipe.reset(); |
| 382 request); | 371 pid_oracle.reset(); |
| 383 if (r == -1) { | |
| 384 LOG(ERROR) << "Failed to get child process's real PID"; | |
| 385 goto error; | |
| 386 } | |
| 387 | 372 |
| 388 Pickle reply(reinterpret_cast<char*>(reply_buf), r); | 373 // Always receive a real PID from the zygote host, though it might |
| 389 PickleIterator iter(reply); | 374 // be invalid (see below). |
| 390 if (!reply.ReadInt(&iter, &real_pid)) | 375 base::ProcessId real_pid; |
| 391 goto error; | 376 CHECK(base::ReadFromFD(kZygoteSocketPairFd, |
| 392 if (real_pid <= 0) { | 377 reinterpret_cast<char*>(&real_pid), |
|
jln (very slow on Chromium)
2014/05/02 18:25:00
Pickle?
mdempsky
2014/05/02 23:36:01
Done.
| |
| 393 // METHOD_GET_CHILD_WITH_INODE failed. Did the child die already? | 378 sizeof(real_pid))); |
| 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 | 379 |
| 403 // Now set-up this process to be tracked by the Zygote. | 380 if (pid < 0) |
| 404 if (process_info_map_.find(real_pid) != process_info_map_.end()) { | 381 return -1; |
| 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 | 382 |
| 411 // If we're using a helper, we still need to let the child process know | 383 // If we successfully forked a child, but it crashed without sending |
| 412 // we've discovered its real PID, but we don't actually reveal the PID. | 384 // a message to the browser, the browser won't have found its PID. |
| 413 const base::ProcessId pid_for_child = use_helper ? 0 : real_pid; | 385 if (real_pid < 0) { |
| 386 error: | |
|
jln (very slow on Chromium)
2014/05/02 18:25:00
Let's kill this label at all cost!
mdempsky
2014/05/02 20:23:09
Yeah, I'm not a fan of it and hope to kill it comp
mdempsky
2014/05/02 23:36:01
Done.
| |
| 387 if (waitpid(pid, NULL, WNOHANG) == -1) | |
|
jln (very slow on Chromium)
2014/05/02 18:25:00
This is racy. The kernel can perfectly return imme
mdempsky
2014/05/02 23:36:01
I changed this to using kill(pid, SIGKILL) in Kill
| |
| 388 LOG(ERROR) << "Failed to wait for child process"; | |
| 389 return -1; | |
| 390 } | |
| 391 | |
| 392 // If we're not using a helper, send the PID back to the child process. | |
|
jln (very slow on Chromium)
2014/05/02 18:25:00
Isn't it better to send something in all cases? I
mdempsky
2014/05/02 20:23:09
I'm leaning towards not sending the PID, but I don
| |
| 393 if (!use_helper) { | |
| 414 ssize_t written = | 394 ssize_t written = |
| 415 HANDLE_EINTR(write(pipe_fds[1], &pid_for_child, sizeof(pid_for_child))); | 395 HANDLE_EINTR(write(write_pipe.get(), &real_pid, sizeof(real_pid))); |
|
jln (very slow on Chromium)
2014/05/02 18:25:00
Pickle?
jln (very slow on Chromium)
2014/05/02 18:25:00
Aren't we at risk of SIGPIPE if the child disappea
mdempsky
2014/05/02 20:23:09
Yeah, I was wondering about that. I was somewhat
mdempsky
2014/05/02 23:36:01
Decided to keep as a pipe and use read/write for n
| |
| 416 if (written != sizeof(pid_for_child)) { | 396 if (written != sizeof(real_pid)) { |
| 417 LOG(ERROR) << "Failed to synchronise with child process"; | 397 LOG(ERROR) << "Failed to synchronise with child process"; |
| 418 goto error; | 398 goto error; |
| 419 } | 399 } |
| 420 close(pipe_fds[1]); | |
| 421 return real_pid; | |
| 422 } | 400 } |
| 423 | 401 |
| 424 error: | 402 // Now set-up this process to be tracked by the Zygote. |
| 425 if (pid > 0) { | 403 if (process_info_map_.find(real_pid) != process_info_map_.end()) { |
| 426 if (waitpid(pid, NULL, WNOHANG) == -1) | 404 LOG(ERROR) << "Already tracking PID " << real_pid; |
| 427 LOG(ERROR) << "Failed to wait for process"; | 405 NOTREACHED(); |
| 428 } | 406 } |
| 429 if (dummy_fd >= 0) | 407 process_info_map_[real_pid].internal_pid = pid; |
| 430 close(dummy_fd); | 408 process_info_map_[real_pid].started_from_helper = use_helper; |
| 431 if (pipe_fds[0] >= 0) | 409 |
| 432 close(pipe_fds[0]); | 410 return real_pid; |
| 433 if (pipe_fds[1] >= 0) | |
| 434 close(pipe_fds[1]); | |
| 435 return -1; | |
| 436 } | 411 } |
| 437 | 412 |
| 438 base::ProcessId Zygote::ReadArgsAndFork(const Pickle& pickle, | 413 base::ProcessId Zygote::ReadArgsAndFork(const Pickle& pickle, |
| 439 PickleIterator iter, | 414 PickleIterator iter, |
| 440 ScopedVector<base::ScopedFD> fds, | 415 ScopedVector<base::ScopedFD> fds, |
| 441 std::string* uma_name, | 416 std::string* uma_name, |
| 442 int* uma_sample, | 417 int* uma_sample, |
| 443 int* uma_boundary_value) { | 418 int* uma_boundary_value) { |
| 444 std::vector<std::string> args; | 419 std::vector<std::string> args; |
| 445 int argc = 0; | 420 int argc = 0; |
| (...skipping 13 matching lines...) Expand all Loading... | |
| 459 std::string arg; | 434 std::string arg; |
| 460 if (!pickle.ReadString(&iter, &arg)) | 435 if (!pickle.ReadString(&iter, &arg)) |
| 461 return -1; | 436 return -1; |
| 462 args.push_back(arg); | 437 args.push_back(arg); |
| 463 if (arg.compare(0, channel_id_prefix.length(), channel_id_prefix) == 0) | 438 if (arg.compare(0, channel_id_prefix.length(), channel_id_prefix) == 0) |
| 464 channel_id = arg.substr(channel_id_prefix.length()); | 439 channel_id = arg.substr(channel_id_prefix.length()); |
| 465 } | 440 } |
| 466 | 441 |
| 467 if (!pickle.ReadInt(&iter, &numfds)) | 442 if (!pickle.ReadInt(&iter, &numfds)) |
| 468 return -1; | 443 return -1; |
| 469 if (numfds != static_cast<int>(fds.size())) | 444 if (numfds != static_cast<int>(fds.size()) - 1) |
|
jln (very slow on Chromium)
2014/05/02 18:25:00
This seems very confusing.
Is it really problemati
mdempsky
2014/05/02 23:36:01
Nope, just didn't think to do that instead. Agree
| |
| 470 return -1; | 445 return -1; |
| 471 | 446 |
| 447 // First FD is the PID oracle socket. | |
| 448 base::ScopedFD pid_oracle(fds[0]->Pass()); | |
| 449 | |
| 450 // Remaining FDs are for the global descriptor mapping. | |
| 472 for (int i = 0; i < numfds; ++i) { | 451 for (int i = 0; i < numfds; ++i) { |
| 473 base::GlobalDescriptors::Key key; | 452 base::GlobalDescriptors::Key key; |
| 474 if (!pickle.ReadUInt32(&iter, &key)) | 453 if (!pickle.ReadUInt32(&iter, &key)) |
| 475 return -1; | 454 return -1; |
| 476 mapping.push_back(std::make_pair(key, fds[i]->get())); | 455 mapping.push_back(std::make_pair(key, fds[1 + i]->get())); |
| 477 } | 456 } |
| 478 | 457 |
| 479 mapping.push_back(std::make_pair( | 458 mapping.push_back(std::make_pair( |
| 480 static_cast<uint32_t>(kSandboxIPCChannel), GetSandboxFD())); | 459 static_cast<uint32_t>(kSandboxIPCChannel), GetSandboxFD())); |
| 481 | 460 |
| 482 // Returns twice, once per process. | 461 // Returns twice, once per process. |
| 483 base::ProcessId child_pid = ForkWithRealPid(process_type, mapping, channel_id, | 462 base::ProcessId child_pid = ForkWithRealPid(process_type, |
| 484 uma_name, uma_sample, | 463 mapping, |
| 464 channel_id, | |
| 465 pid_oracle.Pass(), | |
| 466 uma_name, | |
| 467 uma_sample, | |
| 485 uma_boundary_value); | 468 uma_boundary_value); |
| 486 if (!child_pid) { | 469 if (!child_pid) { |
| 487 // This is the child process. | 470 // This is the child process. |
| 488 | 471 |
| 489 // Our socket from the browser. | 472 // Our socket from the browser. |
| 490 PCHECK(0 == IGNORE_EINTR(close(kZygoteSocketPairFd))); | 473 PCHECK(0 == IGNORE_EINTR(close(kZygoteSocketPairFd))); |
| 491 | 474 |
| 492 // Pass ownership of file descriptors from fds to GlobalDescriptors. | 475 // Pass ownership of file descriptors from fds to GlobalDescriptors. |
| 493 for (ScopedVector<base::ScopedFD>::iterator i = fds.begin(); i != fds.end(); | 476 for (ScopedVector<base::ScopedFD>::iterator i = fds.begin(); i != fds.end(); |
| 494 ++i) | 477 ++i) |
| (...skipping 55 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 550 PickleIterator iter) { | 533 PickleIterator iter) { |
| 551 if (HANDLE_EINTR(write(fd, &sandbox_flags_, sizeof(sandbox_flags_))) != | 534 if (HANDLE_EINTR(write(fd, &sandbox_flags_, sizeof(sandbox_flags_))) != |
| 552 sizeof(sandbox_flags_)) { | 535 sizeof(sandbox_flags_)) { |
| 553 PLOG(ERROR) << "write"; | 536 PLOG(ERROR) << "write"; |
| 554 } | 537 } |
| 555 | 538 |
| 556 return false; | 539 return false; |
| 557 } | 540 } |
| 558 | 541 |
| 559 } // namespace content | 542 } // namespace content |
| OLD | NEW |