diff options
author | 2018-04-12 17:15:34 +0000 | |
---|---|---|
committer | 2018-04-12 17:15:34 +0000 | |
commit | 2d9f5a384f759e3c7fde095c26d4d6ddb18a90e4 (patch) | |
tree | ab58e4371c5126adc7f59bc8f1eeb1020f2383d0 /lib | |
parent | Implement MAP_STACK option for mmap(). Synchronous faults (pagefault and (diff) | |
download | wireguard-openbsd-2d9f5a384f759e3c7fde095c26d4d6ddb18a90e4.tar.xz wireguard-openbsd-2d9f5a384f759e3c7fde095c26d4d6ddb18a90e4.zip |
(file missed from previous commit)
Implement MAP_STACK option for mmap(). Synchronous faults (pagefault and
syscall) confirm the stack register points at MAP_STACK memory, otherwise
SIGSEGV is delivered. sigaltstack() and pthread_attr_setstack() are modified
to create a MAP_STACK sub-region which satisfies alignment requirements.
Observe that MAP_STACK can only be set/cleared by mmap(), which zeroes the
contents of the region -- there is no mprotect() equivalent operation, so
there is no MAP_STACK-adding gadget.
This opportunistic software-emulation of a stack protection bit makes
stack-pivot operations during ROPchain fragile (kind of like removing a
tool from the toolbox).
original discussion with tedu, uvm work by stefan, testing by mortimer
Diffstat (limited to 'lib')
-rw-r--r-- | lib/librthread/rthread_attr.c | 52 |
1 files changed, 48 insertions, 4 deletions
diff --git a/lib/librthread/rthread_attr.c b/lib/librthread/rthread_attr.c index ceba9e8fb8a..2ffe05ffc2b 100644 --- a/lib/librthread/rthread_attr.c +++ b/lib/librthread/rthread_attr.c @@ -1,4 +1,4 @@ -/* $OpenBSD: rthread_attr.c,v 1.23 2017/09/05 02:40:54 guenther Exp $ */ +/* $OpenBSD: rthread_attr.c,v 1.24 2018/04/12 17:15:34 deraadt Exp $ */ /* * Copyright (c) 2004,2005 Ted Unangst <tedu@openbsd.org> * All Rights Reserved. @@ -19,8 +19,11 @@ * generic attribute support */ +#include <sys/mman.h> + #include <stdint.h> #include <stdlib.h> +#include <syslog.h> #include <unistd.h> #include <errno.h> @@ -118,13 +121,54 @@ int pthread_attr_setstack(pthread_attr_t *attrp, void *stackaddr, size_t stacksize) { int error; + volatile char *p = stackaddr; + size_t i; + struct syslog_data data = SYSLOG_DATA_INIT; + + if (stacksize < PTHREAD_STACK_MIN) { + syslog_r(LOG_USER, &data, + "pthread_attr_setstack(%p, %zu): " + "stack size below min size %d", + stackaddr, stacksize, PTHREAD_STACK_MIN); + return (EINVAL); + } /* - * XXX Add an alignment test, on stackaddr for stack-grows-up - * archs or on stackaddr+stacksize for stack-grows-down archs + * Make sure that the stack is page-aligned and a multiple + * of the page size */ - if (stacksize < PTHREAD_STACK_MIN) + if (((uintptr_t)stackaddr % PTHREAD_STACK_MIN) != 0 + || (stacksize % PTHREAD_STACK_MIN) != 0) { + syslog_r(LOG_USER, &data, + "pthread_attr_setstack(%p, 0x%zx): " + "unaligned thread stack start and/or size", + stackaddr, stacksize); return (EINVAL); + } + + /* + * We are going to re-mmap() stackaddr to MAP_STACK, but only + * if the entire range [stackaddr, stackaddr+stacksize) consists + * of valid address that are mapped PROT_READ|PROT_WRITE. + * Test this by reading and writing every page. + * + * XXX: What if the caller has SIGSEGV blocked or ignored? + * Then we won't crash here when entering an invalid mapping. + */ + for (i = 0; i < stacksize; i += PTHREAD_STACK_MIN) { + char val = p[i]; + + p[i] = val; + } + + if (mmap(stackaddr, stacksize, PROT_READ|PROT_WRITE, + MAP_FIXED|MAP_STACK|MAP_ANON|MAP_PRIVATE, -1, 0) == MAP_FAILED) { + syslog_r(LOG_USER, &data, + "pthread_attr_setstack(%p, %zu): mmap error %m", + stackaddr, stacksize); + return (errno); + } + if ((error = pthread_attr_setstackaddr(attrp, stackaddr))) return (error); (*attrp)->stack_size = stacksize; |