From mboxrd@z Thu Jan 1 00:00:00 1970 X-Msuck: nntp://news.gmane.org/gmane.linux.lib.musl.general/10705 Path: news.gmane.org!.POSTED!not-for-mail From: "LeMay, Michael" Newsgroups: gmane.linux.lib.musl.general Subject: [RFC PATCH v3 1/2] support SafeStack Date: Fri, 4 Nov 2016 20:33:21 +0000 Message-ID: <390CE752059EB848A71F4F676EBAB76D3AC2988F@ORSMSX114.amr.corp.intel.com> Reply-To: musl@lists.openwall.com NNTP-Posting-Host: blaine.gmane.org Mime-Version: 1.0 Content-Type: text/plain; charset="us-ascii" Content-Transfer-Encoding: quoted-printable X-Trace: blaine.gmane.org 1478291631 26604 195.159.176.226 (4 Nov 2016 20:33:51 GMT) X-Complaints-To: usenet@blaine.gmane.org NNTP-Posting-Date: Fri, 4 Nov 2016 20:33:51 +0000 (UTC) To: "musl@lists.openwall.com" Original-X-From: musl-return-10718-gllmg-musl=m.gmane.org@lists.openwall.com Fri Nov 04 21:33:47 2016 Return-path: Envelope-to: gllmg-musl@m.gmane.org Original-Received: from mother.openwall.net ([195.42.179.200]) by blaine.gmane.org with smtp (Exim 4.84_2) (envelope-from ) id 1c2lB9-00050C-O2 for gllmg-musl@m.gmane.org; Fri, 04 Nov 2016 21:33:35 +0100 Original-Received: (qmail 29763 invoked by uid 550); 4 Nov 2016 20:33:37 -0000 Mailing-List: contact musl-help@lists.openwall.com; run by ezmlm Precedence: bulk List-Post: List-Help: List-Unsubscribe: List-Subscribe: List-ID: Original-Received: (qmail 29731 invoked from network); 4 Nov 2016 20:33:35 -0000 X-ExtLoop1: 1 X-IronPort-AV: E=Sophos;i="5.31,445,1473145200"; d="scan'208";a="27540077" Thread-Topic: [RFC PATCH v3 1/2] support SafeStack Thread-Index: AdI22qAmyWifAInzQBa+R0FNOn2Tqw== Accept-Language: en-US Content-Language: en-US X-MS-Has-Attach: X-MS-TNEF-Correlator: x-originating-ip: [10.22.254.140] Xref: news.gmane.org gmane.linux.lib.musl.general:10705 Archived-At: This patch adds storage in the thread control block for the unsafe stack po= inter and safe stack metadata for that thread when SafeStack is enabled. It modi= fies the libc and dynamic linker initialization routines and thread creation rou= tines to allocate and initialize both safe and unsafe stacks. Likewise, it modif= ies the thread destruction routine to unmap the safe stack. The check for buggy brk implementations relies on comparing the addresses o= f stack-allocated objects to addresses returned by the brk syscall. SafeStac= k moves the allocations to the unsafe stack, breaking the check. This patch disables the check when SafeStack is enabled. Signed-off-by: Michael LeMay --- 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 *); =20 +void __preinit_unsafe_stack(void); +void __init_unsafe_stack(void); + __attribute__((__visibility__("hidden"))) const char *__libc_get_version(void); =20 @@ -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. */ =20 +#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 =3D (void *)sp[-2]; void *p2 =3D (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. */ =20 +#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(); } =20 + __init_unsafe_stack(); + /* Only trust user/env if kernel says we're not suid/sgid */ if (!libc.secure) { env_path =3D 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); =20 +void __preinit_unsafe_stack(void); +void __init_unsafe_stack(void); + #define AUX_CNT 38 =20 void __init_libc(char **envp, char *pn) @@ -36,6 +39,7 @@ void __init_libc(char **envp, char *pn) } =20 __init_tls(aux); + __init_unsafe_stack(); __init_ssp((void *)aux[AT_RANDOM]); =20 if (aux[AT_UID]=3D=3Daux[AT_EUID] && aux[AT_GID]=3D=3Daux[AT_EGID] @@ -67,6 +71,7 @@ int __libc_start_main(int (*main)(int,char **,char **), i= nt argc, char **argv) { char **envp =3D argv+argc+1; =20 + __preinit_unsafe_stack(); __init_libc(envp, argv[0]); __libc_start_init(); =20 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 =3D=3D 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 +#include +#include + +#if SAFE_STACK + +static bool unsafe_stack_ptr_inited =3D 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. I= t 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 =3D __pthread_self(); + + /* Set the unsafe stack pointer in the current TCB to the statically-allo= cated + * unsafe stack, since some of the subroutines invoked below may use the + * unsafe stack. */ + self->unsafe_stack_ptr =3D preinit_us + sizeof(preinit_us); + + if (pthread_getattr_np(self, &attr) !=3D 0) + a_crash(); + + if (pthread_attr_getstack(&attr, &stack_base, &stack_size) !=3D 0) + a_crash(); + + stack_size *=3D 2; + + /* This mapping is not reclaimed until the process exits. */ + uint8_t *unsafe_stack =3D __mmap(0, stack_size, PROT_NONE, MAP_PRIVATE|MA= P_ANON, -1, 0); + if (unsafe_stack =3D=3D MAP_FAILED) + a_crash(); + + unsafe_stack +=3D DEFAULT_GUARD_SIZE; + stack_size -=3D DEFAULT_GUARD_SIZE; + + if (__mprotect(unsafe_stack, stack_size, PROT_READ|PROT_WRITE) + && errno !=3D ENOSYS) + a_crash(); + + self->unsafe_stack_ptr =3D unsafe_stack + stack_size; + + unsafe_stack_ptr_inited =3D true; +} + +static struct pthread preinit_tcb =3D { + .unsafe_stack_ptr =3D 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_at= tr_t *restrict attr) +{ + size_t size, guard; + unsigned char *map =3D 0; + + new->unsafe_stack_ptr =3D new->stack; + + guard =3D ROUND(DEFAULT_GUARD_SIZE + attr->_a_guardsize); + size =3D ROUND(new->stack_size + guard); + + map =3D __mmap(0, size, PROT_NONE, MAP_PRIVATE|MAP_ANON, -1, 0); + if (map =3D=3D MAP_FAILED) + goto fail; + if (__mprotect(map+guard, size-guard, PROT_READ|PROT_WRITE) + && errno !=3D ENOSYS) { + __munmap(map, size); + goto fail; + } + + new->safe_stack_base =3D map; + new->safe_stack_size =3D size; + new->stack =3D 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 at= tr) +{ + 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 @@ =20 static int traverses_stack_p(uintptr_t old, uintptr_t new) { +#if !SAFE_STACK const uintptr_t len =3D 8<<20; uintptr_t a, b; =20 @@ -23,6 +24,7 @@ static int traverses_stack_p(uintptr_t old, uintptr_t new= ) b =3D (uintptr_t)&b; a =3D b > len ? b-len : 0; if (new>a && oldcanceldisable =3D 1; self->cancelasync =3D 0; self->result =3D result; @@ -176,6 +180,8 @@ static void init_file_lock(FILE *f) =20 void *__copy_tls(unsigned char *); =20 +int __safestack_init_thread(struct pthread *restrict, const pthread_attr_t= *restrict); + int __pthread_create(pthread_t *restrict res, const pthread_attr_t *restri= ct attrp, void *(*entry)(void *), void *restrict arg) { int ret, c11 =3D (attrp =3D=3D __ATTRP_C11_THREAD); @@ -269,9 +275,15 @@ int __pthread_create(pthread_t *restrict res, const pt= hread_attr_t *restrict att new->robust_list.head =3D &new->robust_list.head; new->unblock_cancel =3D self->cancel; new->CANARY =3D self->CANARY; + ret =3D __safestack_init_thread(new, &attr); + if (ret !=3D 0) { + if (map) __munmap(map, size); + __release_ptc(); + return ret; + } =20 a_inc(&libc.threads_minus_1); - ret =3D __clone((c11 ? start_c11 : start), stack, flags, new, &new->tid, = TP_ADJ(new), &new->tid); + ret =3D __clone((c11 ? start_c11 : start), new->stack, flags, new, &new->= tid, TP_ADJ(new), &new->tid); =20 __release_ptc(); =20 --=20 2.7.4