Home | History | Annotate | Download | only in zygote_host
      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/browser/zygote_host/zygote_host_impl_linux.h"
      6 
      7 #include <sys/socket.h>
      8 #include <sys/stat.h>
      9 #include <sys/types.h>
     10 #include <unistd.h>
     11 
     12 #include "base/base_switches.h"
     13 #include "base/command_line.h"
     14 #include "base/environment.h"
     15 #include "base/file_util.h"
     16 #include "base/files/file_enumerator.h"
     17 #include "base/linux_util.h"
     18 #include "base/logging.h"
     19 #include "base/memory/linked_ptr.h"
     20 #include "base/memory/scoped_ptr.h"
     21 #include "base/metrics/histogram.h"
     22 #include "base/path_service.h"
     23 #include "base/posix/eintr_wrapper.h"
     24 #include "base/posix/unix_domain_socket_linux.h"
     25 #include "base/process/launch.h"
     26 #include "base/process/memory.h"
     27 #include "base/strings/string_number_conversions.h"
     28 #include "base/strings/string_util.h"
     29 #include "base/strings/utf_string_conversions.h"
     30 #include "base/time/time.h"
     31 #include "content/browser/renderer_host/render_sandbox_host_linux.h"
     32 #include "content/common/child_process_sandbox_support_impl_linux.h"
     33 #include "content/common/zygote_commands_linux.h"
     34 #include "content/public/browser/content_browser_client.h"
     35 #include "content/public/common/content_switches.h"
     36 #include "content/public/common/result_codes.h"
     37 #include "sandbox/linux/suid/client/setuid_sandbox_client.h"
     38 #include "sandbox/linux/suid/common/sandbox.h"
     39 #include "ui/base/ui_base_switches.h"
     40 #include "ui/gfx/switches.h"
     41 
     42 #if defined(USE_TCMALLOC)
     43 #include "third_party/tcmalloc/chromium/src/gperftools/heap-profiler.h"
     44 #endif
     45 
     46 namespace content {
     47 
     48 // static
     49 ZygoteHost* ZygoteHost::GetInstance() {
     50   return ZygoteHostImpl::GetInstance();
     51 }
     52 
     53 ZygoteHostImpl::ZygoteHostImpl()
     54     : control_fd_(-1),
     55       control_lock_(),
     56       pid_(-1),
     57       init_(false),
     58       using_suid_sandbox_(false),
     59       sandbox_binary_(),
     60       have_read_sandbox_status_word_(false),
     61       sandbox_status_(0),
     62       child_tracking_lock_(),
     63       list_of_running_zygote_children_(),
     64       should_teardown_after_last_child_exits_(false) {}
     65 
     66 ZygoteHostImpl::~ZygoteHostImpl() { TearDown(); }
     67 
     68 // static
     69 ZygoteHostImpl* ZygoteHostImpl::GetInstance() {
     70   return Singleton<ZygoteHostImpl>::get();
     71 }
     72 
     73 void ZygoteHostImpl::Init(const std::string& sandbox_cmd) {
     74   DCHECK(!init_);
     75   init_ = true;
     76 
     77   base::FilePath chrome_path;
     78   CHECK(PathService::Get(base::FILE_EXE, &chrome_path));
     79   CommandLine cmd_line(chrome_path);
     80 
     81   cmd_line.AppendSwitchASCII(switches::kProcessType, switches::kZygoteProcess);
     82 
     83   int fds[2];
     84 #if defined(OS_FREEBSD) || defined(OS_OPENBSD)
     85   // The BSDs often don't support SOCK_SEQPACKET yet, so fall back to
     86   // SOCK_DGRAM if necessary.
     87   if (socketpair(PF_UNIX, SOCK_SEQPACKET, 0, fds) != 0)
     88     CHECK(socketpair(PF_UNIX, SOCK_DGRAM, 0, fds) == 0);
     89 #else
     90   CHECK(socketpair(PF_UNIX, SOCK_SEQPACKET, 0, fds) == 0);
     91 #endif
     92   base::FileHandleMappingVector fds_to_map;
     93   fds_to_map.push_back(std::make_pair(fds[1], kZygoteSocketPairFd));
     94 
     95   const CommandLine& browser_command_line = *CommandLine::ForCurrentProcess();
     96   if (browser_command_line.HasSwitch(switches::kZygoteCmdPrefix)) {
     97     cmd_line.PrependWrapper(
     98         browser_command_line.GetSwitchValueNative(switches::kZygoteCmdPrefix));
     99   }
    100   // Append any switches from the browser process that need to be forwarded on
    101   // to the zygote/renderers.
    102   // Should this list be obtained from browser_render_process_host.cc?
    103   static const char* kForwardSwitches[] = {
    104     switches::kAllowSandboxDebugging,
    105     switches::kLoggingLevel,
    106     switches::kEnableLogging,  // Support, e.g., --enable-logging=stderr.
    107     switches::kV,
    108     switches::kVModule,
    109     switches::kRegisterPepperPlugins,
    110     switches::kDisableSeccompFilterSandbox,
    111 
    112     // Zygote process needs to know what resources to have loaded when it
    113     // becomes a renderer process.
    114     switches::kForceDeviceScaleFactor,
    115     switches::kTouchOptimizedUI,
    116 
    117     switches::kNoSandbox,
    118   };
    119   cmd_line.CopySwitchesFrom(browser_command_line, kForwardSwitches,
    120                             arraysize(kForwardSwitches));
    121 
    122   GetContentClient()->browser()->AppendExtraCommandLineSwitches(&cmd_line, -1);
    123 
    124   sandbox_binary_ = sandbox_cmd.c_str();
    125 
    126   // A non empty sandbox_cmd means we want a SUID sandbox.
    127   using_suid_sandbox_ = !sandbox_cmd.empty();
    128 
    129   if (using_suid_sandbox_) {
    130     struct stat st;
    131     if (stat(sandbox_binary_.c_str(), &st) != 0) {
    132       LOG(FATAL) << "The SUID sandbox helper binary is missing: "
    133                  << sandbox_binary_ << " Aborting now.";
    134     }
    135 
    136     if (access(sandbox_binary_.c_str(), X_OK) == 0 &&
    137         (st.st_uid == 0) &&
    138         (st.st_mode & S_ISUID) &&
    139         (st.st_mode & S_IXOTH)) {
    140       cmd_line.PrependWrapper(sandbox_binary_);
    141 
    142       scoped_ptr<sandbox::SetuidSandboxClient>
    143           sandbox_client(sandbox::SetuidSandboxClient::Create());
    144       sandbox_client->SetupLaunchEnvironment();
    145     } else {
    146       LOG(FATAL) << "The SUID sandbox helper binary was found, but is not "
    147                     "configured correctly. Rather than run without sandboxing "
    148                     "I'm aborting now. You need to make sure that "
    149                  << sandbox_binary_ << " is owned by root and has mode 4755.";
    150     }
    151   }
    152 
    153   // Start up the sandbox host process and get the file descriptor for the
    154   // renderers to talk to it.
    155   const int sfd = RenderSandboxHostLinux::GetInstance()->GetRendererSocket();
    156   fds_to_map.push_back(std::make_pair(sfd, GetSandboxFD()));
    157 
    158   int dummy_fd = -1;
    159   if (using_suid_sandbox_) {
    160     dummy_fd = socket(PF_UNIX, SOCK_DGRAM, 0);
    161     CHECK(dummy_fd >= 0);
    162     fds_to_map.push_back(std::make_pair(dummy_fd, kZygoteIdFd));
    163   }
    164 
    165   base::ProcessHandle process = -1;
    166   base::LaunchOptions options;
    167   options.fds_to_remap = &fds_to_map;
    168   base::LaunchProcess(cmd_line.argv(), options, &process);
    169   CHECK(process != -1) << "Failed to launch zygote process";
    170 
    171   if (using_suid_sandbox_) {
    172     // In the SUID sandbox, the real zygote is forked from the sandbox.
    173     // We need to look for it.
    174     // But first, wait for the zygote to tell us it's running.
    175     // The sending code is in content/browser/zygote_main_linux.cc.
    176     std::vector<int> fds_vec;
    177     const int kExpectedLength = sizeof(kZygoteHelloMessage);
    178     char buf[kExpectedLength];
    179     const ssize_t len = UnixDomainSocket::RecvMsg(fds[0], buf, sizeof(buf),
    180                                                   &fds_vec);
    181     CHECK(len == kExpectedLength) << "Incorrect zygote magic length";
    182     CHECK(0 == strcmp(buf, kZygoteHelloMessage))
    183         << "Incorrect zygote hello";
    184 
    185     std::string inode_output;
    186     ino_t inode = 0;
    187     // Figure out the inode for |dummy_fd|, close |dummy_fd| on our end,
    188     // and find the zygote process holding |dummy_fd|.
    189     if (base::FileDescriptorGetInode(&inode, dummy_fd)) {
    190       close(dummy_fd);
    191       std::vector<std::string> get_inode_cmdline;
    192       get_inode_cmdline.push_back(sandbox_binary_);
    193       get_inode_cmdline.push_back(base::kFindInodeSwitch);
    194       get_inode_cmdline.push_back(base::Int64ToString(inode));
    195       CommandLine get_inode_cmd(get_inode_cmdline);
    196       if (base::GetAppOutput(get_inode_cmd, &inode_output)) {
    197         base::StringToInt(inode_output, &pid_);
    198       }
    199     }
    200     CHECK(pid_ > 0) << "Did not find zygote process (using sandbox binary "
    201         << sandbox_binary_ << ")";
    202 
    203     if (process != pid_) {
    204       // Reap the sandbox.
    205       base::EnsureProcessGetsReaped(process);
    206     }
    207   } else {
    208     // Not using the SUID sandbox.
    209     pid_ = process;
    210   }
    211 
    212   close(fds[1]);
    213   control_fd_ = fds[0];
    214 
    215   Pickle pickle;
    216   pickle.WriteInt(kZygoteCommandGetSandboxStatus);
    217   if (!SendMessage(pickle, NULL))
    218     LOG(FATAL) << "Cannot communicate with zygote";
    219   // We don't wait for the reply. We'll read it in ReadReply.
    220 }
    221 
    222 void ZygoteHostImpl::TearDownAfterLastChild() {
    223   bool do_teardown = false;
    224   {
    225     base::AutoLock lock(child_tracking_lock_);
    226     should_teardown_after_last_child_exits_ = true;
    227     do_teardown = list_of_running_zygote_children_.empty();
    228   }
    229   if (do_teardown) {
    230     TearDown();
    231   }
    232 }
    233 
    234 // Note: this is also called from the destructor.
    235 void ZygoteHostImpl::TearDown() {
    236   base::AutoLock lock(control_lock_);
    237   if (control_fd_ > -1) {
    238     // Closing the IPC channel will act as a notification to exit
    239     // to the Zygote.
    240     if (IGNORE_EINTR(close(control_fd_))) {
    241       PLOG(ERROR) << "Could not close Zygote control channel.";
    242       NOTREACHED();
    243     }
    244     control_fd_ = -1;
    245   }
    246 }
    247 
    248 void ZygoteHostImpl::ZygoteChildBorn(pid_t process) {
    249   base::AutoLock lock(child_tracking_lock_);
    250   bool new_element_inserted =
    251       list_of_running_zygote_children_.insert(process).second;
    252   DCHECK(new_element_inserted);
    253 }
    254 
    255 void ZygoteHostImpl::ZygoteChildDied(pid_t process) {
    256   bool do_teardown = false;
    257   {
    258     base::AutoLock lock(child_tracking_lock_);
    259     size_t num_erased = list_of_running_zygote_children_.erase(process);
    260     DCHECK_EQ(1U, num_erased);
    261     do_teardown = should_teardown_after_last_child_exits_ &&
    262                   list_of_running_zygote_children_.empty();
    263   }
    264   if (do_teardown) {
    265     TearDown();
    266   }
    267 }
    268 
    269 bool ZygoteHostImpl::SendMessage(const Pickle& data,
    270                                  const std::vector<int>* fds) {
    271   DCHECK_NE(-1, control_fd_);
    272   CHECK(data.size() <= kZygoteMaxMessageLength)
    273       << "Trying to send too-large message to zygote (sending " << data.size()
    274       << " bytes, max is " << kZygoteMaxMessageLength << ")";
    275   CHECK(!fds || fds->size() <= UnixDomainSocket::kMaxFileDescriptors)
    276       << "Trying to send message with too many file descriptors to zygote "
    277       << "(sending " << fds->size() << ", max is "
    278       << UnixDomainSocket::kMaxFileDescriptors << ")";
    279 
    280   return UnixDomainSocket::SendMsg(control_fd_,
    281                                    data.data(), data.size(),
    282                                    fds ? *fds : std::vector<int>());
    283 }
    284 
    285 ssize_t ZygoteHostImpl::ReadReply(void* buf, size_t buf_len) {
    286   DCHECK_NE(-1, control_fd_);
    287   // At startup we send a kZygoteCommandGetSandboxStatus request to the zygote,
    288   // but don't wait for the reply. Thus, the first time that we read from the
    289   // zygote, we get the reply to that request.
    290   if (!have_read_sandbox_status_word_) {
    291     if (HANDLE_EINTR(read(control_fd_, &sandbox_status_,
    292                           sizeof(sandbox_status_))) !=
    293         sizeof(sandbox_status_)) {
    294       return -1;
    295     }
    296     have_read_sandbox_status_word_ = true;
    297   }
    298 
    299   return HANDLE_EINTR(read(control_fd_, buf, buf_len));
    300 }
    301 
    302 pid_t ZygoteHostImpl::ForkRequest(
    303     const std::vector<std::string>& argv,
    304     const std::vector<FileDescriptorInfo>& mapping,
    305     const std::string& process_type) {
    306   DCHECK(init_);
    307   Pickle pickle;
    308 
    309   pickle.WriteInt(kZygoteCommandFork);
    310   pickle.WriteString(process_type);
    311   pickle.WriteInt(argv.size());
    312   for (std::vector<std::string>::const_iterator
    313        i = argv.begin(); i != argv.end(); ++i)
    314     pickle.WriteString(*i);
    315 
    316   pickle.WriteInt(mapping.size());
    317 
    318   std::vector<int> fds;
    319   // Scoped pointers cannot be stored in containers, so we have to use a
    320   // linked_ptr.
    321   std::vector<linked_ptr<file_util::ScopedFD> > autodelete_fds;
    322   for (std::vector<FileDescriptorInfo>::const_iterator
    323        i = mapping.begin(); i != mapping.end(); ++i) {
    324     pickle.WriteUInt32(i->id);
    325     fds.push_back(i->fd.fd);
    326     if (i->fd.auto_close) {
    327       // Auto-close means we need to close the FDs after they have been passed
    328       // to the other process.
    329       linked_ptr<file_util::ScopedFD> ptr(
    330           new file_util::ScopedFD(&(fds.back())));
    331       autodelete_fds.push_back(ptr);
    332     }
    333   }
    334 
    335   pid_t pid;
    336   {
    337     base::AutoLock lock(control_lock_);
    338     if (!SendMessage(pickle, &fds))
    339       return base::kNullProcessHandle;
    340 
    341     // Read the reply, which pickles the PID and an optional UMA enumeration.
    342     static const unsigned kMaxReplyLength = 2048;
    343     char buf[kMaxReplyLength];
    344     const ssize_t len = ReadReply(buf, sizeof(buf));
    345 
    346     Pickle reply_pickle(buf, len);
    347     PickleIterator iter(reply_pickle);
    348     if (len <= 0 || !reply_pickle.ReadInt(&iter, &pid))
    349       return base::kNullProcessHandle;
    350 
    351     // If there is a nonempty UMA name string, then there is a UMA
    352     // enumeration to record.
    353     std::string uma_name;
    354     int uma_sample;
    355     int uma_boundary_value;
    356     if (reply_pickle.ReadString(&iter, &uma_name) &&
    357         !uma_name.empty() &&
    358         reply_pickle.ReadInt(&iter, &uma_sample) &&
    359         reply_pickle.ReadInt(&iter, &uma_boundary_value)) {
    360       // We cannot use the UMA_HISTOGRAM_ENUMERATION macro here,
    361       // because that's only for when the name is the same every time.
    362       // Here we're using whatever name we got from the other side.
    363       // But since it's likely that the same one will be used repeatedly
    364       // (even though it's not guaranteed), we cache it here.
    365       static base::HistogramBase* uma_histogram;
    366       if (!uma_histogram || uma_histogram->histogram_name() != uma_name) {
    367         uma_histogram = base::LinearHistogram::FactoryGet(
    368             uma_name, 1,
    369             uma_boundary_value,
    370             uma_boundary_value + 1,
    371             base::HistogramBase::kUmaTargetedHistogramFlag);
    372       }
    373       uma_histogram->Add(uma_sample);
    374     }
    375 
    376     if (pid <= 0)
    377       return base::kNullProcessHandle;
    378   }
    379 
    380 #if !defined(OS_OPENBSD)
    381   // This is just a starting score for a renderer or extension (the
    382   // only types of processes that will be started this way).  It will
    383   // get adjusted as time goes on.  (This is the same value as
    384   // chrome::kLowestRendererOomScore in chrome/chrome_constants.h, but
    385   // that's not something we can include here.)
    386   const int kLowestRendererOomScore = 300;
    387   AdjustRendererOOMScore(pid, kLowestRendererOomScore);
    388 #endif
    389 
    390   ZygoteChildBorn(pid);
    391   return pid;
    392 }
    393 
    394 #if !defined(OS_OPENBSD)
    395 void ZygoteHostImpl::AdjustRendererOOMScore(base::ProcessHandle pid,
    396                                             int score) {
    397   // 1) You can't change the oom_score_adj of a non-dumpable process
    398   //    (EPERM) unless you're root. Because of this, we can't set the
    399   //    oom_adj from the browser process.
    400   //
    401   // 2) We can't set the oom_score_adj before entering the sandbox
    402   //    because the zygote is in the sandbox and the zygote is as
    403   //    critical as the browser process. Its oom_adj value shouldn't
    404   //    be changed.
    405   //
    406   // 3) A non-dumpable process can't even change its own oom_score_adj
    407   //    because it's root owned 0644. The sandboxed processes don't
    408   //    even have /proc, but one could imagine passing in a descriptor
    409   //    from outside.
    410   //
    411   // So, in the normal case, we use the SUID binary to change it for us.
    412   // However, Fedora (and other SELinux systems) don't like us touching other
    413   // process's oom_score_adj (or oom_adj) values
    414   // (https://bugzilla.redhat.com/show_bug.cgi?id=581256).
    415   //
    416   // The offical way to get the SELinux mode is selinux_getenforcemode, but I
    417   // don't want to add another library to the build as it's sure to cause
    418   // problems with other, non-SELinux distros.
    419   //
    420   // So we just check for files in /selinux. This isn't foolproof, but it's not
    421   // bad and it's easy.
    422 
    423   static bool selinux;
    424   static bool selinux_valid = false;
    425 
    426   if (!selinux_valid) {
    427     const base::FilePath kSelinuxPath("/selinux");
    428     base::FileEnumerator en(kSelinuxPath, false, base::FileEnumerator::FILES);
    429     bool has_selinux_files = !en.Next().empty();
    430 
    431     selinux = access(kSelinuxPath.value().c_str(), X_OK) == 0 &&
    432               has_selinux_files;
    433     selinux_valid = true;
    434   }
    435 
    436   if (using_suid_sandbox_ && !selinux) {
    437 #if defined(USE_TCMALLOC)
    438     // If heap profiling is running, these processes are not exiting, at least
    439     // on ChromeOS. The easiest thing to do is not launch them when profiling.
    440     // TODO(stevenjb): Investigate further and fix.
    441     if (IsHeapProfilerRunning())
    442       return;
    443 #endif
    444     std::vector<std::string> adj_oom_score_cmdline;
    445     adj_oom_score_cmdline.push_back(sandbox_binary_);
    446     adj_oom_score_cmdline.push_back(sandbox::kAdjustOOMScoreSwitch);
    447     adj_oom_score_cmdline.push_back(base::Int64ToString(pid));
    448     adj_oom_score_cmdline.push_back(base::IntToString(score));
    449 
    450     base::ProcessHandle sandbox_helper_process;
    451     if (base::LaunchProcess(adj_oom_score_cmdline, base::LaunchOptions(),
    452                             &sandbox_helper_process)) {
    453       base::EnsureProcessGetsReaped(sandbox_helper_process);
    454     }
    455   } else if (!using_suid_sandbox_) {
    456     if (!base::AdjustOOMScore(pid, score))
    457       PLOG(ERROR) << "Failed to adjust OOM score of renderer with pid " << pid;
    458   }
    459 }
    460 #endif
    461 
    462 void ZygoteHostImpl::EnsureProcessTerminated(pid_t process) {
    463   DCHECK(init_);
    464   Pickle pickle;
    465 
    466   pickle.WriteInt(kZygoteCommandReap);
    467   pickle.WriteInt(process);
    468   if (!SendMessage(pickle, NULL))
    469     LOG(ERROR) << "Failed to send Reap message to zygote";
    470   ZygoteChildDied(process);
    471 }
    472 
    473 base::TerminationStatus ZygoteHostImpl::GetTerminationStatus(
    474     base::ProcessHandle handle,
    475     bool known_dead,
    476     int* exit_code) {
    477   DCHECK(init_);
    478   Pickle pickle;
    479   pickle.WriteInt(kZygoteCommandGetTerminationStatus);
    480   pickle.WriteBool(known_dead);
    481   pickle.WriteInt(handle);
    482 
    483   static const unsigned kMaxMessageLength = 128;
    484   char buf[kMaxMessageLength];
    485   ssize_t len;
    486   {
    487     base::AutoLock lock(control_lock_);
    488     if (!SendMessage(pickle, NULL))
    489       LOG(ERROR) << "Failed to send GetTerminationStatus message to zygote";
    490     len = ReadReply(buf, sizeof(buf));
    491   }
    492 
    493   // Set this now to handle the error cases.
    494   if (exit_code)
    495     *exit_code = RESULT_CODE_NORMAL_EXIT;
    496   int status = base::TERMINATION_STATUS_NORMAL_TERMINATION;
    497 
    498   if (len == -1) {
    499     LOG(WARNING) << "Error reading message from zygote: " << errno;
    500   } else if (len == 0) {
    501     LOG(WARNING) << "Socket closed prematurely.";
    502   } else {
    503     Pickle read_pickle(buf, len);
    504     int tmp_status, tmp_exit_code;
    505     PickleIterator iter(read_pickle);
    506     if (!read_pickle.ReadInt(&iter, &tmp_status) ||
    507         !read_pickle.ReadInt(&iter, &tmp_exit_code)) {
    508       LOG(WARNING)
    509           << "Error parsing GetTerminationStatus response from zygote.";
    510     } else {
    511       if (exit_code)
    512         *exit_code = tmp_exit_code;
    513       status = tmp_status;
    514     }
    515   }
    516 
    517   if (status != base::TERMINATION_STATUS_STILL_RUNNING) {
    518     ZygoteChildDied(handle);
    519   }
    520   return static_cast<base::TerminationStatus>(status);
    521 }
    522 
    523 pid_t ZygoteHostImpl::GetPid() const {
    524   return pid_;
    525 }
    526 
    527 pid_t ZygoteHostImpl::GetSandboxHelperPid() const {
    528   return RenderSandboxHostLinux::GetInstance()->pid();
    529 }
    530 
    531 int ZygoteHostImpl::GetSandboxStatus() const {
    532   if (have_read_sandbox_status_word_)
    533     return sandbox_status_;
    534   return 0;
    535 }
    536 
    537 }  // namespace content
    538