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