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