diff options
author | Theo de Raadt <deraadt@cvs.openbsd.org> | 2018-04-12 17:15:35 +0000 |
---|---|---|
committer | Theo de Raadt <deraadt@cvs.openbsd.org> | 2018-04-12 17:15:35 +0000 |
commit | 24fb6166b97816f05ec45586882e78a881b9ffbd (patch) | |
tree | 1b58ef9cc5dd22f4fcc7663edcb908f1e64401b6 | |
parent | 9e91f96d779c665c5377cfd5667ee7e276f4c6eb (diff) |
(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
-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; |