Update To 11.40.268.0
[platform/framework/web/crosswalk.git] / src / components / nacl / zygote / nacl_fork_delegate_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 "components/nacl/zygote/nacl_fork_delegate_linux.h"
6
7 #include <signal.h>
8 #include <stdlib.h>
9 #include <sys/resource.h>
10 #include <sys/socket.h>
11
12 #include <set>
13
14 #include "base/basictypes.h"
15 #include "base/command_line.h"
16 #include "base/cpu.h"
17 #include "base/files/file_path.h"
18 #include "base/files/scoped_file.h"
19 #include "base/logging.h"
20 #include "base/memory/scoped_ptr.h"
21 #include "base/memory/scoped_vector.h"
22 #include "base/path_service.h"
23 #include "base/pickle.h"
24 #include "base/posix/eintr_wrapper.h"
25 #include "base/posix/global_descriptors.h"
26 #include "base/posix/unix_domain_socket_linux.h"
27 #include "base/process/kill.h"
28 #include "base/process/launch.h"
29 #include "base/strings/string_split.h"
30 #include "base/third_party/dynamic_annotations/dynamic_annotations.h"
31 #include "build/build_config.h"
32 #include "components/nacl/common/nacl_nonsfi_util.h"
33 #include "components/nacl/common/nacl_paths.h"
34 #include "components/nacl/common/nacl_switches.h"
35 #include "components/nacl/loader/nacl_helper_linux.h"
36 #include "content/public/common/content_descriptors.h"
37 #include "content/public/common/content_switches.h"
38 #include "sandbox/linux/suid/client/setuid_sandbox_client.h"
39 #include "sandbox/linux/suid/common/sandbox.h"
40
41 namespace {
42
43 // Note these need to match up with their counterparts in nacl_helper_linux.c
44 // and nacl_helper_bootstrap_linux.c.
45 const char kNaClHelperReservedAtZero[] =
46     "--reserved_at_zero=0xXXXXXXXXXXXXXXXX";
47 const char kNaClHelperRDebug[] = "--r_debug=0xXXXXXXXXXXXXXXXX";
48
49 // This is an environment variable which controls which (if any) other
50 // environment variables are passed through to NaCl processes.  e.g.,
51 // NACL_ENV_PASSTHROUGH="PATH,CWD" would pass both $PATH and $CWD to the child
52 // process.
53 const char kNaClEnvPassthrough[] = "NACL_ENV_PASSTHROUGH";
54 char kNaClEnvPassthroughDelimiter = ',';
55
56 // The following environment variables are always passed through if they exist
57 // in the parent process.
58 const char kNaClExeStderr[] = "NACL_EXE_STDERR";
59 const char kNaClExeStdout[] = "NACL_EXE_STDOUT";
60 const char kNaClVerbosity[] = "NACLVERBOSITY";
61
62 #if defined(ARCH_CPU_X86)
63 bool NonZeroSegmentBaseIsSlow() {
64   base::CPU cpuid;
65   // Using a non-zero segment base is known to be very slow on Intel
66   // Atom CPUs.  See "Segmentation-based Memory Protection Mechanism
67   // on Intel Atom Microarchitecture: Coding Optimizations" (Leonardo
68   // Potenza, Intel).
69   //
70   // The following list of CPU model numbers is taken from:
71   // "Intel 64 and IA-32 Architectures Software Developer's Manual"
72   // (http://download.intel.com/products/processor/manual/325462.pdf),
73   // "Table 35-1. CPUID Signature Values of DisplayFamily_DisplayModel"
74   // (Volume 3C, 35-1), which contains:
75   //   "06_36H - Intel Atom S Processor Family
76   //    06_1CH, 06_26H, 06_27H, 06_35, 06_36 - Intel Atom Processor Family"
77   if (cpuid.family() == 6) {
78     switch (cpuid.model()) {
79       case 0x1c:
80       case 0x26:
81       case 0x27:
82       case 0x35:
83       case 0x36:
84         return true;
85     }
86   }
87   return false;
88 }
89 #endif
90
91 // Send an IPC request on |ipc_channel|. The request is contained in
92 // |request_pickle| and can have file descriptors attached in |attached_fds|.
93 // |reply_data_buffer| must be allocated by the caller and will contain the
94 // reply. The size of the reply will be written to |reply_size|.
95 // This code assumes that only one thread can write to |ipc_channel| to make
96 // requests.
97 bool SendIPCRequestAndReadReply(int ipc_channel,
98                                 const std::vector<int>& attached_fds,
99                                 const Pickle& request_pickle,
100                                 char* reply_data_buffer,
101                                 size_t reply_data_buffer_size,
102                                 ssize_t* reply_size) {
103   DCHECK_LE(static_cast<size_t>(kNaClMaxIPCMessageLength),
104             reply_data_buffer_size);
105   DCHECK(reply_size);
106
107   if (!UnixDomainSocket::SendMsg(ipc_channel, request_pickle.data(),
108                                  request_pickle.size(), attached_fds)) {
109     LOG(ERROR) << "SendIPCRequestAndReadReply: SendMsg failed";
110     return false;
111   }
112
113   // Then read the remote reply.
114   ScopedVector<base::ScopedFD> received_fds;
115   const ssize_t msg_len =
116       UnixDomainSocket::RecvMsg(ipc_channel, reply_data_buffer,
117                                 reply_data_buffer_size, &received_fds);
118   if (msg_len <= 0) {
119     LOG(ERROR) << "SendIPCRequestAndReadReply: RecvMsg failed";
120     return false;
121   }
122   *reply_size = msg_len;
123   return true;
124 }
125
126 }  // namespace.
127
128 namespace nacl {
129
130 void AddNaClZygoteForkDelegates(
131     ScopedVector<content::ZygoteForkDelegate>* delegates) {
132   delegates->push_back(new NaClForkDelegate(false /* nonsfi_mode */));
133   delegates->push_back(new NaClForkDelegate(true /* nonsfi_mode */));
134 }
135
136 NaClForkDelegate::NaClForkDelegate(bool nonsfi_mode)
137     : nonsfi_mode_(nonsfi_mode), status_(kNaClHelperUnused), fd_(-1) {
138 }
139
140 void NaClForkDelegate::Init(const int sandboxdesc,
141                             const bool enable_layer1_sandbox) {
142   VLOG(1) << "NaClForkDelegate::Init()";
143
144   // Only launch the non-SFI helper process if non-SFI mode is enabled.
145   if (nonsfi_mode_ && !IsNonSFIModeEnabled()) {
146     return;
147   }
148
149   scoped_ptr<sandbox::SetuidSandboxClient> setuid_sandbox_client(
150       sandbox::SetuidSandboxClient::Create());
151
152   // For communications between the NaCl loader process and
153   // the SUID sandbox.
154   int nacl_sandbox_descriptor =
155       base::GlobalDescriptors::kBaseDescriptor + kSandboxIPCChannel;
156   // Confirm a hard-wired assumption.
157   DCHECK_EQ(sandboxdesc, nacl_sandbox_descriptor);
158
159   int fds[2];
160   PCHECK(0 == socketpair(PF_UNIX, SOCK_SEQPACKET, 0, fds));
161   base::FileHandleMappingVector fds_to_map;
162   fds_to_map.push_back(std::make_pair(fds[1], kNaClZygoteDescriptor));
163   fds_to_map.push_back(std::make_pair(sandboxdesc, nacl_sandbox_descriptor));
164
165   bool use_nacl_bootstrap = false;
166   // For non-SFI mode, we do not use fixed address space.
167   if (!nonsfi_mode_) {
168     // Using nacl_helper_bootstrap is not necessary on x86-64 because
169     // NaCl's x86-64 sandbox is not zero-address-based.  Starting
170     // nacl_helper through nacl_helper_bootstrap works on x86-64, but it
171     // leaves nacl_helper_bootstrap mapped at a fixed address at the
172     // bottom of the address space, which is undesirable because it
173     // effectively defeats ASLR.
174 #if defined(ARCH_CPU_X86_64)
175     use_nacl_bootstrap = false;
176 #elif defined(ARCH_CPU_X86)
177     // Performance vs. security trade-off: We prefer using a
178     // non-zero-address-based sandbox on x86-32 because it provides some
179     // ASLR and so is more secure.  However, on Atom CPUs, using a
180     // non-zero segment base is very slow, so we use a zero-based
181     // sandbox on those.
182     use_nacl_bootstrap = NonZeroSegmentBaseIsSlow();
183 #else
184     use_nacl_bootstrap = true;
185 #endif
186   }
187
188   status_ = kNaClHelperUnused;
189   base::FilePath helper_exe;
190   base::FilePath helper_bootstrap_exe;
191   if (!PathService::Get(
192           nonsfi_mode_ ? nacl::FILE_NACL_HELPER_NONSFI : nacl::FILE_NACL_HELPER,
193           &helper_exe)) {
194     status_ = kNaClHelperMissing;
195   } else if (use_nacl_bootstrap &&
196              !PathService::Get(nacl::FILE_NACL_HELPER_BOOTSTRAP,
197                                &helper_bootstrap_exe)) {
198     status_ = kNaClHelperBootstrapMissing;
199   } else if (RunningOnValgrind()) {
200     status_ = kNaClHelperValgrind;
201   } else {
202     CommandLine::StringVector argv_to_launch;
203     {
204       CommandLine cmd_line(CommandLine::NO_PROGRAM);
205       if (use_nacl_bootstrap)
206         cmd_line.SetProgram(helper_bootstrap_exe);
207       else
208         cmd_line.SetProgram(helper_exe);
209
210       // Append any switches that need to be forwarded to the NaCl helper.
211       static const char* kForwardSwitches[] = {
212         switches::kDisableSeccompFilterSandbox,
213         switches::kEnableNaClDebug,
214         switches::kNaClDangerousNoSandboxNonSfi,
215         switches::kNoSandbox,
216       };
217       const CommandLine& current_cmd_line = *CommandLine::ForCurrentProcess();
218       cmd_line.CopySwitchesFrom(current_cmd_line, kForwardSwitches,
219                                 arraysize(kForwardSwitches));
220
221       // The command line needs to be tightly controlled to use
222       // |helper_bootstrap_exe|. So from now on, argv_to_launch should be
223       // modified directly.
224       argv_to_launch = cmd_line.argv();
225     }
226     if (use_nacl_bootstrap) {
227       // Arguments to the bootstrap helper which need to be at the start
228       // of the command line, right after the helper's path.
229       CommandLine::StringVector bootstrap_prepend;
230       bootstrap_prepend.push_back(helper_exe.value());
231       bootstrap_prepend.push_back(kNaClHelperReservedAtZero);
232       bootstrap_prepend.push_back(kNaClHelperRDebug);
233       argv_to_launch.insert(argv_to_launch.begin() + 1,
234                             bootstrap_prepend.begin(),
235                             bootstrap_prepend.end());
236     }
237
238     base::LaunchOptions options;
239
240     base::ScopedFD dummy_fd;
241     if (enable_layer1_sandbox) {
242       // NaCl needs to keep tight control of the cmd_line, so prepend the
243       // setuid sandbox wrapper manually.
244       base::FilePath sandbox_path =
245           setuid_sandbox_client->GetSandboxBinaryPath();
246       argv_to_launch.insert(argv_to_launch.begin(), sandbox_path.value());
247       setuid_sandbox_client->SetupLaunchOptions(
248           &options, &fds_to_map, &dummy_fd);
249       setuid_sandbox_client->SetupLaunchEnvironment();
250     }
251
252     options.fds_to_remap = &fds_to_map;
253
254     // The NaCl processes spawned may need to exceed the ambient soft limit
255     // on RLIMIT_AS to allocate the untrusted address space and its guard
256     // regions.  The nacl_helper itself cannot just raise its own limit,
257     // because the existing limit may prevent the initial exec of
258     // nacl_helper_bootstrap from succeeding, with its large address space
259     // reservation.
260     std::vector<int> max_these_limits;
261     max_these_limits.push_back(RLIMIT_AS);
262     options.maximize_rlimits = &max_these_limits;
263
264     // To avoid information leaks in Non-SFI mode, clear the environment for
265     // the NaCl Helper process.
266     options.clear_environ = true;
267     AddPassthroughEnvToOptions(&options);
268
269     if (!base::LaunchProcess(argv_to_launch, options, NULL))
270       status_ = kNaClHelperLaunchFailed;
271     // parent and error cases are handled below
272
273     if (enable_layer1_sandbox) {
274       // Sanity check that dummy_fd was kept alive for LaunchProcess.
275       DCHECK(dummy_fd.is_valid());
276     }
277   }
278   if (IGNORE_EINTR(close(fds[1])) != 0)
279     LOG(ERROR) << "close(fds[1]) failed";
280   if (status_ == kNaClHelperUnused) {
281     const ssize_t kExpectedLength = strlen(kNaClHelperStartupAck);
282     char buf[kExpectedLength];
283
284     // Wait for ack from nacl_helper, indicating it is ready to help
285     const ssize_t nread = HANDLE_EINTR(read(fds[0], buf, sizeof(buf)));
286     if (nread == kExpectedLength &&
287         memcmp(buf, kNaClHelperStartupAck, nread) == 0) {
288       // all is well
289       status_ = kNaClHelperSuccess;
290       fd_ = fds[0];
291       return;
292     }
293
294     status_ = kNaClHelperAckFailed;
295     LOG(ERROR) << "Bad NaCl helper startup ack (" << nread << " bytes)";
296   }
297   // TODO(bradchen): Make this LOG(ERROR) when the NaCl helper
298   // becomes the default.
299   fd_ = -1;
300   if (IGNORE_EINTR(close(fds[0])) != 0)
301     LOG(ERROR) << "close(fds[0]) failed";
302 }
303
304 void NaClForkDelegate::InitialUMA(std::string* uma_name,
305                                   int* uma_sample,
306                                   int* uma_boundary_value) {
307   *uma_name = nonsfi_mode_ ? "NaCl.Client.HelperNonSFI.InitState"
308                            : "NaCl.Client.Helper.InitState";
309   *uma_sample = status_;
310   *uma_boundary_value = kNaClHelperStatusBoundary;
311 }
312
313 NaClForkDelegate::~NaClForkDelegate() {
314   // side effect of close: delegate process will terminate
315   if (status_ == kNaClHelperSuccess) {
316     if (IGNORE_EINTR(close(fd_)) != 0)
317       LOG(ERROR) << "close(fd_) failed";
318   }
319 }
320
321 bool NaClForkDelegate::CanHelp(const std::string& process_type,
322                                std::string* uma_name,
323                                int* uma_sample,
324                                int* uma_boundary_value) {
325   // We can only help with a specific process type depending on nonsfi_mode_.
326   const char* helpable_process_type = nonsfi_mode_
327                                           ? switches::kNaClLoaderNonSfiProcess
328                                           : switches::kNaClLoaderProcess;
329   if (process_type != helpable_process_type)
330     return false;
331   *uma_name = nonsfi_mode_ ? "NaCl.Client.HelperNonSFI.StateOnFork"
332                            : "NaCl.Client.Helper.StateOnFork";
333   *uma_sample = status_;
334   *uma_boundary_value = kNaClHelperStatusBoundary;
335   return true;
336 }
337
338 pid_t NaClForkDelegate::Fork(const std::string& process_type,
339                              const std::vector<int>& fds,
340                              const std::string& channel_id) {
341   VLOG(1) << "NaClForkDelegate::Fork";
342
343   DCHECK(fds.size() == kNumPassedFDs);
344
345   if (status_ != kNaClHelperSuccess) {
346     LOG(ERROR) << "Cannot launch NaCl process: nacl_helper failed to start";
347     return -1;
348   }
349
350   // First, send a remote fork request.
351   Pickle write_pickle;
352   write_pickle.WriteInt(nacl::kNaClForkRequest);
353   // TODO(hamaji): When we split the helper binary for non-SFI mode
354   // from nacl_helper, stop sending this information.
355   write_pickle.WriteBool(nonsfi_mode_);
356   write_pickle.WriteString(channel_id);
357
358   char reply_buf[kNaClMaxIPCMessageLength];
359   ssize_t reply_size = 0;
360   bool got_reply =
361       SendIPCRequestAndReadReply(fd_, fds, write_pickle,
362                                  reply_buf, sizeof(reply_buf), &reply_size);
363   if (!got_reply) {
364     LOG(ERROR) << "Could not perform remote fork.";
365     return -1;
366   }
367
368   // Now see if the other end managed to fork.
369   Pickle reply_pickle(reply_buf, reply_size);
370   PickleIterator iter(reply_pickle);
371   pid_t nacl_child;
372   if (!iter.ReadInt(&nacl_child)) {
373     LOG(ERROR) << "NaClForkDelegate::Fork: pickle failed";
374     return -1;
375   }
376   VLOG(1) << "nacl_child is " << nacl_child;
377   return nacl_child;
378 }
379
380 bool NaClForkDelegate::GetTerminationStatus(pid_t pid, bool known_dead,
381                                             base::TerminationStatus* status,
382                                             int* exit_code) {
383   VLOG(1) << "NaClForkDelegate::GetTerminationStatus";
384   DCHECK(status);
385   DCHECK(exit_code);
386
387   Pickle write_pickle;
388   write_pickle.WriteInt(nacl::kNaClGetTerminationStatusRequest);
389   write_pickle.WriteInt(pid);
390   write_pickle.WriteBool(known_dead);
391
392   const std::vector<int> empty_fds;
393   char reply_buf[kNaClMaxIPCMessageLength];
394   ssize_t reply_size = 0;
395   bool got_reply =
396       SendIPCRequestAndReadReply(fd_, empty_fds, write_pickle,
397                                  reply_buf, sizeof(reply_buf), &reply_size);
398   if (!got_reply) {
399     LOG(ERROR) << "Could not perform remote GetTerminationStatus.";
400     return false;
401   }
402
403   Pickle reply_pickle(reply_buf, reply_size);
404   PickleIterator iter(reply_pickle);
405   int termination_status;
406   if (!iter.ReadInt(&termination_status) ||
407       termination_status < 0 ||
408       termination_status >= base::TERMINATION_STATUS_MAX_ENUM) {
409     LOG(ERROR) << "GetTerminationStatus: pickle failed";
410     return false;
411   }
412
413   int remote_exit_code;
414   if (!iter.ReadInt(&remote_exit_code)) {
415     LOG(ERROR) << "GetTerminationStatus: pickle failed";
416     return false;
417   }
418
419   *status = static_cast<base::TerminationStatus>(termination_status);
420   *exit_code = remote_exit_code;
421   return true;
422 }
423
424 // static
425 void NaClForkDelegate::AddPassthroughEnvToOptions(
426     base::LaunchOptions* options) {
427   scoped_ptr<base::Environment> env(base::Environment::Create());
428   std::string pass_through_string;
429   std::vector<std::string> pass_through_vars;
430   if (env->GetVar(kNaClEnvPassthrough, &pass_through_string)) {
431     base::SplitString(
432         pass_through_string, kNaClEnvPassthroughDelimiter, &pass_through_vars);
433   }
434   pass_through_vars.push_back(kNaClExeStderr);
435   pass_through_vars.push_back(kNaClExeStdout);
436   pass_through_vars.push_back(kNaClVerbosity);
437   pass_through_vars.push_back(sandbox::kSandboxEnvironmentApiRequest);
438   for (size_t i = 0; i < pass_through_vars.size(); ++i) {
439     std::string temp;
440     if (env->GetVar(pass_through_vars[i].c_str(), &temp))
441       options->environ[pass_through_vars[i]] = temp;
442   }
443 }
444
445 }  // namespace nacl