Follow @Openwall on Twitter for new release announcements and other news
[<prev] [next>] [day] [month] [year] [list]
Date: Fri, 4 Nov 2016 20:33:21 +0000
From: "LeMay, Michael" <michael.lemay@...el.com>
To: "musl@...ts.openwall.com" <musl@...ts.openwall.com>
Subject: [RFC PATCH v3 1/2] support SafeStack

This patch adds storage in the thread control block for the unsafe stack pointer
and safe stack metadata for that thread when SafeStack is enabled.  It modifies
the libc and dynamic linker initialization routines and thread creation routines
to allocate and initialize both safe and unsafe stacks.  Likewise, it modifies
the thread destruction routine to unmap the safe stack.

The check for buggy brk implementations relies on comparing the addresses of
stack-allocated objects to addresses returned by the brk syscall.  SafeStack
moves the allocations to the unsafe stack, breaking the check.  This patch
disables the check when SafeStack is enabled.

Signed-off-by: Michael LeMay <michael.lemay@...el.com>
---
 ldso/dynlink.c              |  13 +++++
 src/env/__libc_start_main.c |   5 ++
 src/internal/pthread_impl.h |  13 +++++
 src/internal/safe_stack.c   | 128 ++++++++++++++++++++++++++++++++++++++++++++
 src/malloc/expand_heap.c    |   2 +
 src/thread/pthread_create.c |  14 ++++-
 6 files changed, 174 insertions(+), 1 deletion(-)
 create mode 100644 src/internal/safe_stack.c

diff --git a/ldso/dynlink.c b/ldso/dynlink.c
index e458f38..c203c6b 100644
--- a/ldso/dynlink.c
+++ b/ldso/dynlink.c
@@ -99,6 +99,9 @@ int __init_tp(void *);
 void __init_libc(char **, char *);
 void *__copy_tls(unsigned char *);
 
+void __preinit_unsafe_stack(void);
+void __init_unsafe_stack(void);
+
 __attribute__((__visibility__("hidden")))
 const char *__libc_get_version(void);
 
@@ -1324,9 +1327,14 @@ static void update_tls_size()
  * linker itself, but some of the relocations performed may need to be
  * replaced later due to copy relocations in the main program. */
 
