Upstream version 5.34.92.0
[platform/framework/web/crosswalk.git] / src / components / nacl / loader / nacl_listener.cc
1 // Copyright 2013 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/loader/nacl_listener.h"
6
7 #include <errno.h>
8 #include <stdlib.h>
9
10 #if defined(OS_POSIX)
11 #include <unistd.h>
12 #endif
13
14 #include "base/command_line.h"
15 #include "base/logging.h"
16 #include "base/memory/scoped_ptr.h"
17 #include "base/message_loop/message_loop.h"
18 #include "base/rand_util.h"
19 #include "components/nacl/common/nacl_messages.h"
20 #include "components/nacl/loader/nacl_ipc_adapter.h"
21 #include "components/nacl/loader/nacl_validation_db.h"
22 #include "components/nacl/loader/nacl_validation_query.h"
23 #include "ipc/ipc_channel_handle.h"
24 #include "ipc/ipc_switches.h"
25 #include "ipc/ipc_sync_channel.h"
26 #include "ipc/ipc_sync_message_filter.h"
27 #include "native_client/src/trusted/service_runtime/sel_main_chrome.h"
28 #include "native_client/src/trusted/validator/nacl_file_info.h"
29
30 #if defined(OS_POSIX)
31 #include "base/file_descriptor_posix.h"
32 #endif
33
34 #if defined(OS_LINUX)
35 #include "components/nacl/loader/nonsfi/nonsfi_main.h"
36 #include "content/public/common/child_process_sandbox_support_linux.h"
37 #endif
38
39 #if defined(OS_WIN)
40 #include <fcntl.h>
41 #include <io.h>
42
43 #include "content/public/common/sandbox_init.h"
44 #endif
45
46 namespace {
47 #if defined(OS_MACOSX)
48
49 // On Mac OS X, shm_open() works in the sandbox but does not give us
50 // an FD that we can map as PROT_EXEC.  Rather than doing an IPC to
51 // get an executable SHM region when CreateMemoryObject() is called,
52 // we preallocate one on startup, since NaCl's sel_ldr only needs one
53 // of them.  This saves a round trip.
54
55 base::subtle::Atomic32 g_shm_fd = -1;
56
57 int CreateMemoryObject(size_t size, int executable) {
58   if (executable && size > 0) {
59     int result_fd = base::subtle::NoBarrier_AtomicExchange(&g_shm_fd, -1);
60     if (result_fd != -1) {
61       // ftruncate() is disallowed by the Mac OS X sandbox and
62       // returns EPERM.  Luckily, we can get the same effect with
63       // lseek() + write().
64       if (lseek(result_fd, size - 1, SEEK_SET) == -1) {
65         LOG(ERROR) << "lseek() failed: " << errno;
66         return -1;
67       }
68       if (write(result_fd, "", 1) != 1) {
69         LOG(ERROR) << "write() failed: " << errno;
70         return -1;
71       }
72       return result_fd;
73     }
74   }
75   // Fall back to NaCl's default implementation.
76   return -1;
77 }
78
79 #elif defined(OS_LINUX)
80
81 int CreateMemoryObject(size_t size, int executable) {
82   return content::MakeSharedMemorySegmentViaIPC(size, executable);
83 }
84
85 #elif defined(OS_WIN)
86
87 NaClListener* g_listener;
88
89 // We wrap the function to convert the bool return value to an int.
90 int BrokerDuplicateHandle(NaClHandle source_handle,
91                           uint32_t process_id,
92                           NaClHandle* target_handle,
93                           uint32_t desired_access,
94                           uint32_t options) {
95   return content::BrokerDuplicateHandle(source_handle, process_id,
96                                         target_handle, desired_access,
97                                         options);
98 }
99
100 int AttachDebugExceptionHandler(const void* info, size_t info_size) {
101   std::string info_string(reinterpret_cast<const char*>(info), info_size);
102   bool result = false;
103   if (!g_listener->Send(new NaClProcessMsg_AttachDebugExceptionHandler(
104            info_string, &result)))
105     return false;
106   return result;
107 }
108
109 #endif
110
111 }  // namespace
112
113 class BrowserValidationDBProxy : public NaClValidationDB {
114  public:
115   explicit BrowserValidationDBProxy(NaClListener* listener)
116       : listener_(listener) {
117   }
118
119   virtual bool QueryKnownToValidate(const std::string& signature) OVERRIDE {
120     // Initialize to false so that if the Send fails to write to the return
121     // value we're safe.  For example if the message is (for some reason)
122     // dispatched as an async message the return parameter will not be written.
123     bool result = false;
124     if (!listener_->Send(new NaClProcessMsg_QueryKnownToValidate(signature,
125                                                                  &result))) {
126       LOG(ERROR) << "Failed to query NaCl validation cache.";
127       result = false;
128     }
129     return result;
130   }
131
132   virtual void SetKnownToValidate(const std::string& signature) OVERRIDE {
133     // Caching is optional: NaCl will still work correctly if the IPC fails.
134     if (!listener_->Send(new NaClProcessMsg_SetKnownToValidate(signature))) {
135       LOG(ERROR) << "Failed to update NaCl validation cache.";
136     }
137   }
138
139   virtual bool ResolveFileToken(struct NaClFileToken* file_token,
140                                 int32* fd, std::string* path) OVERRIDE {
141     *fd = -1;
142     *path = "";
143     if (file_token->lo == 0 && file_token->hi == 0) {
144       return false;
145     }
146     IPC::PlatformFileForTransit ipc_fd = IPC::InvalidPlatformFileForTransit();
147     base::FilePath ipc_path;
148     if (!listener_->Send(new NaClProcessMsg_ResolveFileToken(file_token->lo,
149                                                              file_token->hi,
150                                                              &ipc_fd,
151                                                              &ipc_path))) {
152       return false;
153     }
154     if (ipc_fd == IPC::InvalidPlatformFileForTransit()) {
155       return false;
156     }
157     base::PlatformFile handle =
158         IPC::PlatformFileForTransitToPlatformFile(ipc_fd);
159 #if defined(OS_WIN)
160     // On Windows, valid handles are 32 bit unsigned integers so this is safe.
161     *fd = reinterpret_cast<uintptr_t>(handle);
162 #else
163     *fd = handle;
164 #endif
165     // It doesn't matter if the path is invalid UTF8 as long as it's consistent
166     // and unforgeable.
167     *path = ipc_path.AsUTF8Unsafe();
168     return true;
169   }
170
171  private:
172   // The listener never dies, otherwise this might be a dangling reference.
173   NaClListener* listener_;
174 };
175
176
177 NaClListener::NaClListener() : shutdown_event_(true, false),
178                                io_thread_("NaCl_IOThread"),
179 #if defined(OS_LINUX)
180                                prereserved_sandbox_size_(0),
181 #endif
182 #if defined(OS_POSIX)
183                                number_of_cores_(-1),  // unknown/error
184 #endif
185                                main_loop_(NULL) {
186   io_thread_.StartWithOptions(
187       base::Thread::Options(base::MessageLoop::TYPE_IO, 0));
188 #if defined(OS_WIN)
189   DCHECK(g_listener == NULL);
190   g_listener = this;
191 #endif
192 }
193
194 NaClListener::~NaClListener() {
195   NOTREACHED();
196   shutdown_event_.Signal();
197 #if defined(OS_WIN)
198   g_listener = NULL;
199 #endif
200 }
201
202 bool NaClListener::Send(IPC::Message* msg) {
203   DCHECK(main_loop_ != NULL);
204   if (base::MessageLoop::current() == main_loop_) {
205     // This thread owns the channel.
206     return channel_->Send(msg);
207   } else {
208     // This thread does not own the channel.
209     return filter_->Send(msg);
210   }
211 }
212
213 void NaClListener::Listen() {
214   std::string channel_name =
215       CommandLine::ForCurrentProcess()->GetSwitchValueASCII(
216           switches::kProcessChannelID);
217   channel_.reset(new IPC::SyncChannel(
218       this, io_thread_.message_loop_proxy().get(), &shutdown_event_));
219   filter_ = new IPC::SyncMessageFilter(&shutdown_event_);
220   channel_->AddFilter(filter_.get());
221   channel_->Init(channel_name, IPC::Channel::MODE_CLIENT, true);
222   main_loop_ = base::MessageLoop::current();
223   main_loop_->Run();
224 }
225
226 bool NaClListener::OnMessageReceived(const IPC::Message& msg) {
227   bool handled = true;
228   IPC_BEGIN_MESSAGE_MAP(NaClListener, msg)
229       IPC_MESSAGE_HANDLER(NaClProcessMsg_Start, OnStart)
230       IPC_MESSAGE_UNHANDLED(handled = false)
231   IPC_END_MESSAGE_MAP()
232   return handled;
233 }
234
235 void NaClListener::OnStart(const nacl::NaClStartParams& params) {
236   struct NaClChromeMainArgs *args = NaClChromeMainArgsCreate();
237   if (args == NULL) {
238     LOG(ERROR) << "NaClChromeMainArgsCreate() failed";
239     return;
240   }
241
242   if (params.enable_ipc_proxy) {
243     // Create the initial PPAPI IPC channel between the NaCl IRT and the
244     // browser process. The IRT uses this channel to communicate with the
245     // browser and to create additional IPC channels to renderer processes.
246     IPC::ChannelHandle handle =
247         IPC::Channel::GenerateVerifiedChannelID("nacl");
248     scoped_refptr<NaClIPCAdapter> ipc_adapter(
249         new NaClIPCAdapter(handle, io_thread_.message_loop_proxy().get()));
250     ipc_adapter->ConnectChannel();
251
252     // Pass a NaClDesc to the untrusted side. This will hold a ref to the
253     // NaClIPCAdapter.
254     args->initial_ipc_desc = ipc_adapter->MakeNaClDesc();
255 #if defined(OS_POSIX)
256     handle.socket = base::FileDescriptor(
257         ipc_adapter->TakeClientFileDescriptor(), true);
258 #endif
259     if (!Send(new NaClProcessHostMsg_PpapiChannelCreated(handle)))
260       LOG(ERROR) << "Failed to send IPC channel handle to NaClProcessHost.";
261   }
262
263   std::vector<nacl::FileDescriptor> handles = params.handles;
264
265 #if defined(OS_LINUX) || defined(OS_MACOSX)
266   args->urandom_fd = dup(base::GetUrandomFD());
267   if (args->urandom_fd < 0) {
268     LOG(ERROR) << "Failed to dup() the urandom FD";
269     return;
270   }
271   args->number_of_cores = number_of_cores_;
272   args->create_memory_object_func = CreateMemoryObject;
273 # if defined(OS_MACOSX)
274   CHECK(handles.size() >= 1);
275   g_shm_fd = nacl::ToNativeHandle(handles[handles.size() - 1]);
276   handles.pop_back();
277 # endif
278 #endif
279
280   if (params.uses_irt) {
281     CHECK(handles.size() >= 1);
282     NaClHandle irt_handle = nacl::ToNativeHandle(handles[handles.size() - 1]);
283     handles.pop_back();
284
285 #if defined(OS_WIN)
286     args->irt_fd = _open_osfhandle(reinterpret_cast<intptr_t>(irt_handle),
287                                    _O_RDONLY | _O_BINARY);
288     if (args->irt_fd < 0) {
289       LOG(ERROR) << "_open_osfhandle() failed";
290       return;
291     }
292 #else
293     args->irt_fd = irt_handle;
294 #endif
295   } else {
296     // Otherwise, the IRT handle is not even sent.
297     args->irt_fd = -1;
298   }
299
300   if (params.validation_cache_enabled) {
301     // SHA256 block size.
302     CHECK_EQ(params.validation_cache_key.length(), (size_t) 64);
303     // The cache structure is not freed and exists until the NaCl process exits.
304     args->validation_cache = CreateValidationCache(
305         new BrowserValidationDBProxy(this), params.validation_cache_key,
306         params.version);
307   }
308
309   CHECK(handles.size() == 1);
310   args->imc_bootstrap_handle = nacl::ToNativeHandle(handles[0]);
311   args->enable_exception_handling = params.enable_exception_handling;
312   args->enable_debug_stub = params.enable_debug_stub;
313   args->enable_dyncode_syscalls = params.enable_dyncode_syscalls;
314   if (!params.enable_dyncode_syscalls) {
315     // Bound the initial nexe's code segment size under PNaCl to
316     // reduce the chance of a code spraying attack succeeding (see
317     // https://code.google.com/p/nativeclient/issues/detail?id=3572).
318     // We assume that !params.enable_dyncode_syscalls is synonymous
319     // with PNaCl.  We can't apply this arbitrary limit outside of
320     // PNaCl because it might break existing NaCl apps, and this limit
321     // is only useful if the dyncode syscalls are disabled.
322     args->initial_nexe_max_code_bytes = 32 << 20;  // 32 MB
323   }
324 #if defined(OS_LINUX) || defined(OS_MACOSX)
325   args->debug_stub_server_bound_socket_fd = nacl::ToNativeHandle(
326       params.debug_stub_server_bound_socket);
327 #endif
328 #if defined(OS_WIN)
329   args->broker_duplicate_handle_func = BrokerDuplicateHandle;
330   args->attach_debug_exception_handler_func = AttachDebugExceptionHandler;
331 #endif
332 #if defined(OS_LINUX)
333   args->prereserved_sandbox_size = prereserved_sandbox_size_;
334 #endif
335
336 #if defined(OS_LINUX)
337   if (params.enable_nonsfi_mode) {
338     nacl::nonsfi::MainStart(args->imc_bootstrap_handle);
339     NOTREACHED();
340     return;
341   }
342 #endif
343   NaClChromeMainStart(args);
344   NOTREACHED();
345 }