123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458 |
- // Copyright 2013 The Chromium Authors. All rights reserved.
- // Use of this source code is governed by a BSD-style license that can be
- // found in the LICENSE file.
- #include "components/nacl/loader/nacl_listener.h"
- #include <errno.h>
- #include <fcntl.h>
- #include <stdlib.h>
- #include <string.h>
- #include <memory>
- #include <utility>
- #include "base/bind.h"
- #include "base/memory/raw_ptr.h"
- #include "build/build_config.h"
- #if BUILDFLAG(IS_POSIX)
- #include <unistd.h>
- #endif
- #include "base/command_line.h"
- #include "base/logging.h"
- #include "base/memory/read_only_shared_memory_region.h"
- #include "base/message_loop/message_pump_type.h"
- #include "base/rand_util.h"
- #include "base/run_loop.h"
- #include "base/synchronization/waitable_event.h"
- #include "base/task/single_thread_task_runner.h"
- #include "base/threading/thread_task_runner_handle.h"
- #include "build/build_config.h"
- #include "components/nacl/common/nacl.mojom.h"
- #include "components/nacl/common/nacl_messages.h"
- #include "components/nacl/common/nacl_service.h"
- #include "components/nacl/common/nacl_switches.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 "ipc/ipc_channel_handle.h"
- #include "ipc/ipc_sync_channel.h"
- #include "ipc/ipc_sync_message_filter.h"
- #include "mojo/public/cpp/bindings/pending_remote.h"
- #include "native_client/src/public/chrome_main.h"
- #include "native_client/src/public/nacl_app.h"
- #include "native_client/src/public/nacl_desc.h"
- #if BUILDFLAG(IS_LINUX) || BUILDFLAG(IS_CHROMEOS)
- #include "content/public/common/zygote/sandbox_support_linux.h"
- #endif
- #if BUILDFLAG(IS_POSIX)
- #include "base/posix/eintr_wrapper.h"
- #endif
- #if BUILDFLAG(IS_WIN)
- #include <io.h>
- #endif
- namespace {
- NaClListener* g_listener;
- void FatalLogHandler(const char* data, size_t bytes) {
- // We use uint32_t rather than size_t for the case when the browser and NaCl
- // processes are a mix of 32-bit and 64-bit processes.
- uint32_t copy_bytes = std::min<uint32_t>(static_cast<uint32_t>(bytes),
- nacl::kNaClCrashInfoMaxLogSize);
- // We copy the length of the crash data to the start of the shared memory
- // segment so we know how much to copy.
- memcpy(g_listener->crash_info_shmem_memory(), ©_bytes, sizeof(uint32_t));
- memcpy((char*)g_listener->crash_info_shmem_memory() + sizeof(uint32_t),
- data,
- copy_bytes);
- }
- void LoadStatusCallback(int load_status) {
- g_listener->trusted_listener()->renderer_host()->ReportLoadStatus(
- static_cast<NaClErrorCode>(load_status));
- }
- #if BUILDFLAG(IS_WIN)
- 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 task runner runs.
- // Also, creates and sets the corresponding NaClDesc to the given nap with
- // the FD #.
- void SetUpIPCAdapter(
- IPC::ChannelHandle* handle,
- scoped_refptr<base::SingleThreadTaskRunner> task_runner,
- struct NaClApp* nap,
- int nacl_fd,
- NaClIPCAdapter::ResolveFileTokenCallback resolve_file_token_cb,
- NaClIPCAdapter::OpenResourceCallback open_resource_cb) {
- mojo::MessagePipe pipe;
- scoped_refptr<NaClIPCAdapter> ipc_adapter(new NaClIPCAdapter(
- pipe.handle0.release(), task_runner, std::move(resolve_file_token_cb),
- std::move(open_resource_cb)));
- ipc_adapter->ConnectChannel();
- *handle = pipe.handle1.release();
- // 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) {
- }
- 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;
- }
- 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.";
- }
- }
- private:
- // The listener never dies, otherwise this might be a dangling reference.
- raw_ptr<NaClListener> listener_;
- };
- NaClListener::NaClListener()
- : shutdown_event_(base::WaitableEvent::ResetPolicy::MANUAL,
- base::WaitableEvent::InitialState::NOT_SIGNALED),
- io_thread_("NaCl_IOThread"),
- #if BUILDFLAG(IS_LINUX) || BUILDFLAG(IS_CHROMEOS)
- prereserved_sandbox_size_(0),
- #endif
- #if BUILDFLAG(IS_POSIX)
- number_of_cores_(-1), // unknown/error
- #endif
- is_started_(false) {
- io_thread_.StartWithOptions(
- base::Thread::Options(base::MessagePumpType::IO, 0));
- DCHECK(g_listener == NULL);
- g_listener = this;
- }
- NaClListener::~NaClListener() {
- NOTREACHED();
- shutdown_event_.Signal();
- g_listener = NULL;
- }
- bool NaClListener::Send(IPC::Message* msg) {
- DCHECK(!!main_task_runner_);
- if (main_task_runner_->BelongsToCurrentThread()) {
- // This thread owns the channel.
- return channel_->Send(msg);
- }
- // This thread does not own the channel.
- return filter_->Send(msg);
- }
- // The NaClProcessMsg_ResolveFileTokenAsyncReply message must be
- // processed in a MessageFilter so it can be handled on the IO thread.
- // The main thread used by NaClListener is busy in
- // NaClChromeMainAppStart(), so it can't be used for servicing messages.
- class FileTokenMessageFilter : public IPC::MessageFilter {
- public:
- bool OnMessageReceived(const IPC::Message& msg) override {
- bool handled = true;
- IPC_BEGIN_MESSAGE_MAP(FileTokenMessageFilter, msg)
- IPC_MESSAGE_HANDLER(NaClProcessMsg_ResolveFileTokenReply,
- OnResolveFileTokenReply)
- IPC_MESSAGE_UNHANDLED(handled = false)
- IPC_END_MESSAGE_MAP()
- return handled;
- }
- void OnResolveFileTokenReply(
- uint64_t token_lo,
- uint64_t token_hi,
- IPC::PlatformFileForTransit ipc_fd,
- base::FilePath file_path) {
- CHECK(g_listener);
- g_listener->OnFileTokenResolved(token_lo, token_hi, ipc_fd, file_path);
- }
- private:
- ~FileTokenMessageFilter() override {}
- };
- void NaClListener::Listen() {
- NaClService service(io_thread_.task_runner());
- channel_ = IPC::SyncChannel::Create(this, io_thread_.task_runner().get(),
- base::ThreadTaskRunnerHandle::Get(),
- &shutdown_event_);
- filter_ = channel_->CreateSyncMessageFilter();
- channel_->AddFilter(new FileTokenMessageFilter());
- channel_->Init(service.TakeChannelPipe().release(), IPC::Channel::MODE_CLIENT,
- true);
- main_task_runner_ = base::ThreadTaskRunnerHandle::Get();
- base::RunLoop().Run();
- }
- #if BUILDFLAG(IS_LINUX) || BUILDFLAG(IS_CHROMEOS)
- // static
- int NaClListener::MakeSharedMemorySegment(size_t length, int executable) {
- return content::SharedMemoryIPCSupport::MakeSharedMemorySegment(length,
- executable);
- }
- #endif
- bool NaClListener::OnMessageReceived(const IPC::Message& msg) {
- bool handled = true;
- IPC_BEGIN_MESSAGE_MAP(NaClListener, msg)
- IPC_MESSAGE_HANDLER(NaClProcessMsg_AddPrefetchedResource,
- OnAddPrefetchedResource)
- IPC_MESSAGE_HANDLER(NaClProcessMsg_Start, OnStart)
- IPC_MESSAGE_UNHANDLED(handled = false)
- IPC_END_MESSAGE_MAP()
- return handled;
- }
- bool NaClListener::OnOpenResource(
- const IPC::Message& msg,
- const std::string& key,
- NaClIPCAdapter::OpenResourceReplyCallback cb) {
- // This callback is executed only on |io_thread_| with NaClIPCAdapter's
- // |lock_| not being held.
- DCHECK(!cb.is_null());
- auto it = prefetched_resource_files_.find(key);
- if (it != prefetched_resource_files_.end()) {
- // Fast path for prefetched FDs.
- IPC::PlatformFileForTransit file = it->second.first;
- base::FilePath path = it->second.second;
- prefetched_resource_files_.erase(it);
- // A pre-opened resource descriptor is available. Run the reply callback
- // and return true.
- std::move(cb).Run(msg, file, path);
- return true;
- }
- // Return false to fall back to the slow path. Let NaClIPCAdapter issue an
- // IPC to the renderer.
- return false;
- }
- void NaClListener::OnAddPrefetchedResource(
- const nacl::NaClResourcePrefetchResult& prefetched_resource_file) {
- DCHECK(!is_started_);
- if (is_started_)
- return;
- bool result = prefetched_resource_files_.insert(std::make_pair(
- prefetched_resource_file.file_key,
- std::make_pair(
- prefetched_resource_file.file,
- prefetched_resource_file.file_path_metadata))).second;
- if (!result) {
- LOG(FATAL) << "Duplicated open_resource key: "
- << prefetched_resource_file.file_key;
- }
- }
- void NaClListener::OnStart(nacl::NaClStartParams params) {
- is_started_ = true;
- #if BUILDFLAG(IS_LINUX) || BUILDFLAG(IS_CHROMEOS) || BUILDFLAG(IS_APPLE)
- int urandom_fd = HANDLE_EINTR(dup(base::GetUrandomFD()));
- if (urandom_fd < 0) {
- LOG(FATAL) << "Failed to dup() the urandom FD";
- }
- NaClChromeMainSetUrandomFd(urandom_fd);
- #endif
- struct NaClApp* nap = NULL;
- NaClChromeMainInit();
- CHECK(params.crash_info_shmem_region.IsValid());
- crash_info_shmem_mapping_ = params.crash_info_shmem_region.Map();
- base::ReadOnlySharedMemoryRegion ro_shmem_region =
- base::WritableSharedMemoryRegion::ConvertToReadOnly(
- std::move(params.crash_info_shmem_region));
- CHECK(crash_info_shmem_mapping_.IsValid());
- CHECK(ro_shmem_region.IsValid());
- NaClSetFatalErrorCallback(&FatalLogHandler);
- nap = NaClAppCreate();
- if (nap == NULL) {
- LOG(FATAL) << "NaClAppCreate() failed";
- }
- IPC::ChannelHandle browser_handle;
- IPC::ChannelHandle ppapi_renderer_handle;
- IPC::ChannelHandle manifest_service_handle;
- // 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_.task_runner(), nap,
- NACL_CHROME_DESC_BASE,
- NaClIPCAdapter::ResolveFileTokenCallback(),
- NaClIPCAdapter::OpenResourceCallback());
- SetUpIPCAdapter(&ppapi_renderer_handle, io_thread_.task_runner(), nap,
- NACL_CHROME_DESC_BASE + 1,
- NaClIPCAdapter::ResolveFileTokenCallback(),
- NaClIPCAdapter::OpenResourceCallback());
- SetUpIPCAdapter(&manifest_service_handle, io_thread_.task_runner(), nap,
- NACL_CHROME_DESC_BASE + 2,
- base::BindRepeating(&NaClListener::ResolveFileToken,
- base::Unretained(this)),
- base::BindRepeating(&NaClListener::OnOpenResource,
- base::Unretained(this)));
- mojo::PendingRemote<nacl::mojom::NaClRendererHost> renderer_host;
- if (!Send(new NaClProcessHostMsg_PpapiChannelsCreated(
- browser_handle, ppapi_renderer_handle,
- renderer_host.InitWithNewPipeAndPassReceiver().PassPipe().release(),
- manifest_service_handle, ro_shmem_region)))
- LOG(FATAL) << "Failed to send IPC channel handle to NaClProcessHost.";
- trusted_listener_ = std::make_unique<NaClTrustedListener>(
- std::move(renderer_host), io_thread_.task_runner().get());
- struct NaClChromeMainArgs* args = NaClChromeMainArgsCreate();
- if (args == NULL) {
- LOG(FATAL) << "NaClChromeMainArgsCreate() failed";
- }
- #if BUILDFLAG(IS_POSIX)
- args->number_of_cores = number_of_cores_;
- #endif
- #if BUILDFLAG(IS_LINUX) || BUILDFLAG(IS_CHROMEOS)
- args->create_memory_object_func = &MakeSharedMemorySegment;
- #endif
- DCHECK(params.process_type != nacl::kUnknownNaClProcessType);
- CHECK(params.irt_handle != IPC::InvalidPlatformFileForTransit());
- base::PlatformFile irt_handle =
- IPC::PlatformFileForTransitToPlatformFile(params.irt_handle);
- #if BUILDFLAG(IS_WIN)
- args->irt_fd = _open_osfhandle(reinterpret_cast<intptr_t>(irt_handle),
- _O_RDONLY | _O_BINARY);
- if (args->irt_fd < 0) {
- LOG(FATAL) << "_open_osfhandle() failed";
- }
- #else
- args->irt_fd = irt_handle;
- #endif
- 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);
- }
- args->enable_debug_stub = params.enable_debug_stub;
- // Now configure parts that depend on process type.
- // Start with stricter settings.
- args->enable_exception_handling = 0;
- args->enable_dyncode_syscalls = 0;
- // pnacl_mode=1 mostly disables things (IRT interfaces and syscalls).
- args->pnacl_mode = 1;
- // 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 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.
- if (params.process_type == nacl::kNativeNaClProcessType) {
- args->enable_exception_handling = 1;
- args->enable_dyncode_syscalls = 1;
- args->pnacl_mode = 0;
- args->initial_nexe_max_code_bytes = 0;
- } else if (params.process_type == nacl::kPNaClTranslatorProcessType) {
- args->pnacl_mode = 0;
- }
- #if BUILDFLAG(IS_POSIX)
- args->debug_stub_server_bound_socket_fd =
- IPC::PlatformFileForTransitToPlatformFile(
- params.debug_stub_server_bound_socket);
- #endif
- #if BUILDFLAG(IS_WIN)
- args->attach_debug_exception_handler_func = AttachDebugExceptionHandler;
- args->debug_stub_server_port_selected_handler_func =
- DebugStubPortSelectedHandler;
- #endif
- args->load_status_handler_func = LoadStatusCallback;
- #if BUILDFLAG(IS_LINUX) || BUILDFLAG(IS_CHROMEOS)
- args->prereserved_sandbox_size = prereserved_sandbox_size_;
- #endif
- base::PlatformFile nexe_file = IPC::PlatformFileForTransitToPlatformFile(
- params.nexe_file);
- std::string file_path_str = params.nexe_file_path_metadata.AsUTF8Unsafe();
- args->nexe_desc = NaClDescCreateWithFilePathMetadata(nexe_file,
- file_path_str.c_str());
- int exit_status;
- if (!NaClChromeMainStart(nap, args, &exit_status))
- NaClExit(1);
- // Report the plugin's exit status if the application started successfully.
- trusted_listener_->renderer_host()->ReportExitStatus(exit_status);
- NaClExit(exit_status);
- }
- void NaClListener::ResolveFileToken(
- uint64_t token_lo,
- uint64_t token_hi,
- NaClIPCAdapter::ResolveFileTokenReplyCallback cb) {
- if (!Send(new NaClProcessMsg_ResolveFileToken(token_lo, token_hi))) {
- std::move(cb).Run(IPC::PlatformFileForTransit(), base::FilePath());
- return;
- }
- resolved_cb_ = std::move(cb);
- }
- void NaClListener::OnFileTokenResolved(
- uint64_t token_lo,
- uint64_t token_hi,
- IPC::PlatformFileForTransit ipc_fd,
- base::FilePath file_path) {
- if (resolved_cb_)
- std::move(resolved_cb_).Run(ipc_fd, file_path);
- }
|