+#if SAFE_STACK
+__attribute__((no_sanitize("safe-stack")))
+#endif
 __attribute__((__visibility__("hidden")))
 void __dls2(unsigned char *base, size_t *sp)
 {
+	__preinit_unsafe_stack();
+
 	if (DL_FDPIC) {
 		void *p1 = (void *)sp[-2];
 		void *p2 = (void *)sp[-1];
@@ -1388,6 +1396,9 @@ void __dls2(unsigned char *base, size_t *sp)
  * process dependencies and relocations for the main application and
  * transfer control to its entry point. */
 
+#if SAFE_STACK
+__attribute__((no_sanitize("safe-stack")))
+#endif
 _Noreturn void __dls3(size_t *sp)
 {
 	static struct dso app, vdso;
@@ -1420,6 +1431,8 @@ _Noreturn void __dls3(size_t *sp)
 		a_crash();
 	}
 
+	__init_unsafe_stack();
+
 	/* Only trust user/env if kernel says we're not suid/sgid */
 	if (!libc.secure) {
 		env_path = getenv("LD_LIBRARY_PATH");
diff --git a/src/env/__libc_start_main.c b/src/env/__libc_start_main.c
index 5c79be2..dfb4ebb 100644
--- a/src/env/__libc_start_main.c
+++ b/src/env/__libc_start_main.c
@@ -17,6 +17,9 @@ extern void (*const __init_array_start)(void), (*const __init_array_end)(void);
 static void dummy1(void *p) {}
 weak_alias(dummy1, __init_ssp);
 
+void __preinit_unsafe_stack(void);
+void __init_unsafe_stack(void);
+
 #define AUX_CNT 38
 
 void __init_libc(char **envp, char *pn)
@@ -36,6 +39,7 @@ void __init_libc(char **envp, char *pn)
 	}
 
 	__init_tls(aux);
+	__init_unsafe_stack();
 	__init_ssp((void *)aux[AT_RANDOM]);
 
 	if (aux[AT_UID]==aux[AT_EUID] && aux[AT_GID]==aux[AT_EGID]
@@ -67,6 +71,7 @@ int __libc_start_main(int (*main)(int,char **,char **), int argc, char **argv)
 {
 	char **envp = argv+argc+1;
 
+	__preinit_unsafe_stack();
 	__init_libc(envp, argv[0]);
 	__libc_start_init();
 
diff --git a/src/internal/pthread_impl.h b/src/internal/pthread_impl.h
index 3890bb5..8c68533 100644
--- a/src/internal/pthread_impl.h
+++ b/src/internal/pthread_impl.h
@@ -18,6 +18,19 @@ struct pthread {
 	uintptr_t sysinfo;
 	uintptr_t canary, canary2;
 	pid_t tid, pid;
+#if SAFE_STACK
+	/* offset of unsafe_stack_ptr must be 0x24 for 32-bit targets
+	 * and 0x48 for 64-bit targets */
+#if LONG_MAX >> 32 == 0
+	void *unsafe_stack_ptr;
+	void *safe_stack_base;
+#else
+	void *safe_stack_base;
+	void *unsafe_stack_ptr;
+#endif
+	/* size of safe stack allocation, including the guard */
+	size_t safe_stack_size;
+#endif
 	int tsd_used, errno_val;
 	volatile int cancel, canceldisable, cancelasync;
 	int detached;
diff --git a/src/internal/safe_stack.c b/src/internal/safe_stack.c
new file mode 100644
index 0000000..86804aa
--- /dev/null
+++ b/src/internal/safe_stack.c
@@ -0,0 +1,128 @@
+#define _GNU_SOURCE
+#include "pthread_impl.h"
+#include <stdbool.h>
+#include <stdint.h>
+#include <sys/mman.h>
+
+#if SAFE_STACK
+
+static bool unsafe_stack_ptr_inited = false;
+
+void *__mmap(void *, size_t, int, int, int, off_t);
+int __munmap(void *, size_t);
+int __mprotect(void *, size_t, int);
+
+/* There are no checks for overflows past the end of this stack buffer.  It must
+ * be allocated with adequate space to meet the requirements of all of the code
+ * that runs prior to __init_unsafe_stack allocating a new unsafe stack.  This
+ * buffer is not used after that. */
+static unsigned char preinit_us[4096];
+
+__attribute__((__visibility__("hidden")))
+void __init_unsafe_stack(void)
+{
+	void *stack_base;
+	size_t stack_size;
+	pthread_attr_t attr;
+	struct pthread *self;
+
+	if (unsafe_stack_ptr_inited)
+		return;
+
+	self = __pthread_self();
+
+	/* Set the unsafe stack pointer in the current TCB to the statically-allocated
+	 * unsafe stack, since some of the subroutines invoked below may use the
+	 * unsafe stack. */
+	self->unsafe_stack_ptr = preinit_us + sizeof(preinit_us);
+
+	if (pthread_getattr_np(self, &attr) != 0)
+		a_crash();
+
+	if (pthread_attr_getstack(&attr, &stack_base, &stack_size) != 0)
+		a_crash();
+
+	stack_size *= 2;
+
+	/* This mapping is not reclaimed until the process exits. */
+	uint8_t *unsafe_stack = __mmap(0, stack_size, PROT_NONE, MAP_PRIVATE|MAP_ANON, -1, 0);
+	if (unsafe_stack == MAP_FAILED)
+		a_crash();
+
+	unsafe_stack += DEFAULT_GUARD_SIZE;
+	stack_size -= DEFAULT_GUARD_SIZE;
+
+	if (__mprotect(unsafe_stack, stack_size, PROT_READ|PROT_WRITE)
+	    && errno != ENOSYS)
+		a_crash();
+
+	self->unsafe_stack_ptr = unsafe_stack + stack_size;
+
+	unsafe_stack_ptr_inited = true;
+}
+
+static struct pthread preinit_tcb = {
+	.unsafe_stack_ptr = preinit_us + sizeof(preinit_us)
+};
+
+/* Install a TCB with just the unsafe stack pointer initialized. */
+__attribute__((__visibility__("hidden")))
+void __preinit_unsafe_stack(void)
+{
+	if (unsafe_stack_ptr_inited)
+		return;
+
+	__set_thread_area(&preinit_tcb);
+}
+
+#define ROUND(x) (((x)+PAGE_SIZE-1)&-PAGE_SIZE)
+
+__attribute__((__visibility__("hidden")))
+int __safestack_init_thread(struct pthread *restrict new, const pthread_attr_t *restrict attr)
+{
+	size_t size, guard;
+	unsigned char *map = 0;
+
+	new->unsafe_stack_ptr = new->stack;
+
+	guard = ROUND(DEFAULT_GUARD_SIZE + attr->_a_guardsize);
+	size = ROUND(new->stack_size + guard);
+
+	map = __mmap(0, size, PROT_NONE, MAP_PRIVATE|MAP_ANON, -1, 0);
+	if (map == MAP_FAILED)
+		goto fail;
+	if (__mprotect(map+guard, size-guard, PROT_READ|PROT_WRITE)
+	    && errno != ENOSYS) {
+		__munmap(map, size);
+		goto fail;
+	}
+
+	new->safe_stack_base = map;
+	new->safe_stack_size = size;
+	new->stack = map + size;
+
+	return 0;
+fail:
+	return EAGAIN;
+}
+
+void __safestack_pthread_exit(struct pthread *self)
+{
+	if (self->detached && self->safe_stack_base)
+		__munmap(self->safe_stack_base, self->safe_stack_size);
+}
+
+#else /*SAFE_STACK*/
+
+static void dummy(void) {}
+weak_alias(dummy, __preinit_unsafe_stack);
+weak_alias(dummy, __init_unsafe_stack);
+int dummy1(struct pthread *restrict thr, const pthread_attr_t *restrict attr)
+{
+	return 0;
+}
+weak_alias(dummy1, __safestack_init_thread);
+void dummy2(struct pthread *self) {}
+weak_alias(dummy2, __safestack_pthread_exit);
+
+#endif /*SAFE_STACK*/
diff --git a/src/malloc/expand_heap.c b/src/malloc/expand_heap.c
index d8c0be7..ace2b6a 100644
--- a/src/malloc/expand_heap.c
+++ b/src/malloc/expand_heap.c
@@ -13,6 +13,7 @@
 
 static int traverses_stack_p(uintptr_t old, uintptr_t new)
 {
+#if !SAFE_STACK
 	const uintptr_t len = 8<<20;
 	uintptr_t a, b;
 
@@ -23,6 +24,7 @@ static int traverses_stack_p(uintptr_t old, uintptr_t new)
 	b = (uintptr_t)&b;
 	a = b > len ? b-len : 0;
 	if (new>a && old<b) return 1;
+#endif
 
 	return 0;
 }
diff --git a/src/thread/pthread_create.c b/src/thread/pthread_create.c
index 9f6b98e..debf54e 100644
--- a/src/thread/pthread_create.c
+++ b/src/thread/pthread_create.c
@@ -10,6 +10,8 @@ void *__mmap(void *, size_t, int, int, int, off_t);
 int __munmap(void *, size_t);
 int __mprotect(void *, size_t, int);
 
+void __safestack_pthread_exit(struct pthread *self);
+
 static void dummy_0()
 {
 }
@@ -24,6 +26,8 @@ _Noreturn void __pthread_exit(void *result)
 	pthread_t self = __pthread_self();
 	sigset_t set;
 
+	__safestack_pthread_exit(self);
+
 	self->canceldisable = 1;
 	self->cancelasync = 0;
 	self->result = result;
@@ -176,6 +180,8 @@ static void init_file_lock(FILE *f)
 
 void *__copy_tls(unsigned char *);
 
+int __safestack_init_thread(struct pthread *restrict, const pthread_attr_t *restrict);
+
 int __pthread_create(pthread_t *restrict res, const pthread_attr_t *restrict attrp, void *(*entry)(void *), void *restrict arg)
 {
 	int ret, c11 = (attrp == __ATTRP_C11_THREAD);
@@ -269,9 +275,15 @@ int __pthread_create(pthread_t *restrict res, const pthread_attr_t *restrict att
 	new->robust_list.head = &new->robust_list.head;
 	new->unblock_cancel = self->cancel;
 	new->CANARY = self->CANARY;
+	ret = __safestack_init_thread(new, &attr);
+	if (ret != 0) {
+		if (map) __munmap(map, size);
+		__release_ptc();
+		return ret;
+	}
 
 	a_inc(&libc.threads_minus_1);
-	ret = __clone((c11 ? start_c11 : start), stack, flags, new, &new->tid, TP_ADJ(new), &new->tid);
+	ret = __clone((c11 ? start_c11 : start), new->stack, flags, new, &new->tid, TP_ADJ(new), &new->tid);
 
 	__release_ptc();
 
-- 
2.7.4

Powered by blists - more mailing lists

Confused about mailing lists and their use? Read about mailing lists on Wikipedia and check out these guidelines on proper formatting of your messages.