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

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: Fix compile error 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 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
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
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
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
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
OLDNEW
« content/common/zygote_commands_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