blob: ab98264e4f6d677f94bf167a1eebb07391153f99 [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.
*/
/*
* NaCl Server Runtime user thread state.
*/
#include <string.h>
#include "native_client/src/include/build_config.h"
#include "native_client/src/shared/platform/aligned_malloc.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_sync_checked.h"
#include "native_client/src/trusted/service_runtime/arch/sel_ldr_arch.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_tls.h"
#include "native_client/src/trusted/service_runtime/nacl_switch_to_app.h"
#include "native_client/src/trusted/service_runtime/nacl_stack_safety.h"
#include "native_client/src/trusted/service_runtime/nacl_syscall_common.h"
#include "native_client/src/trusted/service_runtime/osx/mach_thread_map.h"
#if NACL_OSX
#include <sys/sysctl.h>
static int IsTCSMAvailable(void) {
/* Check availability of TCSM in the kernel */
uint32_t current_val = 0;
size_t current_val_size = sizeof(current_val);
int rv = sysctlbyname("kern.tcsm_available",
&current_val, &current_val_size, NULL, 0);
return 0 <= rv && 0 != current_val;
}
static void EnableTCSM(void) {
/* Opt this thread into TCSM */
uint32_t new_val = 1;
int rv = sysctlbyname("kern.tcsm_enable",
NULL, NULL, &new_val, sizeof(new_val));
DCHECK(0 <= rv);
}
#endif
void WINAPI NaClAppThreadLauncher(void *state) {
struct NaClAppThread *natp = (struct NaClAppThread *) state;
uint32_t thread_idx;
NaClLog(4, "NaClAppThreadLauncher: entered\n");
NaClSignalStackRegister(natp->signal_stack);
NaClLog(4, " natp = 0x%016"NACL_PRIxPTR"\n", (uintptr_t) natp);
NaClLog(4, " prog_ctr = 0x%016"NACL_PRIxNACL_REG"\n", natp->user.prog_ctr);
NaClLog(4, "stack_ptr = 0x%016"NACL_PRIxPTR"\n",
NaClGetThreadCtxSp(&natp->user));
thread_idx = NaClGetThreadIdx(natp);
CHECK(0 < thread_idx);
CHECK(thread_idx < NACL_THREAD_MAX);
NaClTlsSetCurrentThread(natp);
nacl_user[thread_idx] = &natp->user;
#if NACL_WINDOWS
nacl_thread_ids[thread_idx] = GetCurrentThreadId();
#elif NACL_OSX
if (IsTCSMAvailable()) {
EnableTCSM();
}
NaClSetCurrentMachThreadForThreadIndex(thread_idx);
#endif
/*
* We have to hold the threads_mu lock until after thread_num field
* in this thread has been initialized. All other threads can only
* find and examine this natp through the threads table, so the fact
* that natp is not consistent (no thread_num) will not be visible.
*/
NaClXMutexLock(&natp->nap->threads_mu);
natp->thread_num = NaClAddThreadMu(natp->nap, natp);
NaClXMutexUnlock(&natp->nap->threads_mu);
NaClVmHoleThreadStackIsSafe(natp->nap);
NaClStackSafetyNowOnUntrustedStack();
/*
* Notify the debug stub, that a new thread is availible.
*/
if (NULL != natp->nap->debug_stub_callbacks) {
natp->nap->debug_stub_callbacks->thread_create_hook(natp);
}
/*
* After this NaClAppThreadSetSuspendState() call, we should not
* claim any mutexes, otherwise we risk deadlock.
*/
NaClAppThreadSetSuspendState(natp, NACL_APP_THREAD_TRUSTED,
NACL_APP_THREAD_UNTRUSTED);
NaClStartThreadInApp(natp, natp->user.prog_ctr);
}
/*
* natp should be thread_self(), called while holding no locks.
*/
void NaClAppThreadTeardown(struct NaClAppThread *natp) {
struct NaClApp *nap;
size_t thread_idx;
/*
* mark this thread as dead; doesn't matter if some other thread is
* asking us to commit suicide.
*/
NaClLog(3, "NaClAppThreadTeardown(0x%08"NACL_PRIxPTR")\n",
(uintptr_t) natp);
nap = natp->nap;
if (NULL != nap->debug_stub_callbacks) {
NaClLog(3, " notifying the debug stub of the thread exit\n");
/*
* This must happen before deallocating the ID natp->thread_num.
* We have the invariant that debug stub lock should be acquired before
* nap->threads_mu lock. Hence we must not hold threads_mu lock while
* calling debug stub hooks.
*/
nap->debug_stub_callbacks->thread_exit_hook(natp);
}
NaClLog(3, " getting thread table lock\n");
NaClXMutexLock(&nap->threads_mu);
NaClLog(3, " getting thread lock\n");
NaClXMutexLock(&natp->mu);
/*
* Remove ourselves from the ldt-indexed global tables. The ldt
* entry is released as part of NaClAppThreadDelete(), and if
* another thread is immediately created (from some other running
* thread) we want to be sure that any ldt-based lookups will not
* reach this dying thread's data.
*/
thread_idx = NaClGetThreadIdx(natp);
/*
* On x86-64 and ARM, clearing nacl_user entry ensures that we will
* fault if another syscall is made with this thread_idx. In
* particular, thread_idx 0 is never used.
*/
nacl_user[thread_idx] = NULL;
#if NACL_WINDOWS
nacl_thread_ids[thread_idx] = 0;
#elif NACL_OSX
NaClClearMachThreadForThreadIndex(thread_idx);
#endif
/*
* Unset the TLS variable so that if a crash occurs during thread
* teardown, the signal handler does not dereference a dangling
* NaClAppThread pointer.
*/
NaClTlsSetCurrentThread(NULL);
NaClLog(3, " removing thread from thread table\n");
/* Deallocate the ID natp->thread_num. */
NaClRemoveThreadMu(nap, natp->thread_num);
NaClLog(3, " unlocking thread\n");
NaClXMutexUnlock(&natp->mu);
NaClLog(3, " unlocking thread table\n");
NaClXMutexUnlock(&nap->threads_mu);
NaClLog(3, " unregistering signal stack\n");
NaClSignalStackUnregister();
NaClLog(3, " freeing thread object\n");
NaClAppThreadDelete(natp);
NaClLog(3, " NaClThreadExit\n");
NaClThreadExit();
NaClLog(LOG_FATAL,
"NaClAppThreadTeardown: NaClThreadExit() should not return\n");
/* NOTREACHED */
}
struct NaClAppThread *NaClAppThreadMake(struct NaClApp *nap,
uintptr_t usr_entry,
uintptr_t usr_stack_ptr,
uint32_t user_tls1,
uint32_t user_tls2) {
struct NaClAppThread *natp;
natp = NaClAlignedMalloc(sizeof *natp, __alignof(struct NaClAppThread));
if (natp == NULL) {
return NULL;
}
NaClLog(4, " natp = 0x%016"NACL_PRIxPTR"\n", (uintptr_t) natp);
NaClLog(4, " nap = 0x%016"NACL_PRIxPTR"\n", (uintptr_t) nap);
NaClLog(4, "usr_stack_ptr = 0x%016"NACL_PRIxPTR"\n", usr_stack_ptr);
/*
* Set these early, in case NaClTlsAllocate() wants to examine them.
*/
natp->nap = nap;
natp->thread_num = -1; /* illegal index */
natp->host_thread_is_defined = 0;
memset(&natp->host_thread, 0, sizeof(natp->host_thread));
if (!NaClAppThreadInitArchSpecific(natp, usr_entry, usr_stack_ptr)) {
goto cleanup_free;
}
NaClTlsSetTlsValue1(natp, user_tls1);
NaClTlsSetTlsValue2(natp, user_tls2);
natp->signal_stack = NULL;
natp->exception_stack = 0;
natp->exception_flag = 0;
if (!NaClMutexCtor(&natp->mu)) {
goto cleanup_free;
}
if (!NaClSignalStackAllocate(&natp->signal_stack)) {
goto cleanup_mu;
}
if (!NaClMutexCtor(&natp->suspend_mu)) {
goto cleanup_mu;
}
natp->suspend_state = NACL_APP_THREAD_TRUSTED;
natp->suspended_registers = NULL;
natp->fault_signal = 0;
natp->dynamic_delete_generation = 0;
if (!NaClCondVarCtor(&natp->futex_condvar)) {
goto cleanup_suspend_mu;
}
return natp;
cleanup_suspend_mu:
NaClMutexDtor(&natp->suspend_mu);
cleanup_mu:
NaClMutexDtor(&natp->mu);
if (NULL != natp->signal_stack) {
NaClSignalStackFree(&natp->signal_stack);
natp->signal_stack = NULL;
}
cleanup_free:
NaClAlignedFree(natp);
return NULL;
}
int NaClAppThreadSpawn(struct NaClApp *nap,
uintptr_t usr_entry,
uintptr_t usr_stack_ptr,
uint32_t user_tls1,
uint32_t user_tls2) {
struct NaClAppThread *natp = NaClAppThreadMake(nap, usr_entry, usr_stack_ptr,
user_tls1, user_tls2);
if (natp == NULL) {
return 0;
}
/*
* We set host_thread_is_defined assuming, for now, that
* NaClThreadCtor() will succeed.
*/
natp->host_thread_is_defined = 1;
if (!NaClThreadCtor(&natp->host_thread, NaClAppThreadLauncher, (void *) natp,
NACL_KERN_STACK_SIZE)) {
/*
* No other thread saw the NaClAppThread, so it is OK that
* host_thread was not initialized despite host_thread_is_defined
* being set.
*/
natp->host_thread_is_defined = 0;
NaClAppThreadDelete(natp);
return 0;
}
return 1;
}
void NaClAppThreadDelete(struct NaClAppThread *natp) {
/*
* the thread must not be still running, else this crashes the system
*/
if (natp->host_thread_is_defined) {
NaClThreadDtor(&natp->host_thread);
}
free(natp->suspended_registers);
NaClMutexDtor(&natp->suspend_mu);
NaClSignalStackFree(natp->signal_stack);
natp->signal_stack = NULL;
NaClCondVarDtor(&natp->futex_condvar);
NaClTlsFree(natp);
NaClMutexDtor(&natp->mu);
NaClAlignedFree(natp);
}