Improve stack overflow diagnostics (take 2).
This reverts commits eb1b07469f2b5a392dc1bfd8adc211aea8c72bc5 and
d14dc3b87fbf80553f1cafa453816b7f11366627, and fixes the bug where
we were calling mmap (which might cause errno to be set) before
__set_tls (which is required to implement errno).
Bug: 8557703
Change-Id: I2c36d00240c56e156e1bb430d8c22a73a068b70c
diff --git a/libc/bionic/pthread.c b/libc/bionic/pthread.c
index 8589cd6..92e2c27 100644
--- a/libc/bionic/pthread.c
+++ b/libc/bionic/pthread.c
@@ -31,6 +31,7 @@
#include <errno.h>
#include <limits.h>
#include <sys/atomics.h>
+#include <sys/mman.h>
#include <unistd.h>
#include "bionic_atomic_inline.h"
@@ -102,6 +103,18 @@
// space (see pthread_key_delete)
pthread_key_clean_all();
+ if (thread->alternate_signal_stack != NULL) {
+ // Tell the kernel to stop using the alternate signal stack.
+ stack_t ss;
+ ss.ss_sp = NULL;
+ ss.ss_flags = SS_DISABLE;
+ sigaltstack(&ss, NULL);
+
+ // Free it.
+ munmap(thread->alternate_signal_stack, SIGSTKSZ);
+ thread->alternate_signal_stack = NULL;
+ }
+
// if the thread is detached, destroy the pthread_internal_t
// otherwise, keep it in memory and signal any joiners.
pthread_mutex_lock(&gThreadListLock);
diff --git a/libc/bionic/pthread_attr.cpp b/libc/bionic/pthread_attr.cpp
index fe1ed4a..d7c6c13 100644
--- a/libc/bionic/pthread_attr.cpp
+++ b/libc/bionic/pthread_attr.cpp
@@ -30,12 +30,16 @@
#include "pthread_internal.h"
-#define DEFAULT_STACK_SIZE (1024 * 1024)
+// Traditionally we give threads a 1MiB stack. When we started allocating per-thread
+// alternate signal stacks to ease debugging of stack overflows, we subtracted the
+// same amount we were using there from the default thread stack size. This should
+// keep memory usage roughly constant.
+#define DEFAULT_THREAD_STACK_SIZE ((1 * 1024 * 1024) - SIGSTKSZ)
int pthread_attr_init(pthread_attr_t* attr) {
attr->flags = 0;
attr->stack_base = NULL;
- attr->stack_size = DEFAULT_STACK_SIZE;
+ attr->stack_size = DEFAULT_THREAD_STACK_SIZE;
attr->guard_size = PAGE_SIZE;
attr->sched_policy = SCHED_NORMAL;
attr->sched_priority = 0;
diff --git a/libc/bionic/pthread_create.cpp b/libc/bionic/pthread_create.cpp
index f45f5e7..63695d3 100644
--- a/libc/bionic/pthread_create.cpp
+++ b/libc/bionic/pthread_create.cpp
@@ -69,9 +69,22 @@
thread->tls[TLS_SLOT_STACK_GUARD] = (void*) __stack_chk_guard;
__set_tls(thread->tls);
+
+ // Create and set an alternate signal stack.
+ // This must happen after __set_tls, in case a system call fails and tries to set errno.
+ stack_t ss;
+ ss.ss_sp = mmap(NULL, SIGSTKSZ, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, 0, 0);
+ if (ss.ss_sp != MAP_FAILED) {
+ ss.ss_size = SIGSTKSZ;
+ ss.ss_flags = 0;
+ sigaltstack(&ss, NULL);
+ thread->alternate_signal_stack = ss.ss_sp;
+ }
}
-// This trampoline is called from the assembly _pthread_clone() function.
+// This trampoline is called from the assembly _pthread_clone function.
+// Our 'tls' and __pthread_clone's 'child_stack' are one and the same, just growing in
+// opposite directions.
extern "C" void __thread_entry(void* (*func)(void*), void* arg, void** tls) {
// Wait for our creating thread to release us. This lets it have time to
// notify gdb about this thread before we start doing anything.
@@ -187,8 +200,12 @@
thread->attr.flags |= PTHREAD_ATTR_FLAG_USER_STACK;
}
- // Make room for TLS.
+ // Make room for the TLS area.
+ // The child stack is the same address, just growing in the opposite direction.
+ // At offsets >= 0, we have the TLS slots.
+ // At offsets < 0, we have the child stack.
void** tls = (void**)((uint8_t*)(thread->attr.stack_base) + thread->attr.stack_size - BIONIC_TLS_SLOTS * sizeof(void*));
+ void* child_stack = tls;
// Create a mutex for the thread in TLS_SLOT_SELF to wait on once it starts so we can keep
// it from doing anything until after we notify the debugger about it
@@ -204,7 +221,7 @@
int flags = CLONE_FILES | CLONE_FS | CLONE_VM | CLONE_SIGHAND | CLONE_THREAD | CLONE_SYSVSEM;
- int tid = __pthread_clone(start_routine, tls, flags, arg);
+ int tid = __pthread_clone(start_routine, child_stack, flags, arg);
if (tid < 0) {
int clone_errno = errno;
if ((thread->attr.flags & PTHREAD_ATTR_FLAG_USER_STACK) == 0) {
diff --git a/libc/bionic/pthread_internal.h b/libc/bionic/pthread_internal.h
index e34788b..98199d3 100644
--- a/libc/bionic/pthread_internal.h
+++ b/libc/bionic/pthread_internal.h
@@ -47,6 +47,8 @@
__pthread_cleanup_t* cleanup_stack;
void** tls; /* thread-local storage area */
+ void* alternate_signal_stack;
+
/*
* The dynamic linker implements dlerror(3), which makes it hard for us to implement this
* per-thread buffer by simply using malloc(3) and free(3).