Home | History | Annotate | Download | only in loader
      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 <fcntl.h>
      9 #include <stdlib.h>
     10 #include <string.h>
     11 
     12 #if defined(OS_POSIX)
     13 #include <unistd.h>
     14 #endif
     15 
     16 #include "base/command_line.h"
     17 #include "base/logging.h"
     18 #include "base/memory/scoped_ptr.h"
     19 #include "base/message_loop/message_loop.h"
     20 #include "base/rand_util.h"
     21 #include "components/nacl/common/nacl_messages.h"
     22 #include "components/nacl/common/nacl_renderer_messages.h"
     23 #include "components/nacl/loader/nacl_ipc_adapter.h"
     24 #include "components/nacl/loader/nacl_validation_db.h"
     25 #include "components/nacl/loader/nacl_validation_query.h"
     26 #include "ipc/ipc_channel_handle.h"
     27 #include "ipc/ipc_switches.h"
     28 #include "ipc/ipc_sync_channel.h"
     29 #include "ipc/ipc_sync_message_filter.h"
     30 #include "native_client/src/public/chrome_main.h"
     31 #include "native_client/src/public/nacl_app.h"
     32 #include "native_client/src/public/nacl_file_info.h"
     33 #include "native_client/src/trusted/service_runtime/include/sys/fcntl.h"
     34 
     35 #if defined(OS_POSIX)
     36 #include "base/file_descriptor_posix.h"
     37 #endif
     38 
     39 #if defined(OS_LINUX)
     40 #include "content/public/common/child_process_sandbox_support_linux.h"
     41 #endif
     42 
     43 #if defined(OS_WIN)
     44 #include <fcntl.h>
     45 #include <io.h>
     46 
     47 #include "content/public/common/sandbox_init.h"
     48 #endif
     49 
     50 namespace {
     51 
     52 NaClListener* g_listener;
     53 
     54 void FatalLogHandler(const char* data, size_t bytes) {
     55   // We use uint32_t rather than size_t for the case when the browser and NaCl
     56   // processes are a mix of 32-bit and 64-bit processes.
     57   uint32_t copy_bytes = std::min<uint32_t>(static_cast<uint32_t>(bytes),
     58                                            nacl::kNaClCrashInfoMaxLogSize);
     59 
     60   // We copy the length of the crash data to the start of the shared memory
     61   // segment so we know how much to copy.
     62   memcpy(g_listener->crash_info_shmem_memory(), &copy_bytes, sizeof(uint32_t));
     63 
     64   memcpy((char*)g_listener->crash_info_shmem_memory() + sizeof(uint32_t),
     65          data,
     66          copy_bytes);
     67 }
     68 
     69 #if defined(OS_MACOSX)
     70 
     71 // On Mac OS X, shm_open() works in the sandbox but does not give us
     72 // an FD that we can map as PROT_EXEC.  Rather than doing an IPC to
     73 // get an executable SHM region when CreateMemoryObject() is called,
     74 // we preallocate one on startup, since NaCl's sel_ldr only needs one
     75 // of them.  This saves a round trip.
     76 
     77 base::subtle::Atomic32 g_shm_fd = -1;
     78 
     79 int CreateMemoryObject(size_t size, int executable) {
     80   if (executable && size > 0) {
     81     int result_fd = base::subtle::NoBarrier_AtomicExchange(&g_shm_fd, -1);
     82     if (result_fd != -1) {
     83       // ftruncate() is disallowed by the Mac OS X sandbox and
     84       // returns EPERM.  Luckily, we can get the same effect with
     85       // lseek() + write().
     86       if (lseek(result_fd, size - 1, SEEK_SET) == -1) {
     87         LOG(ERROR) << "lseek() failed: " << errno;
     88         return -1;
     89       }
     90       if (write(result_fd, "", 1) != 1) {
     91         LOG(ERROR) << "write() failed: " << errno;
     92         return -1;
     93       }
     94       return result_fd;
     95     }
     96   }
     97   // Fall back to NaCl's default implementation.
     98   return -1;
     99 }
    100 
    101 #elif defined(OS_LINUX)
    102 
    103 int CreateMemoryObject(size_t size, int executable) {
    104   return content::MakeSharedMemorySegmentViaIPC(size, executable);
    105 }
    106 
    107 #elif defined(OS_WIN)
    108 // We wrap the function to convert the bool return value to an int.
    109 int BrokerDuplicateHandle(NaClHandle source_handle,
    110                           uint32_t process_id,
    111                           NaClHandle* target_handle,
    112                           uint32_t desired_access,
    113                           uint32_t options) {
    114   return content::BrokerDuplicateHandle(source_handle, process_id,
    115                                         target_handle, desired_access,
    116                                         options);
    117 }
    118 
    119 int AttachDebugExceptionHandler(const void* info, size_t info_size) {
    120   std::string info_string(reinterpret_cast<const char*>(info), info_size);
    121   bool result = false;
    122   if (!g_listener->Send(new NaClProcessMsg_AttachDebugExceptionHandler(
    123            info_string, &result)))
    124     return false;
    125   return result;
    126 }
    127 
    128 void DebugStubPortSelectedHandler(uint16_t port) {
    129   g_listener->Send(new NaClProcessHostMsg_DebugStubPortSelected(port));
    130 }
    131 
    132 #endif
    133 
    134 // Creates the PPAPI IPC channel between the NaCl IRT and the host
    135 // (browser/renderer) process, and starts to listen it on the thread where
    136 // the given message_loop_proxy runs.
    137 // Also, creates and sets the corresponding NaClDesc to the given nap with
    138 // the FD #.
    139 scoped_refptr<NaClIPCAdapter> SetUpIPCAdapter(
    140     IPC::ChannelHandle* handle,
    141     scoped_refptr<base::MessageLoopProxy> message_loop_proxy,
    142     struct NaClApp* nap,
    143     int nacl_fd) {
    144   scoped_refptr<NaClIPCAdapter> ipc_adapter(
    145       new NaClIPCAdapter(*handle, message_loop_proxy.get()));
    146   ipc_adapter->ConnectChannel();
    147 #if defined(OS_POSIX)
    148   handle->socket =
    149       base::FileDescriptor(ipc_adapter->TakeClientFileDescriptor(), true);
    150 #endif
    151 
    152   // Pass a NaClDesc to the untrusted side. This will hold a ref to the
    153   // NaClIPCAdapter.
    154   NaClAppSetDesc(nap, nacl_fd, ipc_adapter->MakeNaClDesc());
    155   return ipc_adapter;
    156 }
    157 
    158 }  // namespace
    159 
    160 class BrowserValidationDBProxy : public NaClValidationDB {
    161  public:
    162   explicit BrowserValidationDBProxy(NaClListener* listener)
    163       : listener_(listener) {
    164   }
    165 
    166   virtual bool QueryKnownToValidate(const std::string& signature) OVERRIDE {
    167     // Initialize to false so that if the Send fails to write to the return
    168     // value we're safe.  For example if the message is (for some reason)
    169     // dispatched as an async message the return parameter will not be written.
    170     bool result = false;
    171     if (!listener_->Send(new NaClProcessMsg_QueryKnownToValidate(signature,
    172                                                                  &result))) {
    173       LOG(ERROR) << "Failed to query NaCl validation cache.";
    174       result = false;
    175     }
    176     return result;
    177   }
    178 
    179   virtual void SetKnownToValidate(const std::string& signature) OVERRIDE {
    180     // Caching is optional: NaCl will still work correctly if the IPC fails.
    181     if (!listener_->Send(new NaClProcessMsg_SetKnownToValidate(signature))) {
    182       LOG(ERROR) << "Failed to update NaCl validation cache.";
    183     }
    184   }
    185 
    186   // This is the "old" code path for resolving file tokens. It's only
    187   // used for resolving the main nexe.
    188   // TODO(teravest): Remove this.
    189   virtual bool ResolveFileToken(struct NaClFileToken* file_token,
    190                                 int32* fd, std::string* path) OVERRIDE {
    191     *fd = -1;
    192     *path = "";
    193     if (!NaClFileTokenIsValid(file_token)) {
    194       return false;
    195     }
    196     IPC::PlatformFileForTransit ipc_fd = IPC::InvalidPlatformFileForTransit();
    197     base::FilePath ipc_path;
    198     if (!listener_->Send(new NaClProcessMsg_ResolveFileToken(file_token->lo,
    199                                                              file_token->hi,
    200                                                              &ipc_fd,
    201                                                              &ipc_path))) {
    202       return false;
    203     }
    204     if (ipc_fd == IPC::InvalidPlatformFileForTransit()) {
    205       return false;
    206     }
    207     base::PlatformFile handle =
    208         IPC::PlatformFileForTransitToPlatformFile(ipc_fd);
    209 #if defined(OS_WIN)
    210     // On Windows, valid handles are 32 bit unsigned integers so this is safe.
    211     *fd = reinterpret_cast<uintptr_t>(handle);
    212 #else
    213     *fd = handle;
    214 #endif
    215     // It doesn't matter if the path is invalid UTF8 as long as it's consistent
    216     // and unforgeable.
    217     *path = ipc_path.AsUTF8Unsafe();
    218     return true;
    219   }
    220 
    221  private:
    222   // The listener never dies, otherwise this might be a dangling reference.
    223   NaClListener* listener_;
    224 };
    225 
    226 
    227 NaClListener::NaClListener() : shutdown_event_(true, false),
    228                                io_thread_("NaCl_IOThread"),
    229 #if defined(OS_LINUX)
    230                                prereserved_sandbox_size_(0),
    231 #endif
    232 #if defined(OS_POSIX)
    233                                number_of_cores_(-1),  // unknown/error
    234 #endif
    235                                main_loop_(NULL) {
    236   io_thread_.StartWithOptions(
    237       base::Thread::Options(base::MessageLoop::TYPE_IO, 0));
    238   DCHECK(g_listener == NULL);
    239   g_listener = this;
    240 }
    241 
    242 NaClListener::~NaClListener() {
    243   NOTREACHED();
    244   shutdown_event_.Signal();
    245   g_listener = NULL;
    246 }
    247 
    248 bool NaClListener::Send(IPC::Message* msg) {
    249   DCHECK(main_loop_ != NULL);
    250   if (base::MessageLoop::current() == main_loop_) {
    251     // This thread owns the channel.
    252     return channel_->Send(msg);
    253   } else {
    254     // This thread does not own the channel.
    255     return filter_->Send(msg);
    256   }
    257 }
    258 
    259 // The NaClProcessMsg_ResolveFileTokenAsyncReply message must be
    260 // processed in a MessageFilter so it can be handled on the IO thread.
    261 // The main thread used by NaClListener is busy in
    262 // NaClChromeMainAppStart(), so it can't be used for servicing messages.
    263 class FileTokenMessageFilter : public IPC::MessageFilter {
    264  public:
    265   virtual bool OnMessageReceived(const IPC::Message& msg) OVERRIDE {
    266     bool handled = true;
    267     IPC_BEGIN_MESSAGE_MAP(FileTokenMessageFilter, msg)
    268       IPC_MESSAGE_HANDLER(NaClProcessMsg_ResolveFileTokenAsyncReply,
    269                           OnResolveFileTokenAsyncReply)
    270       IPC_MESSAGE_UNHANDLED(handled = false)
    271     IPC_END_MESSAGE_MAP()
    272     return handled;
    273   }
    274 
    275   void OnResolveFileTokenAsyncReply(
    276       uint64_t token_lo,
    277       uint64_t token_hi,
    278       IPC::PlatformFileForTransit ipc_fd,
    279       base::FilePath file_path) {
    280     CHECK(g_listener);
    281     g_listener->OnFileTokenResolved(token_lo, token_hi, ipc_fd, file_path);
    282   }
    283  private:
    284   virtual ~FileTokenMessageFilter() { }
    285 };
    286 
    287 void NaClListener::Listen() {
    288   std::string channel_name =
    289       CommandLine::ForCurrentProcess()->GetSwitchValueASCII(
    290           switches::kProcessChannelID);
    291   channel_ = IPC::SyncChannel::Create(
    292       this, io_thread_.message_loop_proxy().get(), &shutdown_event_);
    293   filter_ = new IPC::SyncMessageFilter(&shutdown_event_);
    294   channel_->AddFilter(filter_.get());
    295   channel_->AddFilter(new FileTokenMessageFilter());
    296   channel_->Init(channel_name, IPC::Channel::MODE_CLIENT, true);
    297   main_loop_ = base::MessageLoop::current();
    298   main_loop_->Run();
    299 }
    300 
    301 bool NaClListener::OnMessageReceived(const IPC::Message& msg) {
    302   bool handled = true;
    303   IPC_BEGIN_MESSAGE_MAP(NaClListener, msg)
    304       IPC_MESSAGE_HANDLER(NaClProcessMsg_Start, OnStart)
    305       IPC_MESSAGE_UNHANDLED(handled = false)
    306   IPC_END_MESSAGE_MAP()
    307   return handled;
    308 }
    309 
    310 void NaClListener::OnStart(const nacl::NaClStartParams& params) {
    311 #if defined(OS_LINUX) || defined(OS_MACOSX)
    312   int urandom_fd = dup(base::GetUrandomFD());
    313   if (urandom_fd < 0) {
    314     LOG(ERROR) << "Failed to dup() the urandom FD";
    315     return;
    316   }
    317   NaClChromeMainSetUrandomFd(urandom_fd);
    318 #endif
    319   struct NaClApp* nap = NULL;
    320   NaClChromeMainInit();
    321 
    322   crash_info_shmem_.reset(new base::SharedMemory(params.crash_info_shmem_handle,
    323                                                  false));
    324   CHECK(crash_info_shmem_->Map(nacl::kNaClCrashInfoShmemSize));
    325   NaClSetFatalErrorCallback(&FatalLogHandler);
    326 
    327   nap = NaClAppCreate();
    328   if (nap == NULL) {
    329     LOG(ERROR) << "NaClAppCreate() failed";
    330     return;
    331   }
    332 
    333   IPC::ChannelHandle browser_handle;
    334   IPC::ChannelHandle ppapi_renderer_handle;
    335   IPC::ChannelHandle manifest_service_handle;
    336 
    337   if (params.enable_ipc_proxy) {
    338     browser_handle = IPC::Channel::GenerateVerifiedChannelID("nacl");
    339     ppapi_renderer_handle = IPC::Channel::GenerateVerifiedChannelID("nacl");
    340     manifest_service_handle = IPC::Channel::GenerateVerifiedChannelID("nacl");
    341 
    342     // Create the PPAPI IPC channels between the NaCl IRT and the host
    343     // (browser/renderer) processes. The IRT uses these channels to
    344     // communicate with the host and to initialize the IPC dispatchers.
    345     SetUpIPCAdapter(&browser_handle, io_thread_.message_loop_proxy(),
    346                     nap, NACL_CHROME_DESC_BASE);
    347     SetUpIPCAdapter(&ppapi_renderer_handle, io_thread_.message_loop_proxy(),
    348                     nap, NACL_CHROME_DESC_BASE + 1);
    349 
    350     scoped_refptr<NaClIPCAdapter> manifest_ipc_adapter =
    351         SetUpIPCAdapter(&manifest_service_handle,
    352                         io_thread_.message_loop_proxy(),
    353                         nap,
    354                         NACL_CHROME_DESC_BASE + 2);
    355     manifest_ipc_adapter->set_resolve_file_token_callback(
    356         base::Bind(&NaClListener::ResolveFileToken, base::Unretained(this)));
    357   }
    358 
    359   trusted_listener_ = new NaClTrustedListener(
    360       IPC::Channel::GenerateVerifiedChannelID("nacl"),
    361       io_thread_.message_loop_proxy().get(),
    362       &shutdown_event_);
    363   if (!Send(new NaClProcessHostMsg_PpapiChannelsCreated(
    364           browser_handle,
    365           ppapi_renderer_handle,
    366           trusted_listener_->TakeClientChannelHandle(),
    367           manifest_service_handle)))
    368     LOG(ERROR) << "Failed to send IPC channel handle to NaClProcessHost.";
    369 
    370   std::vector<nacl::FileDescriptor> handles = params.handles;
    371   struct NaClChromeMainArgs* args = NaClChromeMainArgsCreate();
    372   if (args == NULL) {
    373     LOG(ERROR) << "NaClChromeMainArgsCreate() failed";
    374     return;
    375   }
    376 
    377 #if defined(OS_LINUX) || defined(OS_MACOSX)
    378   args->number_of_cores = number_of_cores_;
    379   args->create_memory_object_func = CreateMemoryObject;
    380 # if defined(OS_MACOSX)
    381   CHECK(handles.size() >= 1);
    382   g_shm_fd = nacl::ToNativeHandle(handles[handles.size() - 1]);
    383   handles.pop_back();
    384 # endif
    385 #endif
    386 
    387   if (params.uses_irt) {
    388     CHECK(handles.size() >= 1);
    389     NaClHandle irt_handle = nacl::ToNativeHandle(handles[handles.size() - 1]);
    390     handles.pop_back();
    391 
    392 #if defined(OS_WIN)
    393     args->irt_fd = _open_osfhandle(reinterpret_cast<intptr_t>(irt_handle),
    394                                    _O_RDONLY | _O_BINARY);
    395     if (args->irt_fd < 0) {
    396       LOG(ERROR) << "_open_osfhandle() failed";
    397       return;
    398     }
    399 #else
    400     args->irt_fd = irt_handle;
    401 #endif
    402   } else {
    403     // Otherwise, the IRT handle is not even sent.
    404     args->irt_fd = -1;
    405   }
    406 
    407   if (params.validation_cache_enabled) {
    408     // SHA256 block size.
    409     CHECK_EQ(params.validation_cache_key.length(), (size_t) 64);
    410     // The cache structure is not freed and exists until the NaCl process exits.
    411     args->validation_cache = CreateValidationCache(
    412         new BrowserValidationDBProxy(this), params.validation_cache_key,
    413         params.version);
    414   }
    415 
    416   CHECK(handles.size() == 1);
    417   args->imc_bootstrap_handle = nacl::ToNativeHandle(handles[0]);
    418   args->enable_exception_handling = params.enable_exception_handling;
    419   args->enable_debug_stub = params.enable_debug_stub;
    420   args->enable_dyncode_syscalls = params.enable_dyncode_syscalls;
    421   if (!params.enable_dyncode_syscalls) {
    422     // Bound the initial nexe's code segment size under PNaCl to
    423     // reduce the chance of a code spraying attack succeeding (see
    424     // https://code.google.com/p/nativeclient/issues/detail?id=3572).
    425     // We assume that !params.enable_dyncode_syscalls is synonymous
    426     // with PNaCl.  We can't apply this arbitrary limit outside of
    427     // PNaCl because it might break existing NaCl apps, and this limit
    428     // is only useful if the dyncode syscalls are disabled.
    429     args->initial_nexe_max_code_bytes = 64 << 20;  // 64 MB
    430 
    431     // Indicate that this is a PNaCl module.
    432     // TODO(jvoung): Plumb through something indicating that this is PNaCl
    433     // instead of relying on enable_dyncode_syscalls.
    434     args->pnacl_mode = 1;
    435   }
    436 #if defined(OS_LINUX) || defined(OS_MACOSX)
    437   args->debug_stub_server_bound_socket_fd = nacl::ToNativeHandle(
    438       params.debug_stub_server_bound_socket);
    439 #endif
    440 #if defined(OS_WIN)
    441   args->broker_duplicate_handle_func = BrokerDuplicateHandle;
    442   args->attach_debug_exception_handler_func = AttachDebugExceptionHandler;
    443   args->debug_stub_server_port_selected_handler_func =
    444       DebugStubPortSelectedHandler;
    445 #endif
    446 #if defined(OS_LINUX)
    447   args->prereserved_sandbox_size = prereserved_sandbox_size_;
    448 #endif
    449 
    450   NaClFileInfo nexe_file_info;
    451   base::PlatformFile nexe_file = IPC::PlatformFileForTransitToPlatformFile(
    452       params.nexe_file);
    453 #if defined(OS_WIN)
    454   nexe_file_info.desc =
    455       _open_osfhandle(reinterpret_cast<intptr_t>(nexe_file),
    456                       _O_RDONLY | _O_BINARY);
    457 #elif defined(OS_POSIX)
    458   nexe_file_info.desc = nexe_file;
    459 #else
    460 #error Unsupported target platform.
    461 #endif
    462   nexe_file_info.file_token.lo = params.nexe_token_lo;
    463   nexe_file_info.file_token.hi = params.nexe_token_hi;
    464   args->nexe_desc = NaClDescIoFromFileInfo(nexe_file_info, NACL_ABI_O_RDONLY);
    465 
    466   int exit_status;
    467   if (!NaClChromeMainStart(nap, args, &exit_status))
    468     NaClExit(1);
    469 
    470   // Report the plugin's exit status if the application started successfully.
    471   trusted_listener_->Send(new NaClRendererMsg_ReportExitStatus(exit_status));
    472   NaClExit(exit_status);
    473 }
    474 
    475 void NaClListener::ResolveFileToken(
    476     uint64_t token_lo,
    477     uint64_t token_hi,
    478     base::Callback<void(IPC::PlatformFileForTransit, base::FilePath)> cb) {
    479   if (!Send(new NaClProcessMsg_ResolveFileTokenAsync(token_lo, token_hi))) {
    480     cb.Run(IPC::PlatformFileForTransit(), base::FilePath());
    481     return;
    482   }
    483   resolved_cb_ = cb;
    484 }
    485 
    486 void NaClListener::OnFileTokenResolved(
    487     uint64_t token_lo,
    488     uint64_t token_hi,
    489     IPC::PlatformFileForTransit ipc_fd,
    490     base::FilePath file_path) {
    491   resolved_cb_.Run(ipc_fd, file_path);
    492   resolved_cb_.Reset();
    493 }
    494