blob: cb26e547605c9ef7ab30a3008bd2ecd1144b49dc [file] [log] [blame]
/*
* Copyright (c) 2012 The Native Client 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 <string.h>
/*
* NaCl Simple/secure ELF loader (NaCl SEL).
*/
#include "native_client/src/include/build_config.h"
#if NACL_WINDOWS
#include <intrin.h>
#endif
#include "native_client/src/include/portability.h"
#include "native_client/src/include/portability_io.h"
#include "native_client/src/include/portability_string.h"
#include "native_client/src/include/nacl_macros.h"
#include "native_client/src/public/nacl_app.h"
#include "native_client/src/shared/gio/gio.h"
#include "native_client/src/shared/platform/nacl_check.h"
#include "native_client/src/shared/platform/nacl_exit.h"
#include "native_client/src/shared/platform/nacl_log.h"
#include "native_client/src/shared/platform/nacl_sync.h"
#include "native_client/src/shared/platform/nacl_sync_checked.h"
#include "native_client/src/shared/platform/nacl_time.h"
#include "native_client/src/trusted/desc/nacl_desc_base.h"
#include "native_client/src/trusted/desc/nacl_desc_conn_cap.h"
#include "native_client/src/trusted/desc/nacl_desc_imc.h"
#include "native_client/src/trusted/desc/nacl_desc_io.h"
#include "native_client/src/trusted/desc/nrd_xfer.h"
#include "native_client/src/trusted/fault_injection/fault_injection.h"
#include "native_client/src/trusted/fault_injection/test_injection.h"
#include "native_client/src/trusted/interval_multiset/nacl_interval_range_tree_intern.h"
#include "native_client/src/trusted/service_runtime/arch/sel_ldr_arch.h"
#include "native_client/src/trusted/service_runtime/include/bits/nacl_syscalls.h"
#include "native_client/src/trusted/service_runtime/include/sys/fcntl.h"
#include "native_client/src/trusted/service_runtime/include/sys/stat.h"
#include "native_client/src/trusted/service_runtime/include/sys/time.h"
#include "native_client/src/trusted/service_runtime/nacl_app.h"
#include "native_client/src/trusted/service_runtime/nacl_app_thread.h"
#include "native_client/src/trusted/service_runtime/nacl_desc_effector_ldr.h"
#include "native_client/src/trusted/service_runtime/nacl_globals.h"
#include "native_client/src/trusted/service_runtime/nacl_resource.h"
#include "native_client/src/trusted/service_runtime/nacl_syscall_common.h"
#include "native_client/src/trusted/service_runtime/nacl_syscall_list.h"
#include "native_client/src/trusted/service_runtime/nacl_valgrind_hooks.h"
#include "native_client/src/trusted/service_runtime/sel_addrspace.h"
#include "native_client/src/trusted/service_runtime/sel_ldr.h"
#include "native_client/src/trusted/service_runtime/sel_memory.h"
#include "native_client/src/trusted/validator/rich_file_info.h"
static int IsEnvironmentVariableSet(char const *env_name) {
return NULL != getenv(env_name);
}
static int ShouldEnableDyncodeSyscalls(void) {
return !IsEnvironmentVariableSet("NACL_DISABLE_DYNCODE_SYSCALLS");
}
static int ShouldEnableDynamicLoading(void) {
return !IsEnvironmentVariableSet("NACL_DISABLE_DYNAMIC_LOADING");
}
int NaClAppWithEmptySyscallTableCtor(struct NaClApp *nap) {
struct NaClDescEffectorLdr *effp;
int i;
/* Zero-initialize in case we miss any fields below. */
memset(nap, 0, sizeof(*nap));
/* The validation cache will be injected later, if it exists. */
nap->validation_cache = NULL;
nap->validator = NaClCreateValidator();
/* Get the set of features that the CPU we're running on supports. */
/* These may be adjusted later in sel_main.c for fixed-feature CPU mode. */
nap->cpu_features = (NaClCPUFeatures *) malloc(
nap->validator->CPUFeatureSize);
if (NULL == nap->cpu_features) {
goto cleanup_none;
}
nap->validator->GetCurrentCPUFeatures(nap->cpu_features);
nap->fixed_feature_cpu_mode = 0;
nap->addr_bits = NACL_MAX_ADDR_BITS;
nap->stack_size = NACL_DEFAULT_STACK_MAX;
nap->initial_nexe_max_code_bytes = 0;
nap->mem_start = 0;
#if (NACL_ARCH(NACL_BUILD_ARCH) == NACL_x86 \
&& NACL_BUILD_SUBARCH == 32)
nap->pcrel_thunk = 0;
nap->pcrel_thunk_end = 0;
#endif
#if (NACL_ARCH(NACL_BUILD_ARCH) == NACL_x86 \
&& NACL_BUILD_SUBARCH == 64)
nap->nacl_syscall_addr = 0;
nap->get_tls_fast_path1_addr = 0;
nap->get_tls_fast_path2_addr = 0;
#endif
nap->static_text_end = 0;
nap->dynamic_text_start = 0;
nap->dynamic_text_end = 0;
nap->rodata_start = 0;
nap->data_start = 0;
nap->data_end = 0;
nap->initial_entry_pt = 0;
nap->user_entry_pt = 0;
if (!DynArrayCtor(&nap->threads, 2)) {
goto cleanup_cpu_features;
}
if (!DynArrayCtor(&nap->desc_tbl, 2)) {
goto cleanup_threads;
}
if (!NaClVmmapCtor(&nap->mem_map)) {
goto cleanup_desc_tbl;
}
nap->mem_io_regions = (struct NaClIntervalMultiset *) malloc(
sizeof(struct NaClIntervalRangeTree));
if (NULL == nap->mem_io_regions) {
goto cleanup_mem_map;
}
if (!NaClIntervalRangeTreeCtor((struct NaClIntervalRangeTree *)
nap->mem_io_regions)) {
free(nap->mem_io_regions);
nap->mem_io_regions = NULL;
goto cleanup_mem_map;
}
effp = (struct NaClDescEffectorLdr *) malloc(sizeof *effp);
if (NULL == effp) {
goto cleanup_mem_io_regions;
}
if (!NaClDescEffectorLdrCtor(effp, nap)) {
goto cleanup_effp_free;
}
nap->effp = (struct NaClDescEffector *) effp;
nap->enable_dyncode_syscalls = ShouldEnableDyncodeSyscalls();
nap->use_shm_for_dynamic_text = ShouldEnableDynamicLoading();
nap->text_shm = NULL;
if (!NaClMutexCtor(&nap->dynamic_load_mutex)) {
goto cleanup_effp_free;
}
nap->dynamic_page_bitmap = NULL;
nap->dynamic_regions = NULL;
nap->num_dynamic_regions = 0;
nap->dynamic_regions_allocated = 0;
nap->dynamic_delete_generation = 0;
nap->dynamic_mapcache_offset = 0;
nap->dynamic_mapcache_size = 0;
nap->dynamic_mapcache_ret = 0;
nap->main_exe_prevalidated = 0;
if (!NaClResourceNaClAppInit(&nap->resources, nap)) {
goto cleanup_dynamic_load_mutex;
}
if (!NaClMutexCtor(&nap->mu)) {
goto cleanup_dynamic_load_mutex;
}
if (!NaClCondVarCtor(&nap->cv)) {
goto cleanup_mu;
}
#if NACL_WINDOWS
nap->vm_hole_may_exist = 0;
nap->threads_launching = 0;
#endif
for (i = 0; i < NACL_MAX_SYSCALLS; ++i) {
nap->syscall_table[i].handler = &NaClSysNotImplementedDecoder;
}
nap->module_initialization_state = NACL_MODULE_UNINITIALIZED;
nap->module_load_status = LOAD_OK;
nap->ignore_validator_result = 0;
nap->skip_validator = 0;
nap->validator_stub_out_mode = 0;
if (IsEnvironmentVariableSet("NACL_DANGEROUS_ENABLE_FILE_ACCESS")) {
NaClInsecurelyBypassAllAclChecks();
NaClLog(LOG_INFO, "DANGER: ENABLED FILE ACCESS\n");
}
nap->enable_list_mappings = 0;
if (IsEnvironmentVariableSet("NACL_DANGEROUS_ENABLE_LIST_MAPPINGS")) {
/*
* This syscall is not actually know to be dangerous, but is not yet
* exposed by our public API.
*/
NaClLog(LOG_INFO, "DANGER: ENABLED LIST_MAPPINGS\n");
nap->enable_list_mappings = 1;
}
nap->pnacl_mode = 0;
if (!NaClMutexCtor(&nap->threads_mu)) {
goto cleanup_cv;
}
nap->num_threads = 0;
if (!NaClFastMutexCtor(&nap->desc_mu)) {
goto cleanup_threads_mu;
}
nap->running = 0;
nap->exit_status = -1;
#if NACL_ARCH(NACL_BUILD_ARCH) == NACL_x86 && NACL_BUILD_SUBARCH == 32
nap->code_seg_sel = 0;
nap->data_seg_sel = 0;
#endif
nap->debug_stub_callbacks = NULL;
nap->main_nexe_desc = NULL;
nap->irt_nexe_desc = NULL;
nap->exception_handler = 0;
if (!NaClMutexCtor(&nap->exception_mu)) {
goto cleanup_desc_mu;
}
nap->enable_exception_handling = 0;
#if NACL_WINDOWS
nap->debug_exception_handler_state = NACL_DEBUG_EXCEPTION_HANDLER_NOT_STARTED;
nap->attach_debug_exception_handler_func = NULL;
#endif
nap->enable_faulted_thread_queue = 0;
nap->faulted_thread_count = 0;
#if NACL_WINDOWS
nap->faulted_thread_event = INVALID_HANDLE_VALUE;
#else
nap->faulted_thread_fd_read = -1;
nap->faulted_thread_fd_write = -1;
#endif
#if NACL_LINUX || NACL_OSX
/*
* Try to pre-cache information that we can't obtain with the outer
* sandbox on. If the outer sandbox has already been enabled, this
* will just set sc_nprocessors_onln to -1, and it is the
* responsibility of the caller to replace this with a sane value
* after the Ctor returns.
*/
nap->sc_nprocessors_onln = sysconf(_SC_NPROCESSORS_ONLN);
#endif
#if !NACL_LINUX
if (!NaClMutexCtor(&nap->futex_wait_list_mu)) {
goto cleanup_exception_mu;
}
nap->futex_wait_list_head.next = &nap->futex_wait_list_head;
nap->futex_wait_list_head.prev = &nap->futex_wait_list_head;
#endif
return 1;
#if !NACL_LINUX
cleanup_exception_mu:
NaClMutexDtor(&nap->exception_mu);
#endif
cleanup_desc_mu:
NaClFastMutexDtor(&nap->desc_mu);
cleanup_threads_mu:
NaClMutexDtor(&nap->threads_mu);
cleanup_cv:
NaClCondVarDtor(&nap->cv);
cleanup_mu:
NaClMutexDtor(&nap->mu);
cleanup_dynamic_load_mutex:
NaClMutexDtor(&nap->dynamic_load_mutex);
cleanup_effp_free:
free(nap->effp);
cleanup_mem_io_regions:
NaClIntervalMultisetDelete(nap->mem_io_regions);
nap->mem_io_regions = NULL;
cleanup_mem_map:
NaClVmmapDtor(&nap->mem_map);
cleanup_desc_tbl:
DynArrayDtor(&nap->desc_tbl);
cleanup_threads:
DynArrayDtor(&nap->threads);
cleanup_cpu_features:
free(nap->cpu_features);
cleanup_none:
return 0;
}
int NaClAppCtor(struct NaClApp *nap) {
if (!NaClAppWithEmptySyscallTableCtor(nap))
return 0;
NaClAppRegisterDefaultSyscalls(nap);
return 1;
}
struct NaClApp *NaClAppCreate(void) {
struct NaClApp *nap = malloc(sizeof(struct NaClApp));
if (nap == NULL)
NaClLog(LOG_FATAL, "Failed to allocate NaClApp\n");
if (!NaClAppCtor(nap))
NaClLog(LOG_FATAL, "NaClAppCtor() failed\n");
return nap;
}
/*
* unaligned little-endian load. precondition: nbytes should never be
* more than 8.
*/
static uint64_t NaClLoadMem(uintptr_t addr,
size_t user_nbytes) {
uint64_t value = 0;
CHECK(0 != user_nbytes && user_nbytes <= 8);
do {
value = value << 8;
value |= ((uint8_t *) addr)[--user_nbytes];
} while (user_nbytes > 0);
return value;
}
#define GENERIC_LOAD(bits) \
static uint ## bits ## _t NaClLoad ## bits(uintptr_t addr) { \
return (uint ## bits ## _t) NaClLoadMem(addr, sizeof(uint ## bits ## _t)); \
}
#if NACL_BUILD_SUBARCH == 32
GENERIC_LOAD(32)
#endif
GENERIC_LOAD(64)
#undef GENERIC_LOAD
/*
* unaligned little-endian store
*/
static void NaClStoreMem(uintptr_t addr,
size_t nbytes,
uint64_t value) {
size_t i;
CHECK(nbytes <= 8);
for (i = 0; i < nbytes; ++i) {
((uint8_t *) addr)[i] = (uint8_t) value;
value = value >> 8;
}
}
#define GENERIC_STORE(bits) \
static void NaClStore ## bits(uintptr_t addr, \
uint ## bits ## _t v) { \
NaClStoreMem(addr, sizeof(uint ## bits ## _t), v); \
}
GENERIC_STORE(16)
GENERIC_STORE(32)
GENERIC_STORE(64)
#undef GENERIC_STORE
struct NaClPatchInfo *NaClPatchInfoCtor(struct NaClPatchInfo *self) {
if (NULL != self) {
memset(self, 0, sizeof *self);
}
return self;
}
/*
* This function is called by NaClLoadTrampoline and NaClLoadSpringboard to
* patch a single memory location specified in NaClPatchInfo structure.
*/
void NaClApplyPatchToMemory(struct NaClPatchInfo *patch) {
size_t i;
size_t offset;
int64_t reloc;
uintptr_t target_addr;
memcpy((void *) patch->dst, (void *) patch->src, patch->nbytes);
reloc = patch->dst - patch->src;
for (i = 0; i < patch->num_abs64; ++i) {
offset = patch->abs64[i].target - patch->src;
target_addr = patch->dst + offset;
NaClStore64(target_addr, patch->abs64[i].value);
}
for (i = 0; i < patch->num_abs32; ++i) {
offset = patch->abs32[i].target - patch->src;
target_addr = patch->dst + offset;
NaClStore32(target_addr, (uint32_t) patch->abs32[i].value);
}
for (i = 0; i < patch->num_abs16; ++i) {
offset = patch->abs16[i].target - patch->src;
target_addr = patch->dst + offset;
NaClStore16(target_addr, (uint16_t) patch->abs16[i].value);
}
for (i = 0; i < patch->num_rel64; ++i) {
offset = patch->rel64[i] - patch->src;
target_addr = patch->dst + offset;
NaClStore64(target_addr, NaClLoad64(target_addr) - reloc);
}
/*
* rel32 is only supported on 32-bit architectures. The range of a relative
* relocation in untrusted space is +/- 4GB. This can be represented as
* an unsigned 32-bit value mod 2^32, which is handy on a 32 bit system since
* all 32-bit pointer arithmetic is implicitly mod 2^32. On a 64 bit system,
* however, pointer arithmetic is implicitly modulo 2^64, which isn't as
* helpful for our purposes. We could simulate the 32-bit behavior by
* explicitly modding all relative addresses by 2^32, but that seems like an
* expensive way to save a few bytes per reloc.
*/
#if NACL_BUILD_SUBARCH == 32
for (i = 0; i < patch->num_rel32; ++i) {
offset = patch->rel32[i] - patch->src;
target_addr = patch->dst + offset;
NaClStore32(target_addr,
(uint32_t) NaClLoad32(target_addr) - (int32_t) reloc);
}
#endif
}
/*
* Install syscall trampolines at all possible well-formed entry
* points within the trampoline pages. Many of these syscalls will
* correspond to unimplemented system calls and will just abort the
* program.
*/
void NaClLoadTrampoline(struct NaClApp *nap, enum NaClAslrMode aslr_mode) {
int num_syscalls;
int i;
uintptr_t addr;
#if NACL_ARCH(NACL_BUILD_ARCH) == NACL_x86 && NACL_BUILD_SUBARCH == 32
if (!NaClMakePcrelThunk(nap, aslr_mode)) {
NaClLog(LOG_FATAL, "NaClMakePcrelThunk failed!\n");
}
#else
UNREFERENCED_PARAMETER(aslr_mode);
#endif
#if NACL_ARCH(NACL_BUILD_ARCH) == NACL_x86 && NACL_BUILD_SUBARCH == 64
if (!NaClMakeDispatchAddrs(nap)) {
NaClLog(LOG_FATAL, "NaClMakeDispatchAddrs failed!\n");
}
#endif
NaClFillTrampolineRegion(nap);
/*
* Do not bother to fill in the contents of page 0, since we make it
* inaccessible later (see sel_addrspace.c, NaClMemoryProtection)
* anyway to help detect NULL pointer errors, and we might as well
* not dirty the page.
*
* The last syscall entry point is used for springboard code.
*/
num_syscalls = ((NACL_TRAMPOLINE_END - NACL_SYSCALL_START_ADDR)
/ NACL_SYSCALL_BLOCK_SIZE) - 1;
NaClLog(2, "num_syscalls = %d (0x%x)\n", num_syscalls, num_syscalls);
for (i = 0, addr = nap->mem_start + NACL_SYSCALL_START_ADDR;
i < num_syscalls;
++i, addr += NACL_SYSCALL_BLOCK_SIZE) {
NaClPatchOneTrampoline(nap, addr);
}
#if NACL_ARCH(NACL_BUILD_ARCH) == NACL_x86 && NACL_BUILD_SUBARCH == 64
NaClPatchOneTrampolineCall(nap->get_tls_fast_path1_addr,
nap->mem_start + NACL_SYSCALL_START_ADDR
+ NACL_SYSCALL_BLOCK_SIZE * NACL_sys_tls_get);
NaClPatchOneTrampolineCall(nap->get_tls_fast_path2_addr,
nap->mem_start + NACL_SYSCALL_START_ADDR
+ (NACL_SYSCALL_BLOCK_SIZE *
NACL_sys_second_tls_get));
#endif
NACL_TEST_INJECTION(ChangeTrampolines, (nap));
}
struct NaClDesc *NaClAppGetDescMu(struct NaClApp *nap,
int d) {
struct NaClDesc *result;
result = (struct NaClDesc *) DynArrayGet(&nap->desc_tbl, d);
if (NULL != result) {
NaClDescRef(result);
}
return result;
}
void NaClAppSetDescMu(struct NaClApp *nap,
int d,
struct NaClDesc *ndp) {
struct NaClDesc *result;
result = (struct NaClDesc *) DynArrayGet(&nap->desc_tbl, d);
NaClDescSafeUnref(result);
if (!DynArraySet(&nap->desc_tbl, d, ndp)) {
NaClLog(LOG_FATAL,
"NaClAppSetDesc: could not set descriptor %d to 0x%08"
NACL_PRIxPTR"\n",
d,
(uintptr_t) ndp);
}
}
int32_t NaClAppSetDescAvailMu(struct NaClApp *nap,
struct NaClDesc *ndp) {
size_t pos;
pos = DynArrayFirstAvail(&nap->desc_tbl);
if (pos > INT32_MAX) {
NaClLog(LOG_FATAL,
("NaClAppSetDescAvailMu: DynArrayFirstAvail returned a value"
" that is greather than 2**31-1.\n"));
}
NaClAppSetDescMu(nap, (int) pos, ndp);
return (int32_t) pos;
}
struct NaClDesc *NaClAppGetDesc(struct NaClApp *nap,
int d) {
struct NaClDesc *res;
NaClFastMutexLock(&nap->desc_mu);
res = NaClAppGetDescMu(nap, d);
NaClFastMutexUnlock(&nap->desc_mu);
return res;
}
void NaClAppSetDesc(struct NaClApp *nap,
int d,
struct NaClDesc *ndp) {
NaClFastMutexLock(&nap->desc_mu);
NaClAppSetDescMu(nap, d, ndp);
NaClFastMutexUnlock(&nap->desc_mu);
}
int32_t NaClAppSetDescAvail(struct NaClApp *nap,
struct NaClDesc *ndp) {
int32_t pos;
NaClFastMutexLock(&nap->desc_mu);
pos = NaClAppSetDescAvailMu(nap, ndp);
NaClFastMutexUnlock(&nap->desc_mu);
return pos;
}
int NaClAddThreadMu(struct NaClApp *nap,
struct NaClAppThread *natp) {
size_t pos;
pos = DynArrayFirstAvail(&nap->threads);
if (!DynArraySet(&nap->threads, pos, natp)) {
NaClLog(LOG_FATAL,
"NaClAddThreadMu: DynArraySet at position %"NACL_PRIuS" failed\n",
pos);
}
++nap->num_threads;
return (int) pos;
}
int NaClAddThread(struct NaClApp *nap,
struct NaClAppThread *natp) {
int pos;
NaClXMutexLock(&nap->threads_mu);
pos = NaClAddThreadMu(nap, natp);
NaClXMutexUnlock(&nap->threads_mu);
return pos;
}
void NaClRemoveThreadMu(struct NaClApp *nap,
int thread_num) {
if (NULL == DynArrayGet(&nap->threads, thread_num)) {
NaClLog(LOG_FATAL,
"NaClRemoveThreadMu:: thread to be removed is not in the table\n");
}
if (nap->num_threads == 0) {
NaClLog(LOG_FATAL,
"NaClRemoveThreadMu:: num_threads cannot be 0!!!\n");
}
--nap->num_threads;
if (!DynArraySet(&nap->threads, thread_num, (struct NaClAppThread *) NULL)) {
NaClLog(LOG_FATAL,
"NaClRemoveThreadMu:: DynArraySet at position %d failed\n",
thread_num);
}
}
void NaClRemoveThread(struct NaClApp *nap,
int thread_num) {
NaClXMutexLock(&nap->threads_mu);
NaClRemoveThreadMu(nap, thread_num);
NaClXMutexUnlock(&nap->threads_mu);
}
struct NaClAppThread *NaClGetThreadMu(struct NaClApp *nap,
int thread_num) {
return (struct NaClAppThread *) DynArrayGet(&nap->threads, thread_num);
}
void NaClAddHostDescriptor(struct NaClApp *nap,
int host_os_desc,
int flag,
int nacl_desc) {
struct NaClDescIoDesc *dp;
NaClLog(4,
"NaClAddHostDescriptor: host %d as nacl desc %d, flag 0x%x\n",
host_os_desc,
nacl_desc,
flag);
dp = NaClDescIoDescMake(NaClHostDescPosixMake(host_os_desc, flag));
if (NULL == dp) {
NaClLog(LOG_FATAL, "NaClAddHostDescriptor: NaClDescIoDescMake failed\n");
}
NaClAppSetDesc(nap, nacl_desc, (struct NaClDesc *) dp);
}
void NaClAddImcHandle(struct NaClApp *nap,
NaClHandle h,
int nacl_desc) {
struct NaClDescImcDesc *dp;
NaClLog(4,
("NaClAddImcHandle: importing NaClHandle %"NACL_PRIxPTR
" as nacl desc %d\n"),
(uintptr_t) h,
nacl_desc);
dp = (struct NaClDescImcDesc *) malloc(sizeof *dp);
if (NACL_FI_ERROR_COND("NaClAddImcHandle__malloc", NULL == dp)) {
NaClLog(LOG_FATAL, "NaClAddImcHandle: no memory\n");
}
if (NACL_FI_ERROR_COND("NaClAddImcHandle__ctor",
!NaClDescImcDescCtor(dp, h))) {
NaClLog(LOG_FATAL, ("NaClAddImcHandle: cannot construct"
" IMC descriptor object\n"));
}
NaClAppSetDesc(nap, nacl_desc, (struct NaClDesc *) dp);
}
static struct {
int d;
char const *env_name;
int nacl_flags;
int mode;
} const g_nacl_redir_control[] = {
{ 0, "NACL_EXE_STDIN",
NACL_ABI_O_RDONLY, 0, },
{ 1, "NACL_EXE_STDOUT",
NACL_ABI_O_WRONLY | NACL_ABI_O_APPEND | NACL_ABI_O_CREAT, 0777, },
{ 2, "NACL_EXE_STDERR",
NACL_ABI_O_WRONLY | NACL_ABI_O_APPEND | NACL_ABI_O_CREAT, 0777, },
};
/*
* Process I/O redirection/inheritance from the environment.
*
* File redirection is impossible if an outer sandbox is in place. For the
* command-line embedding, we sometimes have an outer sandbox: on OSX, it is
* enabled after loading the file is loaded. We handle this situation by
* allowing the NaClAppInitialDescriptorHookup to fail in which case in falls
* back to default inheritance. This means dup'ing descriptors 0-2 and making
* them available to the NaCl App.
*
* When standard input is inherited, this could result in a NaCl module
* competing for input from the terminal; for graphical / browser plugin
* environments, this never is allowed to happen, and having this is useful for
* debugging, and for potential standalone text-mode applications of NaCl.
*
* TODO(bsy): consider whether default inheritance should occur only
* in debug mode.
*/
void NaClAppInitialDescriptorHookup(struct NaClApp *nap) {
size_t ix;
char const *env;
struct NaClDesc *ndp;
NaClLog(4, "Processing I/O redirection/inheritance from environment\n");
for (ix = 0; ix < NACL_ARRAY_SIZE(g_nacl_redir_control); ++ix) {
ndp = NULL;
if (NULL != (env = getenv(g_nacl_redir_control[ix].env_name))) {
NaClLog(4, "getenv(%s) -> %s\n", g_nacl_redir_control[ix].env_name, env);
ndp = NaClResourceOpen((struct NaClResource *) &nap->resources,
env,
g_nacl_redir_control[ix].nacl_flags,
g_nacl_redir_control[ix].mode);
NaClLog(4, " NaClResourceOpen returned %"NACL_PRIxPTR"\n",
(uintptr_t) ndp);
}
if (NULL != ndp) {
NaClLog(4, "Setting descriptor %d\n", (int) ix);
NaClAppSetDesc(nap, (int) ix, ndp);
} else {
/*
* Environment not set or redirect failed -- handle default inheritance.
*/
NaClAddHostDescriptor(nap, DUP(g_nacl_redir_control[ix].d),
g_nacl_redir_control[ix].nacl_flags, (int) ix);
}
}
NaClLog(4, "... done.\n");
}
enum NaClModuleInitializationState NaClGetInitState(struct NaClApp *nap) {
enum NaClModuleInitializationState state;
NaClXMutexLock(&nap->mu);
state = nap->module_initialization_state;
NaClXMutexUnlock(&nap->mu);
return state;
}
void NaClSetInitState(struct NaClApp *nap,
enum NaClModuleInitializationState state) {
NaClXMutexLock(&nap->mu);
/* The initialization state should be increasing monotonically. */
CHECK(state > nap->module_initialization_state);
nap->module_initialization_state = state;
NaClXCondVarBroadcast(&nap->cv);
NaClXMutexUnlock(&nap->mu);
}
void NaClRememberLoadStatus(struct NaClApp *nap, NaClErrorCode status) {
NaClXMutexLock(&nap->mu);
/* Remember the first error we encountered. */
if (nap->module_load_status == LOAD_OK) {
nap->module_load_status = status;
}
NaClXMutexUnlock(&nap->mu);
}
NaClErrorCode NaClGetLoadStatus(struct NaClApp *nap) {
NaClErrorCode status;
NaClXMutexLock(&nap->mu);
status = nap->module_load_status;
NaClXMutexUnlock(&nap->mu);
return status;
}
void NaClAppLoadModule(struct NaClApp *nap,
struct NaClDesc *nexe,
void (*load_cb)(void *instance_data,
NaClErrorCode status),
void *instance_data) {
NaClErrorCode status = LOAD_OK;
int is_double_init = NaClGetInitState(nap) != NACL_MODULE_UNINITIALIZED;
NaClLog(4,
("Entered NaClAppLoadModule: nap 0x%"NACL_PRIxPTR","
" nexe 0x%"NACL_PRIxPTR"\n"),
(uintptr_t) nap, (uintptr_t) nexe);
if (NULL != load_cb) {
NaClErrorCode cb_status;
if (is_double_init) {
cb_status = LOAD_DUP_LOAD_MODULE;
} else {
cb_status = LOAD_OK;
}
(*load_cb)(instance_data, cb_status);
}
if (is_double_init) {
NaClLog(LOG_ERROR, "NaClAppLoadModule: repeated invocation\n");
return;
}
NaClSetInitState(nap, NACL_MODULE_LOADING);
/*
* Ref was passed by value into |nexe| parameter, so up the refcount.
* Be sure to unref when the parameter's copy goes out of scope
* (when returning).
*/
NaClDescRef(nexe);
NaClXMutexLock(&nap->mu);
/* Transfer ownership from nexe to nap->main_nexe_desc. */
CHECK(nap->main_nexe_desc == NULL);
nap->main_nexe_desc = nexe;
nexe = NULL;
status = NACL_FI_VAL("load_module", NaClErrorCode,
NaClAppLoadFile(nap->main_nexe_desc, nap));
if (LOAD_OK != status) {
NaClDescUnref(nap->main_nexe_desc);
nap->main_nexe_desc = NULL;
}
NaClXMutexUnlock(&nap->mu); /* NaClAppPrepareToLaunch takes mu */
if (LOAD_OK != status) {
NaClRememberLoadStatus(nap, status);
NaClSetInitState(nap, NACL_MODULE_ERROR);
return;
}
/***************************************************************************
* TODO(bsy): Remove/merge the code invoking NaClAppPrepareToLaunch
* and NaClGdbHook below with sel_main's main function. See comment
* there.
***************************************************************************/
/*
* Finish setting up the NaCl App.
*/
status = NaClAppPrepareToLaunch(nap);
NaClRememberLoadStatus(nap, status);
NaClSetInitState(nap, NACL_MODULE_LOADED);
/* Give debuggers a well known point at which xlate_base is known. */
NaClGdbHook(nap);
}
void NaClAppStartModule(struct NaClApp *nap,
void (*start_cb)(void *instance_data,
NaClErrorCode status),
void *instance_data) {
NaClErrorCode status;
NaClLog(4,
("Entered NaClAppStartModule, nap 0x%"NACL_PRIxPTR","
" start_cb 0x%"NACL_PRIxPTR", instance_data 0x%"NACL_PRIxPTR"\n"),
(uintptr_t) nap, (uintptr_t) start_cb, (uintptr_t) instance_data);
/*
* When module is loading, we have to block and wait till it is
* fully loaded before we can proceed with start module.
*/
NaClXMutexLock(&nap->mu);
if (NACL_MODULE_LOADING == nap->module_initialization_state) {
while (NACL_MODULE_LOADING == nap->module_initialization_state) {
NaClXCondVarWait(&nap->cv, &nap->mu);
}
}
status = nap->module_load_status;
if (nap->module_initialization_state != NACL_MODULE_LOADED) {
if (NACL_MODULE_ERROR == nap->module_initialization_state) {
NaClLog(LOG_ERROR, "NaClAppStartModule: error loading module\n");
} else if (nap->module_initialization_state > NACL_MODULE_LOADED) {
NaClLog(LOG_ERROR, "NaClAppStartModule: repeated invocation\n");
status = LOAD_DUP_START_MODULE;
} else if (nap->module_initialization_state < NACL_MODULE_LOADED) {
NaClLog(LOG_ERROR, "NaClAppStartModule: module not loaded\n");
status = LOAD_INTERNAL;
}
NaClXMutexUnlock(&nap->mu);
if (NULL != start_cb) {
(*start_cb)(instance_data, status);
}
return;
}
NaClXMutexUnlock(&nap->mu);
NaClSetInitState(nap, NACL_MODULE_STARTING);
NaClLog(4, "NaClSecureChannelStartModule: load status %d\n", status);
/*
* We need to invoke the callback now, before we signal the main thread
* to possibly start by setting the state to NACL_MODULE_STARTED, since
* in the case of failure the main thread may quickly exit; if the main
* thread does this before we sent the reply, than the plugin (or any
* other runtime host interface) will be left without an aswer. The
* NACL_MODULE_STARTING state is used as an intermediate state to prevent
* double invocations violating the protocol.
*/
if (NULL != start_cb) {
(*start_cb)(instance_data, status);
}
NaClSetInitState(nap, NACL_MODULE_STARTED);
}
/*
* It is fine to have multiple I/O operations read from memory in Write
* or SendMsg like operations.
*/
void NaClVmIoWillStart(struct NaClApp *nap,
uint32_t addr_first_usr,
uint32_t addr_last_usr) {
NaClXMutexLock(&nap->mu);
(*nap->mem_io_regions->vtbl->AddInterval)(nap->mem_io_regions,
addr_first_usr,
addr_last_usr);
NaClXMutexUnlock(&nap->mu);
}
void NaClVmIoHasEnded(struct NaClApp *nap,
uint32_t addr_first_usr,
uint32_t addr_last_usr) {
NaClXMutexLock(&nap->mu);
(*nap->mem_io_regions->vtbl->RemoveInterval)(nap->mem_io_regions,
addr_first_usr,
addr_last_usr);
NaClXMutexUnlock(&nap->mu);
}
void NaClVmIoPendingCheck_mu(struct NaClApp *nap,
uint32_t addr_first_usr,
uint32_t addr_last_usr) {
if ((*nap->mem_io_regions->vtbl->OverlapsWith)(nap->mem_io_regions,
addr_first_usr,
addr_last_usr)) {
NaClLog(LOG_FATAL,
"NaClVmIoWillStart: program mem write race detected. ABORTING\n");
}
}
/*
* GDB's canonical overlay managment routine.
* We need its symbol in the symbol table so don't inline it.
* TODO(dje): add some explanation for the non-GDB person.
*/
#if NACL_WINDOWS
__declspec(dllexport noinline)
#endif
#ifdef __GNUC__
__attribute__((noinline))
#endif
void _ovly_debug_event (void) {
#ifdef __GNUC__
/*
* The asm volatile is here as instructed by the GCC docs.
* It's not enough to declare a function noinline.
* GCC will still look inside the function to see if it's worth calling.
*/
__asm__ volatile ("");
#elif NACL_WINDOWS
/*
* Visual Studio inlines empty functions even with noinline attribute,
* so we need a compile memory barrier to make this function not to be
* inlined. Also, it guarantees that nacl_global_xlate_base initialization
* is not reordered. This is important for gdb since it sets breakpoint on
* this function and reads nacl_global_xlate_base value.
*/
_ReadWriteBarrier();
#endif
}
static void StopForDebuggerInit (uintptr_t mem_start) {
/* Put xlate_base in a place where gdb can find it. */
nacl_global_xlate_base = mem_start;
NaClSandboxMemoryStartForValgrind(mem_start);
_ovly_debug_event();
}
void NaClGdbHook(struct NaClApp const *nap) {
StopForDebuggerInit(nap->mem_start);
}