| Index: components/nacl/loader/nacl_listener.cc
|
| diff --git a/components/nacl/loader/nacl_listener.cc b/components/nacl/loader/nacl_listener.cc
|
| index 433d9417d2abd291e532156de13b676ba6c57a4c..2f4c9e86702401d89a89eb1f168ca20e916f03ef 100644
|
| --- a/components/nacl/loader/nacl_listener.cc
|
| +++ b/components/nacl/loader/nacl_listener.cc
|
| @@ -4,231 +4,30 @@
|
|
|
| #include "components/nacl/loader/nacl_listener.h"
|
|
|
| -#include <errno.h>
|
| -#include <fcntl.h>
|
| -#include <stdlib.h>
|
| -
|
| -#if defined(OS_POSIX)
|
| -#include <unistd.h>
|
| -#endif
|
| -
|
| #include "base/command_line.h"
|
| #include "base/logging.h"
|
| #include "base/memory/scoped_ptr.h"
|
| #include "base/message_loop/message_loop.h"
|
| -#include "base/rand_util.h"
|
| #include "components/nacl/common/nacl_messages.h"
|
| -#include "components/nacl/loader/nacl_ipc_adapter.h"
|
| -#include "components/nacl/loader/nacl_validation_db.h"
|
| -#include "components/nacl/loader/nacl_validation_query.h"
|
| +#include "components/nacl/loader/listener_delegate.h"
|
| #include "ipc/ipc_channel_handle.h"
|
| #include "ipc/ipc_switches.h"
|
| #include "ipc/ipc_sync_channel.h"
|
| #include "ipc/ipc_sync_message_filter.h"
|
| -#include "native_client/src/public/chrome_main.h"
|
| -#include "native_client/src/public/nacl_app.h"
|
| -#include "native_client/src/public/nacl_file_info.h"
|
| -#include "native_client/src/trusted/service_runtime/include/sys/fcntl.h"
|
| -
|
| -#if defined(OS_POSIX)
|
| -#include "base/file_descriptor_posix.h"
|
| -#endif
|
| -
|
| -#if defined(OS_LINUX)
|
| -#include "components/nacl/loader/nonsfi/irt_random.h"
|
| -#include "components/nacl/loader/nonsfi/nonsfi_main.h"
|
| -#include "content/public/common/child_process_sandbox_support_linux.h"
|
| -#include "ppapi/nacl_irt/plugin_startup.h"
|
| -#endif
|
| -
|
| -#if defined(OS_WIN)
|
| -#include <fcntl.h>
|
| -#include <io.h>
|
| -
|
| -#include "content/public/common/sandbox_init.h"
|
| -#endif
|
| -
|
| -namespace {
|
| -#if defined(OS_MACOSX)
|
| -
|
| -// On Mac OS X, shm_open() works in the sandbox but does not give us
|
| -// an FD that we can map as PROT_EXEC. Rather than doing an IPC to
|
| -// get an executable SHM region when CreateMemoryObject() is called,
|
| -// we preallocate one on startup, since NaCl's sel_ldr only needs one
|
| -// of them. This saves a round trip.
|
| -
|
| -base::subtle::Atomic32 g_shm_fd = -1;
|
| -
|
| -int CreateMemoryObject(size_t size, int executable) {
|
| - if (executable && size > 0) {
|
| - int result_fd = base::subtle::NoBarrier_AtomicExchange(&g_shm_fd, -1);
|
| - if (result_fd != -1) {
|
| - // ftruncate() is disallowed by the Mac OS X sandbox and
|
| - // returns EPERM. Luckily, we can get the same effect with
|
| - // lseek() + write().
|
| - if (lseek(result_fd, size - 1, SEEK_SET) == -1) {
|
| - LOG(ERROR) << "lseek() failed: " << errno;
|
| - return -1;
|
| - }
|
| - if (write(result_fd, "", 1) != 1) {
|
| - LOG(ERROR) << "write() failed: " << errno;
|
| - return -1;
|
| - }
|
| - return result_fd;
|
| - }
|
| - }
|
| - // Fall back to NaCl's default implementation.
|
| - return -1;
|
| -}
|
| -
|
| -#elif defined(OS_LINUX)
|
| -
|
| -int CreateMemoryObject(size_t size, int executable) {
|
| - return content::MakeSharedMemorySegmentViaIPC(size, executable);
|
| -}
|
| -
|
| -#elif defined(OS_WIN)
|
| -
|
| -NaClListener* g_listener;
|
| -
|
| -// We wrap the function to convert the bool return value to an int.
|
| -int BrokerDuplicateHandle(NaClHandle source_handle,
|
| - uint32_t process_id,
|
| - NaClHandle* target_handle,
|
| - uint32_t desired_access,
|
| - uint32_t options) {
|
| - return content::BrokerDuplicateHandle(source_handle, process_id,
|
| - target_handle, desired_access,
|
| - options);
|
| -}
|
| -
|
| -int AttachDebugExceptionHandler(const void* info, size_t info_size) {
|
| - std::string info_string(reinterpret_cast<const char*>(info), info_size);
|
| - bool result = false;
|
| - if (!g_listener->Send(new NaClProcessMsg_AttachDebugExceptionHandler(
|
| - info_string, &result)))
|
| - return false;
|
| - return result;
|
| -}
|
| -
|
| -void DebugStubPortSelectedHandler(uint16_t port) {
|
| - g_listener->Send(new NaClProcessHostMsg_DebugStubPortSelected(port));
|
| -}
|
| -
|
| -#endif
|
| -
|
| -// Creates the PPAPI IPC channel between the NaCl IRT and the host
|
| -// (browser/renderer) process, and starts to listen it on the thread where
|
| -// the given message_loop_proxy runs.
|
| -// Also, creates and sets the corresponding NaClDesc to the given nap with
|
| -// the FD #.
|
| -void SetUpIPCAdapter(IPC::ChannelHandle* handle,
|
| - scoped_refptr<base::MessageLoopProxy> message_loop_proxy,
|
| - struct NaClApp* nap,
|
| - int nacl_fd) {
|
| - scoped_refptr<NaClIPCAdapter> ipc_adapter(
|
| - new NaClIPCAdapter(*handle, message_loop_proxy.get()));
|
| - ipc_adapter->ConnectChannel();
|
| -#if defined(OS_POSIX)
|
| - handle->socket =
|
| - base::FileDescriptor(ipc_adapter->TakeClientFileDescriptor(), true);
|
| -#endif
|
| -
|
| - // Pass a NaClDesc to the untrusted side. This will hold a ref to the
|
| - // NaClIPCAdapter.
|
| - NaClAppSetDesc(nap, nacl_fd, ipc_adapter->MakeNaClDesc());
|
| -}
|
|
|
| -} // namespace
|
|
|
| -class BrowserValidationDBProxy : public NaClValidationDB {
|
| - public:
|
| - explicit BrowserValidationDBProxy(NaClListener* listener)
|
| - : listener_(listener) {
|
| - }
|
| -
|
| - virtual bool QueryKnownToValidate(const std::string& signature) OVERRIDE {
|
| - // Initialize to false so that if the Send fails to write to the return
|
| - // value we're safe. For example if the message is (for some reason)
|
| - // dispatched as an async message the return parameter will not be written.
|
| - bool result = false;
|
| - if (!listener_->Send(new NaClProcessMsg_QueryKnownToValidate(signature,
|
| - &result))) {
|
| - LOG(ERROR) << "Failed to query NaCl validation cache.";
|
| - result = false;
|
| - }
|
| - return result;
|
| - }
|
| -
|
| - virtual void SetKnownToValidate(const std::string& signature) OVERRIDE {
|
| - // Caching is optional: NaCl will still work correctly if the IPC fails.
|
| - if (!listener_->Send(new NaClProcessMsg_SetKnownToValidate(signature))) {
|
| - LOG(ERROR) << "Failed to update NaCl validation cache.";
|
| - }
|
| - }
|
| -
|
| - virtual bool ResolveFileToken(struct NaClFileToken* file_token,
|
| - int32* fd, std::string* path) OVERRIDE {
|
| - *fd = -1;
|
| - *path = "";
|
| - if (!NaClFileTokenIsValid(file_token)) {
|
| - return false;
|
| - }
|
| - IPC::PlatformFileForTransit ipc_fd = IPC::InvalidPlatformFileForTransit();
|
| - base::FilePath ipc_path;
|
| - if (!listener_->Send(new NaClProcessMsg_ResolveFileToken(file_token->lo,
|
| - file_token->hi,
|
| - &ipc_fd,
|
| - &ipc_path))) {
|
| - return false;
|
| - }
|
| - if (ipc_fd == IPC::InvalidPlatformFileForTransit()) {
|
| - return false;
|
| - }
|
| - base::PlatformFile handle =
|
| - IPC::PlatformFileForTransitToPlatformFile(ipc_fd);
|
| -#if defined(OS_WIN)
|
| - // On Windows, valid handles are 32 bit unsigned integers so this is safe.
|
| - *fd = reinterpret_cast<uintptr_t>(handle);
|
| -#else
|
| - *fd = handle;
|
| -#endif
|
| - // It doesn't matter if the path is invalid UTF8 as long as it's consistent
|
| - // and unforgeable.
|
| - *path = ipc_path.AsUTF8Unsafe();
|
| - return true;
|
| - }
|
| -
|
| - private:
|
| - // The listener never dies, otherwise this might be a dangling reference.
|
| - NaClListener* listener_;
|
| -};
|
| -
|
| -
|
| -NaClListener::NaClListener() : shutdown_event_(true, false),
|
| - io_thread_("NaCl_IOThread"),
|
| - uses_nonsfi_mode_(false),
|
| -#if defined(OS_LINUX)
|
| - prereserved_sandbox_size_(0),
|
| -#endif
|
| -#if defined(OS_POSIX)
|
| - number_of_cores_(-1), // unknown/error
|
| -#endif
|
| - main_loop_(NULL) {
|
| +NaClListener::NaClListener(scoped_ptr<nacl::ListenerDelegate> delegate)
|
| + : delegate_(delegate.Pass()),
|
| + shutdown_event_(true, false),
|
| + io_thread_("NaCl_IOThread"),
|
| + main_loop_(NULL) {
|
| io_thread_.StartWithOptions(
|
| base::Thread::Options(base::MessageLoop::TYPE_IO, 0));
|
| -#if defined(OS_WIN)
|
| - DCHECK(g_listener == NULL);
|
| - g_listener = this;
|
| -#endif
|
| }
|
|
|
| NaClListener::~NaClListener() {
|
| NOTREACHED();
|
| shutdown_event_.Signal();
|
| -#if defined(OS_WIN)
|
| - g_listener = NULL;
|
| -#endif
|
| }
|
|
|
| bool NaClListener::Send(IPC::Message* msg) {
|
| @@ -265,249 +64,21 @@ bool NaClListener::OnMessageReceived(const IPC::Message& msg) {
|
| }
|
|
|
| void NaClListener::OnStart(const nacl::NaClStartParams& params) {
|
| - if (uses_nonsfi_mode_) {
|
| - StartNonSfi(params);
|
| - return;
|
| - }
|
| -
|
| -#if defined(OS_LINUX) || defined(OS_MACOSX)
|
| - int urandom_fd = dup(base::GetUrandomFD());
|
| - if (urandom_fd < 0) {
|
| - LOG(ERROR) << "Failed to dup() the urandom FD";
|
| - return;
|
| - }
|
| - NaClChromeMainSetUrandomFd(urandom_fd);
|
| -#endif
|
| -
|
| - struct NaClApp* nap = NULL;
|
| - NaClChromeMainInit();
|
| - nap = NaClAppCreate();
|
| - if (nap == NULL) {
|
| - LOG(ERROR) << "NaClAppCreate() failed";
|
| - return;
|
| - }
|
| -
|
| - IPC::ChannelHandle browser_handle;
|
| - IPC::ChannelHandle ppapi_renderer_handle;
|
| -
|
| - if (params.enable_ipc_proxy) {
|
| - browser_handle = IPC::Channel::GenerateVerifiedChannelID("nacl");
|
| - ppapi_renderer_handle = IPC::Channel::GenerateVerifiedChannelID("nacl");
|
| -
|
| - // Create the PPAPI IPC channels between the NaCl IRT and the host
|
| - // (browser/renderer) processes. The IRT uses these channels to
|
| - // communicate with the host and to initialize the IPC dispatchers.
|
| - SetUpIPCAdapter(&browser_handle, io_thread_.message_loop_proxy(),
|
| - nap, NACL_CHROME_DESC_BASE);
|
| - SetUpIPCAdapter(&ppapi_renderer_handle, io_thread_.message_loop_proxy(),
|
| - nap, NACL_CHROME_DESC_BASE + 1);
|
| - }
|
| -
|
| - IPC::ChannelHandle trusted_renderer_handle = CreateTrustedListener(
|
| - io_thread_.message_loop_proxy(), &shutdown_event_);
|
| - if (!Send(new NaClProcessHostMsg_PpapiChannelsCreated(
|
| - browser_handle, ppapi_renderer_handle,
|
| - trusted_renderer_handle, IPC::ChannelHandle())))
|
| - LOG(ERROR) << "Failed to send IPC channel handle to NaClProcessHost.";
|
| -
|
| - std::vector<nacl::FileDescriptor> handles = params.handles;
|
| - struct NaClChromeMainArgs* args = NaClChromeMainArgsCreate();
|
| - if (args == NULL) {
|
| - LOG(ERROR) << "NaClChromeMainArgsCreate() failed";
|
| - return;
|
| - }
|
| -
|
| -#if defined(OS_LINUX) || defined(OS_MACOSX)
|
| - args->number_of_cores = number_of_cores_;
|
| - args->create_memory_object_func = CreateMemoryObject;
|
| -# if defined(OS_MACOSX)
|
| - CHECK(handles.size() >= 1);
|
| - g_shm_fd = nacl::ToNativeHandle(handles[handles.size() - 1]);
|
| - handles.pop_back();
|
| -# endif
|
| -#endif
|
| -
|
| - if (params.uses_irt) {
|
| - CHECK(handles.size() >= 1);
|
| - NaClHandle irt_handle = nacl::ToNativeHandle(handles[handles.size() - 1]);
|
| - handles.pop_back();
|
| -
|
| -#if defined(OS_WIN)
|
| - args->irt_fd = _open_osfhandle(reinterpret_cast<intptr_t>(irt_handle),
|
| - _O_RDONLY | _O_BINARY);
|
| - if (args->irt_fd < 0) {
|
| - LOG(ERROR) << "_open_osfhandle() failed";
|
| - return;
|
| - }
|
| -#else
|
| - args->irt_fd = irt_handle;
|
| -#endif
|
| - } else {
|
| - // Otherwise, the IRT handle is not even sent.
|
| - args->irt_fd = -1;
|
| - }
|
| -
|
| - if (params.validation_cache_enabled) {
|
| - // SHA256 block size.
|
| - CHECK_EQ(params.validation_cache_key.length(), (size_t) 64);
|
| - // The cache structure is not freed and exists until the NaCl process exits.
|
| - args->validation_cache = CreateValidationCache(
|
| - new BrowserValidationDBProxy(this), params.validation_cache_key,
|
| - params.version);
|
| - }
|
| -
|
| - CHECK(handles.size() == 1);
|
| - args->imc_bootstrap_handle = nacl::ToNativeHandle(handles[0]);
|
| - args->enable_exception_handling = params.enable_exception_handling;
|
| - args->enable_debug_stub = params.enable_debug_stub;
|
| - args->enable_dyncode_syscalls = params.enable_dyncode_syscalls;
|
| - if (!params.enable_dyncode_syscalls) {
|
| - // Bound the initial nexe's code segment size under PNaCl to
|
| - // reduce the chance of a code spraying attack succeeding (see
|
| - // https://code.google.com/p/nativeclient/issues/detail?id=3572).
|
| - // We assume that !params.enable_dyncode_syscalls is synonymous
|
| - // with PNaCl. We can't apply this arbitrary limit outside of
|
| - // PNaCl because it might break existing NaCl apps, and this limit
|
| - // is only useful if the dyncode syscalls are disabled.
|
| - args->initial_nexe_max_code_bytes = 64 << 20; // 64 MB
|
| -
|
| - // Indicate that this is a PNaCl module.
|
| - // TODO(jvoung): Plumb through something indicating that this is PNaCl
|
| - // instead of relying on enable_dyncode_syscalls.
|
| - args->pnacl_mode = 1;
|
| - }
|
| -#if defined(OS_LINUX) || defined(OS_MACOSX)
|
| - args->debug_stub_server_bound_socket_fd = nacl::ToNativeHandle(
|
| - params.debug_stub_server_bound_socket);
|
| -#endif
|
| -#if defined(OS_WIN)
|
| - args->broker_duplicate_handle_func = BrokerDuplicateHandle;
|
| - args->attach_debug_exception_handler_func = AttachDebugExceptionHandler;
|
| - args->debug_stub_server_port_selected_handler_func =
|
| - DebugStubPortSelectedHandler;
|
| -#endif
|
| -#if defined(OS_LINUX)
|
| - args->prereserved_sandbox_size = prereserved_sandbox_size_;
|
| -#endif
|
| -
|
| - NaClFileInfo nexe_file_info;
|
| - base::PlatformFile nexe_file = IPC::PlatformFileForTransitToPlatformFile(
|
| - params.nexe_file);
|
| -#if defined(OS_WIN)
|
| - nexe_file_info.desc =
|
| - _open_osfhandle(reinterpret_cast<intptr_t>(nexe_file),
|
| - _O_RDONLY | _O_BINARY);
|
| -#elif defined(OS_POSIX)
|
| - nexe_file_info.desc = nexe_file;
|
| -#else
|
| -#error Unsupported target platform.
|
| -#endif
|
| - nexe_file_info.file_token.lo = params.nexe_token_lo;
|
| - nexe_file_info.file_token.hi = params.nexe_token_hi;
|
| - args->nexe_desc = NaClDescIoFromFileInfo(nexe_file_info, NACL_ABI_O_RDONLY);
|
| -
|
| - NaClChromeMainStartApp(nap, args);
|
| -}
|
| -
|
| -void NaClListener::StartNonSfi(const nacl::NaClStartParams& params) {
|
| -#if !defined(OS_LINUX)
|
| - NOTREACHED() << "Non-SFI NaCl is only supported on Linux";
|
| -#else
|
| - // Random number source initialization.
|
| - nacl::nonsfi::SetUrandomFd(base::GetUrandomFD());
|
| -
|
| - IPC::ChannelHandle browser_handle;
|
| - IPC::ChannelHandle ppapi_renderer_handle;
|
| - IPC::ChannelHandle manifest_service_handle;
|
| -
|
| - if (params.enable_ipc_proxy) {
|
| - browser_handle = IPC::Channel::GenerateVerifiedChannelID("nacl");
|
| - ppapi_renderer_handle = IPC::Channel::GenerateVerifiedChannelID("nacl");
|
| - manifest_service_handle =
|
| - IPC::Channel::GenerateVerifiedChannelID("nacl");
|
| -
|
| - // In non-SFI mode, we neither intercept nor rewrite the message using
|
| - // NaClIPCAdapter, and the channels are connected between the plugin and
|
| - // the hosts directly. So, the IPC::Channel instances will be created in
|
| - // the plugin side, because the IPC::Listener needs to live on the
|
| - // plugin's main thread. However, on initialization (i.e. before loading
|
| - // the plugin binary), the FD needs to be passed to the hosts. So, here
|
| - // we create raw FD pairs, and pass the client side FDs to the hosts,
|
| - // and the server side FDs to the plugin.
|
| - int browser_server_ppapi_fd;
|
| - int browser_client_ppapi_fd;
|
| - int renderer_server_ppapi_fd;
|
| - int renderer_client_ppapi_fd;
|
| - int manifest_service_server_fd;
|
| - int manifest_service_client_fd;
|
| - if (!IPC::SocketPair(
|
| - &browser_server_ppapi_fd, &browser_client_ppapi_fd) ||
|
| - !IPC::SocketPair(
|
| - &renderer_server_ppapi_fd, &renderer_client_ppapi_fd) ||
|
| - !IPC::SocketPair(
|
| - &manifest_service_server_fd, &manifest_service_client_fd)) {
|
| - LOG(ERROR) << "Failed to create sockets for IPC.";
|
| - return;
|
| - }
|
| -
|
| - // Set the plugin IPC channel FDs.
|
| - ppapi::SetIPCFileDescriptors(browser_server_ppapi_fd,
|
| - renderer_server_ppapi_fd,
|
| - manifest_service_server_fd);
|
| - ppapi::StartUpPlugin();
|
| -
|
| - // Send back to the client side IPC channel FD to the host.
|
| - browser_handle.socket =
|
| - base::FileDescriptor(browser_client_ppapi_fd, true);
|
| - ppapi_renderer_handle.socket =
|
| - base::FileDescriptor(renderer_client_ppapi_fd, true);
|
| - manifest_service_handle.socket =
|
| - base::FileDescriptor(manifest_service_client_fd, true);
|
| - }
|
| -
|
| - // TODO(teravest): Do we plan on using this renderer handle for nexe loading
|
| - // for non-SFI? Right now, passing an empty channel handle instead causes
|
| - // hangs, so we'll keep it.
|
| - IPC::ChannelHandle trusted_renderer_handle = CreateTrustedListener(
|
| - io_thread_.message_loop_proxy(), &shutdown_event_);
|
| - if (!Send(new NaClProcessHostMsg_PpapiChannelsCreated(
|
| - browser_handle, ppapi_renderer_handle,
|
| - trusted_renderer_handle, manifest_service_handle)))
|
| - LOG(ERROR) << "Failed to send IPC channel handle to NaClProcessHost.";
|
| -
|
| - // Ensure that the validation cache key (used as an extra input to the
|
| - // validation cache's hashing) isn't exposed accidentally.
|
| - CHECK(!params.validation_cache_enabled);
|
| - CHECK(params.validation_cache_key.size() == 0);
|
| - CHECK(params.version.size() == 0);
|
| - // Ensure that a debug stub FD isn't passed through accidentally.
|
| - CHECK(!params.enable_debug_stub);
|
| - CHECK(params.debug_stub_server_bound_socket.fd == -1);
|
| -
|
| - CHECK(!params.uses_irt);
|
| - CHECK(params.handles.empty());
|
| -
|
| - CHECK(params.nexe_file != IPC::InvalidPlatformFileForTransit());
|
| - CHECK(params.nexe_token_lo == 0);
|
| - CHECK(params.nexe_token_hi == 0);
|
| - nacl::nonsfi::MainStart(
|
| - IPC::PlatformFileForTransitToPlatformFile(params.nexe_file));
|
| -#endif // defined(OS_LINUX)
|
| + delegate_->Start(
|
| + params, io_thread_.message_loop_proxy(), CreateTrustedListener(), this);
|
| }
|
|
|
| -IPC::ChannelHandle NaClListener::CreateTrustedListener(
|
| - base::MessageLoopProxy* message_loop_proxy,
|
| - base::WaitableEvent* shutdown_event) {
|
| +IPC::ChannelHandle NaClListener::CreateTrustedListener() {
|
| // The argument passed to GenerateVerifiedChannelID() here MUST be "nacl".
|
| // Using an alternate channel name prevents the pipe from being created on
|
| // Windows when the sandbox is enabled.
|
| - IPC::ChannelHandle trusted_renderer_handle =
|
| + IPC::ChannelHandle trusted_channel_handle =
|
| IPC::Channel::GenerateVerifiedChannelID("nacl");
|
| trusted_listener_ = new NaClTrustedListener(
|
| - trusted_renderer_handle, io_thread_.message_loop_proxy().get());
|
| + trusted_channel_handle, io_thread_.message_loop_proxy());
|
| #if defined(OS_POSIX)
|
| - trusted_renderer_handle.socket = base::FileDescriptor(
|
| + trusted_channel_handle.socket = base::FileDescriptor(
|
| trusted_listener_->TakeClientFileDescriptor(), true);
|
| #endif
|
| - return trusted_renderer_handle;
|
| + return trusted_channel_handle;
|
| }
|
|
|