Home | History | Annotate | Download | only in zygote
      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
      3 // found in the LICENSE file.
      4 
      5 #include "content/zygote/zygote_linux.h"
      6 
      7 #include <fcntl.h>
      8 #include <string.h>
      9 #include <sys/socket.h>
     10 #include <sys/types.h>
     11 #include <sys/wait.h>
     12 
     13 #include "base/command_line.h"
     14 #include "base/debug/trace_event.h"
     15 #include "base/file_util.h"
     16 #include "base/linux_util.h"
     17 #include "base/logging.h"
     18 #include "base/pickle.h"
     19 #include "base/posix/eintr_wrapper.h"
     20 #include "base/posix/global_descriptors.h"
     21 #include "base/posix/unix_domain_socket_linux.h"
     22 #include "base/process/kill.h"
     23 #include "content/common/sandbox_linux.h"
     24 #include "content/common/set_process_title.h"
     25 #include "content/common/zygote_commands_linux.h"
     26 #include "content/public/common/content_descriptors.h"
     27 #include "content/public/common/result_codes.h"
     28 #include "content/public/common/sandbox_linux.h"
     29 #include "content/public/common/zygote_fork_delegate_linux.h"
     30 #include "ipc/ipc_channel.h"
     31 #include "ipc/ipc_switches.h"
     32 
     33 // See http://code.google.com/p/chromium/wiki/LinuxZygote
     34 
     35 namespace content {
     36 
     37 namespace {
     38 
     39 // NOP function. See below where this handler is installed.
     40 void SIGCHLDHandler(int signal) {
     41 }
     42 
     43 }  // namespace
     44 
     45 const int Zygote::kMagicSandboxIPCDescriptor;
     46 
     47 Zygote::Zygote(int sandbox_flags,
     48                ZygoteForkDelegate* helper)
     49     : sandbox_flags_(sandbox_flags),
     50       helper_(helper),
     51       initial_uma_sample_(0),
     52       initial_uma_boundary_value_(0) {
     53   if (helper_) {
     54     helper_->InitialUMA(&initial_uma_name_,
     55                         &initial_uma_sample_,
     56                         &initial_uma_boundary_value_);
     57   }
     58 }
     59 
     60 Zygote::~Zygote() {
     61 }
     62 
     63 bool Zygote::ProcessRequests() {
     64   // A SOCK_SEQPACKET socket is installed in fd 3. We get commands from the
     65   // browser on it.
     66   // A SOCK_DGRAM is installed in fd 5. This is the sandbox IPC channel.
     67   // See http://code.google.com/p/chromium/wiki/LinuxSandboxIPC
     68 
     69   // We need to accept SIGCHLD, even though our handler is a no-op because
     70   // otherwise we cannot wait on children. (According to POSIX 2001.)
     71   struct sigaction action;
     72   memset(&action, 0, sizeof(action));
     73   action.sa_handler = &SIGCHLDHandler;
     74   CHECK(sigaction(SIGCHLD, &action, NULL) == 0);
     75 
     76   if (UsingSUIDSandbox()) {
     77     // Let the ZygoteHost know we are ready to go.
     78     // The receiving code is in content/browser/zygote_host_linux.cc.
     79     std::vector<int> empty;
     80     bool r = UnixDomainSocket::SendMsg(kBrowserDescriptor,
     81                                        kZygoteHelloMessage,
     82                                        sizeof(kZygoteHelloMessage), empty);
     83 #if defined(OS_CHROMEOS)
     84     LOG_IF(WARNING, !r) << "Sending zygote magic failed";
     85     // Exit normally on chromeos because session manager may send SIGTERM
     86     // right after the process starts and it may fail to send zygote magic
     87     // number to browser process.
     88     if (!r)
     89       _exit(RESULT_CODE_NORMAL_EXIT);
     90 #else
     91     CHECK(r) << "Sending zygote magic failed";
     92 #endif
     93   }
     94 
     95   for (;;) {
     96     // This function call can return multiple times, once per fork().
     97     if (HandleRequestFromBrowser(kBrowserDescriptor))
     98       return true;
     99   }
    100 }
    101 
    102 bool Zygote::UsingSUIDSandbox() const {
    103   return sandbox_flags_ & kSandboxLinuxSUID;
    104 }
    105 
    106 bool Zygote::HandleRequestFromBrowser(int fd) {
    107   std::vector<int> fds;
    108   char buf[kZygoteMaxMessageLength];
    109   const ssize_t len = UnixDomainSocket::RecvMsg(fd, buf, sizeof(buf), &fds);
    110 
    111   if (len == 0 || (len == -1 && errno == ECONNRESET)) {
    112     // EOF from the browser. We should die.
    113     _exit(0);
    114     return false;
    115   }
    116 
    117   if (len == -1) {
    118     PLOG(ERROR) << "Error reading message from browser";
    119     return false;
    120   }
    121 
    122   Pickle pickle(buf, len);
    123   PickleIterator iter(pickle);
    124 
    125   int kind;
    126   if (pickle.ReadInt(&iter, &kind)) {
    127     switch (kind) {
    128       case kZygoteCommandFork:
    129         // This function call can return multiple times, once per fork().
    130         return HandleForkRequest(fd, pickle, iter, fds);
    131 
    132       case kZygoteCommandReap:
    133         if (!fds.empty())
    134           break;
    135         HandleReapRequest(fd, pickle, iter);
    136         return false;
    137       case kZygoteCommandGetTerminationStatus:
    138         if (!fds.empty())
    139           break;
    140         HandleGetTerminationStatus(fd, pickle, iter);
    141         return false;
    142       case kZygoteCommandGetSandboxStatus:
    143         HandleGetSandboxStatus(fd, pickle, iter);
    144         return false;
    145       default:
    146         NOTREACHED();
    147         break;
    148     }
    149   }
    150 
    151   LOG(WARNING) << "Error parsing message from browser";
    152   for (std::vector<int>::const_iterator
    153        i = fds.begin(); i != fds.end(); ++i)
    154     close(*i);
    155   return false;
    156 }
    157 
    158 void Zygote::HandleReapRequest(int fd,
    159                                const Pickle& pickle,
    160                                PickleIterator iter) {
    161   base::ProcessId child;
    162   base::ProcessId actual_child;
    163 
    164   if (!pickle.ReadInt(&iter, &child)) {
    165     LOG(WARNING) << "Error parsing reap request from browser";
    166     return;
    167   }
    168 
    169   if (UsingSUIDSandbox()) {
    170     actual_child = real_pids_to_sandbox_pids[child];
    171     if (!actual_child)
    172       return;
    173     real_pids_to_sandbox_pids.erase(child);
    174   } else {
    175     actual_child = child;
    176   }
    177 
    178   base::EnsureProcessTerminated(actual_child);
    179 }
    180 
    181 void Zygote::HandleGetTerminationStatus(int fd,
    182                                         const Pickle& pickle,
    183                                         PickleIterator iter) {
    184   bool known_dead;
    185   base::ProcessHandle child;
    186 
    187   if (!pickle.ReadBool(&iter, &known_dead) ||
    188       !pickle.ReadInt(&iter, &child)) {
    189     LOG(WARNING) << "Error parsing GetTerminationStatus request "
    190                  << "from browser";
    191     return;
    192   }
    193 
    194   base::TerminationStatus status;
    195   int exit_code;
    196   if (UsingSUIDSandbox())
    197     child = real_pids_to_sandbox_pids[child];
    198   if (child) {
    199     if (known_dead) {
    200       // If we know that the process is already dead and the kernel is cleaning
    201       // it up, we do want to wait until it becomes a zombie and not risk
    202       // returning eroneously that it is still running. However, we do not
    203       // want to risk a bug where we're told a process is dead when it's not.
    204       // By sending SIGKILL, we make sure that WaitForTerminationStatus will
    205       // return quickly even in this case.
    206       if (kill(child, SIGKILL)) {
    207         PLOG(ERROR) << "kill (" << child << ")";
    208       }
    209       status = base::WaitForTerminationStatus(child, &exit_code);
    210     } else {
    211       status = base::GetTerminationStatus(child, &exit_code);
    212     }
    213   } else {
    214     // Assume that if we can't find the child in the sandbox, then
    215     // it terminated normally.
    216     status = base::TERMINATION_STATUS_NORMAL_TERMINATION;
    217     exit_code = RESULT_CODE_NORMAL_EXIT;
    218   }
    219 
    220   Pickle write_pickle;
    221   write_pickle.WriteInt(static_cast<int>(status));
    222   write_pickle.WriteInt(exit_code);
    223   ssize_t written =
    224       HANDLE_EINTR(write(fd, write_pickle.data(), write_pickle.size()));
    225   if (written != static_cast<ssize_t>(write_pickle.size()))
    226     PLOG(ERROR) << "write";
    227 }
    228 
    229 int Zygote::ForkWithRealPid(const std::string& process_type,
    230                             std::vector<int>& fds,
    231                             const std::string& channel_switch,
    232                             std::string* uma_name,
    233                             int* uma_sample,
    234                             int* uma_boundary_value) {
    235   const bool use_helper = (helper_ && helper_->CanHelp(process_type,
    236                                                        uma_name,
    237                                                        uma_sample,
    238                                                        uma_boundary_value));
    239   if (!(use_helper || UsingSUIDSandbox())) {
    240     return fork();
    241   }
    242 
    243   int dummy_fd;
    244   ino_t dummy_inode;
    245   int pipe_fds[2] = { -1, -1 };
    246   base::ProcessId pid = 0;
    247 
    248   dummy_fd = socket(PF_UNIX, SOCK_DGRAM, 0);
    249   if (dummy_fd < 0) {
    250     LOG(ERROR) << "Failed to create dummy FD";
    251     goto error;
    252   }
    253   if (!base::FileDescriptorGetInode(&dummy_inode, dummy_fd)) {
    254     LOG(ERROR) << "Failed to get inode for dummy FD";
    255     goto error;
    256   }
    257   if (pipe(pipe_fds) != 0) {
    258     LOG(ERROR) << "Failed to create pipe";
    259     goto error;
    260   }
    261 
    262   if (use_helper) {
    263     fds.push_back(dummy_fd);
    264     fds.push_back(pipe_fds[0]);
    265     pid = helper_->Fork(fds);
    266   } else {
    267     pid = fork();
    268   }
    269   if (pid < 0) {
    270     goto error;
    271   } else if (pid == 0) {
    272     // In the child process.
    273     close(pipe_fds[1]);
    274     base::ProcessId real_pid;
    275     // Wait until the parent process has discovered our PID.  We
    276     // should not fork any child processes (which the seccomp
    277     // sandbox does) until then, because that can interfere with the
    278     // parent's discovery of our PID.
    279     if (!file_util::ReadFromFD(pipe_fds[0],
    280                                reinterpret_cast<char*>(&real_pid),
    281                                sizeof(real_pid))) {
    282       LOG(FATAL) << "Failed to synchronise with parent zygote process";
    283     }
    284     if (real_pid <= 0) {
    285       LOG(FATAL) << "Invalid pid from parent zygote";
    286     }
    287 #if defined(OS_LINUX)
    288     // Sandboxed processes need to send the global, non-namespaced PID when
    289     // setting up an IPC channel to their parent.
    290     IPC::Channel::SetGlobalPid(real_pid);
    291     // Force the real PID so chrome event data have a PID that corresponds
    292     // to system trace event data.
    293     base::debug::TraceLog::GetInstance()->SetProcessID(
    294         static_cast<int>(real_pid));
    295 #endif
    296     close(pipe_fds[0]);
    297     close(dummy_fd);
    298     return 0;
    299   } else {
    300     // In the parent process.
    301     close(dummy_fd);
    302     dummy_fd = -1;
    303     close(pipe_fds[0]);
    304     pipe_fds[0] = -1;
    305     base::ProcessId real_pid;
    306     if (UsingSUIDSandbox()) {
    307       uint8_t reply_buf[512];
    308       Pickle request;
    309       request.WriteInt(LinuxSandbox::METHOD_GET_CHILD_WITH_INODE);
    310       request.WriteUInt64(dummy_inode);
    311 
    312       const ssize_t r = UnixDomainSocket::SendRecvMsg(
    313           kMagicSandboxIPCDescriptor, reply_buf, sizeof(reply_buf), NULL,
    314           request);
    315       if (r == -1) {
    316         LOG(ERROR) << "Failed to get child process's real PID";
    317         goto error;
    318       }
    319 
    320       Pickle reply(reinterpret_cast<char*>(reply_buf), r);
    321       PickleIterator iter(reply);
    322       if (!reply.ReadInt(&iter, &real_pid))
    323         goto error;
    324       if (real_pid <= 0) {
    325         // METHOD_GET_CHILD_WITH_INODE failed. Did the child die already?
    326         LOG(ERROR) << "METHOD_GET_CHILD_WITH_INODE failed";
    327         goto error;
    328       }
    329       real_pids_to_sandbox_pids[real_pid] = pid;
    330     }
    331     if (use_helper) {
    332       real_pid = pid;
    333       if (!helper_->AckChild(pipe_fds[1], channel_switch)) {
    334         LOG(ERROR) << "Failed to synchronise with zygote fork helper";
    335         goto error;
    336       }
    337     } else {
    338       int written =
    339           HANDLE_EINTR(write(pipe_fds[1], &real_pid, sizeof(real_pid)));
    340       if (written != sizeof(real_pid)) {
    341         LOG(ERROR) << "Failed to synchronise with child process";
    342         goto error;
    343       }
    344     }
    345     close(pipe_fds[1]);
    346     return real_pid;
    347   }
    348 
    349  error:
    350   if (pid > 0) {
    351     if (waitpid(pid, NULL, WNOHANG) == -1)
    352       LOG(ERROR) << "Failed to wait for process";
    353   }
    354   if (dummy_fd >= 0)
    355     close(dummy_fd);
    356   if (pipe_fds[0] >= 0)
    357     close(pipe_fds[0]);
    358   if (pipe_fds[1] >= 0)
    359     close(pipe_fds[1]);
    360   return -1;
    361 }
    362 
    363 base::ProcessId Zygote::ReadArgsAndFork(const Pickle& pickle,
    364                                         PickleIterator iter,
    365                                         std::vector<int>& fds,
    366                                         std::string* uma_name,
    367                                         int* uma_sample,
    368                                         int* uma_boundary_value) {
    369   std::vector<std::string> args;
    370   int argc = 0;
    371   int numfds = 0;
    372   base::GlobalDescriptors::Mapping mapping;
    373   std::string process_type;
    374   std::string channel_id;
    375   const std::string channel_id_prefix = std::string("--")
    376       + switches::kProcessChannelID + std::string("=");
    377 
    378   if (!pickle.ReadString(&iter, &process_type))
    379     return -1;
    380   if (!pickle.ReadInt(&iter, &argc))
    381     return -1;
    382 
    383   for (int i = 0; i < argc; ++i) {
    384     std::string arg;
    385     if (!pickle.ReadString(&iter, &arg))
    386       return -1;
    387     args.push_back(arg);
    388     if (arg.compare(0, channel_id_prefix.length(), channel_id_prefix) == 0)
    389       channel_id = arg;
    390   }
    391 
    392   if (!pickle.ReadInt(&iter, &numfds))
    393     return -1;
    394   if (numfds != static_cast<int>(fds.size()))
    395     return -1;
    396 
    397   for (int i = 0; i < numfds; ++i) {
    398     base::GlobalDescriptors::Key key;
    399     if (!pickle.ReadUInt32(&iter, &key))
    400       return -1;
    401     mapping.push_back(std::make_pair(key, fds[i]));
    402   }
    403 
    404   mapping.push_back(std::make_pair(
    405       static_cast<uint32_t>(kSandboxIPCChannel), kMagicSandboxIPCDescriptor));
    406 
    407   // Returns twice, once per process.
    408   base::ProcessId child_pid = ForkWithRealPid(process_type, fds, channel_id,
    409                                               uma_name, uma_sample,
    410                                               uma_boundary_value);
    411   if (!child_pid) {
    412     // This is the child process.
    413 
    414     close(kBrowserDescriptor);  // Our socket from the browser.
    415     if (UsingSUIDSandbox())
    416       close(kZygoteIdFd);  // Another socket from the browser.
    417     base::GlobalDescriptors::GetInstance()->Reset(mapping);
    418 
    419     // Reset the process-wide command line to our new command line.
    420     CommandLine::Reset();
    421     CommandLine::Init(0, NULL);
    422     CommandLine::ForCurrentProcess()->InitFromArgv(args);
    423 
    424     // Update the process title. The argv was already cached by the call to
    425     // SetProcessTitleFromCommandLine in ChromeMain, so we can pass NULL here
    426     // (we don't have the original argv at this point).
    427     SetProcessTitleFromCommandLine(NULL);
    428   } else if (child_pid < 0) {
    429     LOG(ERROR) << "Zygote could not fork: process_type " << process_type
    430         << " numfds " << numfds << " child_pid " << child_pid;
    431   }
    432   return child_pid;
    433 }
    434 
    435 bool Zygote::HandleForkRequest(int fd,
    436                                const Pickle& pickle,
    437                                PickleIterator iter,
    438                                std::vector<int>& fds) {
    439   std::string uma_name;
    440   int uma_sample;
    441   int uma_boundary_value;
    442   base::ProcessId child_pid = ReadArgsAndFork(pickle, iter, fds,
    443                                               &uma_name, &uma_sample,
    444                                               &uma_boundary_value);
    445   if (child_pid == 0)
    446     return true;
    447   for (std::vector<int>::const_iterator
    448        i = fds.begin(); i != fds.end(); ++i)
    449     close(*i);
    450   if (uma_name.empty()) {
    451     // There is no UMA report from this particular fork.
    452     // Use the initial UMA report if any, and clear that record for next time.
    453     // Note the swap method here is the efficient way to do this, since
    454     // we know uma_name is empty.
    455     uma_name.swap(initial_uma_name_);
    456     uma_sample = initial_uma_sample_;
    457     uma_boundary_value = initial_uma_boundary_value_;
    458   }
    459   // Must always send reply, as ZygoteHost blocks while waiting for it.
    460   Pickle reply_pickle;
    461   reply_pickle.WriteInt(child_pid);
    462   reply_pickle.WriteString(uma_name);
    463   if (!uma_name.empty()) {
    464     reply_pickle.WriteInt(uma_sample);
    465     reply_pickle.WriteInt(uma_boundary_value);
    466   }
    467   if (HANDLE_EINTR(write(fd, reply_pickle.data(), reply_pickle.size())) !=
    468       static_cast<ssize_t> (reply_pickle.size()))
    469     PLOG(ERROR) << "write";
    470   return false;
    471 }
    472 
    473 bool Zygote::HandleGetSandboxStatus(int fd,
    474                                     const Pickle& pickle,
    475                                     PickleIterator iter) {
    476   if (HANDLE_EINTR(write(fd, &sandbox_flags_, sizeof(sandbox_flags_))) !=
    477                    sizeof(sandbox_flags_)) {
    478     PLOG(ERROR) << "write";
    479   }
    480 
    481   return false;
    482 }
    483 
    484 }  // namespace content
    485