Index: components/nacl/loader/nonsfi/irt_thread.cc |
diff --git a/components/nacl/loader/nonsfi/irt_thread.cc b/components/nacl/loader/nonsfi/irt_thread.cc |
index b7f5c9a7e03c7dd708f630acc3f652260c4c13f4..c47eabdaba5d3acb47c527e0b3bd553616c79baa 100644 |
--- a/components/nacl/loader/nonsfi/irt_thread.cc |
+++ b/components/nacl/loader/nonsfi/irt_thread.cc |
@@ -4,13 +4,14 @@ |
#include <errno.h> |
#include <pthread.h> |
+#include <signal.h> |
#include <stdlib.h> |
+#include <sys/mman.h> |
#include "base/logging.h" |
#include "base/macros.h" |
#include "base/memory/scoped_ptr.h" |
#include "components/nacl/loader/nonsfi/irt_interfaces.h" |
-#include "native_client/src/trusted/service_runtime/nacl_signal.h" |
namespace nacl { |
namespace nonsfi { |
@@ -44,6 +45,54 @@ struct ThreadContext { |
// reset via IrtTlsInit(). The pointer can be obtained via IrtTlsGet(). |
__thread void* g_thread_ptr; |
+const int kSignalStackSize = SIGSTKSZ + 4096; |
+const int kNonSfiPageSize = 4096; // really? |
+const int kStackGuardSize = kNonSfiPageSize; |
+ |
+void NaClSignalStackRegister(void *stack) { |
+ /* |
+ * If we set up signal handlers, we must ensure that any thread that |
+ * runs untrusted code has an alternate signal stack set up. The |
+ * default for a new thread is to use the stack pointer from the |
+ * point at which the fault occurs, but it would not be safe to use |
+ * untrusted code's %esp/%rsp value. |
+ */ |
+ stack_t st; |
+ st.ss_size = kSignalStackSize; |
+ st.ss_sp = ((uint8_t *) stack) + kStackGuardSize; |
+ st.ss_flags = 0; |
+ if (sigaltstack(&st, NULL) != 0) { |
+ PLOG(FATAL) << "Failed to register signal stack"; |
+ } |
+} |
+ |
+int NaClSignalStackAllocate(void **result) { |
+ /* |
+ * We use mmap() to allocate the signal stack for two reasons: |
+ * |
+ * 1) By page-aligning the memory allocation (which malloc() does |
+ * not do for small allocations), we avoid allocating any real |
+ * memory in the common case in which the signal handler is never |
+ * run. |
+ * |
+ * 2) We get to create a guard page, to guard against the unlikely |
+ * occurrence of the signal handler both overrunning and doing so in |
+ * an exploitable way. |
+ */ |
+ void *stack = mmap(NULL, kSignalStackSize + kStackGuardSize, |
+ PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANON, |
+ -1, 0); |
+ if (stack == MAP_FAILED) { |
+ return 0; |
+ } |
+ /* We assume that the stack grows downwards. */ |
+ if (mprotect(stack, kStackGuardSize, PROT_NONE) != 0) { |
+ PLOG(FATAL) << "Failed to mprotect() the stack guard page"; |
+ } |
+ *result = stack; |
+ return 1; |
+} |
+ |
void* ThreadMain(void *arg) { |
::scoped_ptr<ThreadContext> context(static_cast<ThreadContext*>(arg)); |
g_thread_ptr = context->thread_ptr; |
@@ -94,11 +143,40 @@ int IrtThreadCreate(void (*start_func)(), void* stack, void* thread_ptr) { |
return 0; |
} |
+void* NaClSignalStackUnregister(void) { |
+ /* |
+ * Unregister the signal stack in case a fault occurs between the |
+ * thread deallocating the signal stack and exiting. Such a fault |
+ * could be unsafe if the address space were reallocated before the |
+ * fault, although that is unlikely. |
+ */ |
+ stack_t st; |
+ st.ss_size = 0; |
+ st.ss_sp = NULL; |
+ st.ss_flags = SS_DISABLE; |
+ stack_t old_stack; |
+ if (sigaltstack(&st, &old_stack) != 0) { |
+ PLOG(FATAL) << "Failed to unregister signal stack."; |
+ } |
+ return old_stack.ss_sp; |
+} |
+ |
+void NaClSignalStackFree(void *stack) { |
+ CHECK(stack != NULL); |
+ if (munmap(stack, kSignalStackSize + kStackGuardSize) != 0) { |
+ PLOG(FATAL) << "Failed to munmap() signal stack."; |
+ } |
+} |
+ |
void IrtThreadExit(int32_t* stack_flag) { |
// As we actually don't use stack given to thread_create, it means that the |
// memory can be released whenever. |
if (stack_flag) |
*stack_flag = 0; |
+ NaClSignalStackUnregister(); |
+ // TODO: How do I know altstack address ? Should I trust sigaltstack |
+ // return value ? |
+ // NaClSignalStackFree(signal_stack); |
pthread_exit(NULL); |
} |