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

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: Respond to jln feedback 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
« content/zygote/zygote_linux.h ('K') | « content/zygote/zygote_linux.h ('k') | no next file » | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
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 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
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
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
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
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
OLDNEW
« content/zygote/zygote_linux.h ('K') | « content/zygote/zygote_linux.h ('k') | no next file » | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698