Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(38)

Side by Side Diff: content/zygote/zygote_linux.cc

Issue 269543014: Use RecvMsgWithPid to find real PID for zygote children (Closed) Base URL: svn://svn.chromium.org/chrome/trunk/src
Patch Set: Tweak PID discovery to handle crashing child processes Created 6 years, 7 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch | Annotate | Revision Log
OLDNEW
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
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
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
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
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
OLDNEW
« content/browser/zygote_host/zygote_host_impl_linux.cc ('K') | « content/zygote/zygote_linux.h ('k') | no next file » | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698