diff options
Diffstat (limited to 'libc/bionic/pthread_create.cpp')
-rw-r--r-- | libc/bionic/pthread_create.cpp | 161 |
1 files changed, 98 insertions, 63 deletions
diff --git a/libc/bionic/pthread_create.cpp b/libc/bionic/pthread_create.cpp index fc8afa2..dbdb180 100644 --- a/libc/bionic/pthread_create.cpp +++ b/libc/bionic/pthread_create.cpp @@ -29,12 +29,14 @@ #include <pthread.h> #include <errno.h> +#include <string.h> #include <sys/mman.h> #include <unistd.h> #include "pthread_internal.h" #include "private/bionic_macros.h" +#include "private/bionic_prctl.h" #include "private/bionic_ssp.h" #include "private/bionic_tls.h" #include "private/libc_logging.h" @@ -51,33 +53,52 @@ extern "C" int __isthreaded; // This code is used both by each new pthread and the code that initializes the main thread. void __init_tls(pthread_internal_t* thread) { - if (thread->user_allocated_stack()) { - // We don't know where the user got their stack, so assume the worst and zero the TLS area. - memset(&thread->tls[0], 0, BIONIC_TLS_SLOTS * sizeof(void*)); + if (thread->mmap_size == 0) { + // If the TLS area was not allocated by mmap(), it may not have been cleared to zero. + // So assume the worst and zero the TLS area. + memset(thread->tls, 0, sizeof(thread->tls)); + memset(thread->key_data, 0, sizeof(thread->key_data)); } // Slot 0 must point to itself. The x86 Linux kernel reads the TLS from %fs:0. thread->tls[TLS_SLOT_SELF] = thread->tls; thread->tls[TLS_SLOT_THREAD_ID] = thread; // GCC looks in the TLS for the stack guard on x86, so copy it there from our global. - thread->tls[TLS_SLOT_STACK_GUARD] = (void*) __stack_chk_guard; + thread->tls[TLS_SLOT_STACK_GUARD] = reinterpret_cast<void*>(__stack_chk_guard); } void __init_alternate_signal_stack(pthread_internal_t* thread) { // Create and set an alternate signal stack. - stack_t ss; - ss.ss_sp = mmap(NULL, SIGSTKSZ, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0); - if (ss.ss_sp != MAP_FAILED) { - ss.ss_size = SIGSTKSZ; + void* stack_base = mmap(NULL, SIGNAL_STACK_SIZE, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0); + if (stack_base != MAP_FAILED) { + + // Create a guard page to catch stack overflows in signal handlers. + if (mprotect(stack_base, PAGE_SIZE, PROT_NONE) == -1) { + munmap(stack_base, SIGNAL_STACK_SIZE); + return; + } + stack_t ss; + ss.ss_sp = reinterpret_cast<uint8_t*>(stack_base) + PAGE_SIZE; + ss.ss_size = SIGNAL_STACK_SIZE - PAGE_SIZE; ss.ss_flags = 0; sigaltstack(&ss, NULL); - thread->alternate_signal_stack = ss.ss_sp; + thread->alternate_signal_stack = stack_base; + + // We can only use const static allocated string for mapped region name, as Android kernel + // uses the string pointer directly when dumping /proc/pid/maps. + prctl(PR_SET_VMA, PR_SET_VMA_ANON_NAME, ss.ss_sp, ss.ss_size, "thread signal stack"); } } -int __init_thread(pthread_internal_t* thread, bool add_to_thread_list) { +int __init_thread(pthread_internal_t* thread) { int error = 0; + if (__predict_true((thread->attr.flags & PTHREAD_ATTR_FLAG_DETACHED) == 0)) { + atomic_init(&thread->join_state, THREAD_NOT_JOINED); + } else { + atomic_init(&thread->join_state, THREAD_DETACHED); + } + // Set the scheduling policy/priority of the thread. if (thread->attr.sched_policy != SCHED_NORMAL) { sched_param param; @@ -94,36 +115,73 @@ int __init_thread(pthread_internal_t* thread, bool add_to_thread_list) { thread->cleanup_stack = NULL; - if (add_to_thread_list) { - _pthread_internal_add(thread); - } - return error; } -static void* __create_thread_stack(pthread_internal_t* thread) { +static void* __create_thread_mapped_space(size_t mmap_size, size_t stack_guard_size) { // Create a new private anonymous map. int prot = PROT_READ | PROT_WRITE; int flags = MAP_PRIVATE | MAP_ANONYMOUS | MAP_NORESERVE; - void* stack = mmap(NULL, thread->attr.stack_size, prot, flags, -1, 0); - if (stack == MAP_FAILED) { + void* space = mmap(NULL, mmap_size, prot, flags, -1, 0); + if (space == MAP_FAILED) { __libc_format_log(ANDROID_LOG_WARN, "libc", - "pthread_create failed: couldn't allocate %zd-byte stack: %s", - thread->attr.stack_size, strerror(errno)); + "pthread_create failed: couldn't allocate %zu-bytes mapped space: %s", + mmap_size, strerror(errno)); return NULL; } - // Set the guard region at the end of the stack to PROT_NONE. - if (mprotect(stack, thread->attr.guard_size, PROT_NONE) == -1) { + // Stack is at the lower end of mapped space, stack guard region is at the lower end of stack. + // Set the stack guard region to PROT_NONE, so we can detect thread stack overflow. + if (mprotect(space, stack_guard_size, PROT_NONE) == -1) { __libc_format_log(ANDROID_LOG_WARN, "libc", - "pthread_create failed: couldn't mprotect PROT_NONE %zd-byte stack guard region: %s", - thread->attr.guard_size, strerror(errno)); - munmap(stack, thread->attr.stack_size); + "pthread_create failed: couldn't mprotect PROT_NONE %zu-byte stack guard region: %s", + stack_guard_size, strerror(errno)); + munmap(space, mmap_size); return NULL; } - return stack; + return space; +} + +static int __allocate_thread(pthread_attr_t* attr, pthread_internal_t** threadp, void** child_stack) { + size_t mmap_size; + uint8_t* stack_top; + + if (attr->stack_base == NULL) { + // The caller didn't provide a stack, so allocate one. + // Make sure the stack size and guard size are multiples of PAGE_SIZE. + mmap_size = BIONIC_ALIGN(attr->stack_size + sizeof(pthread_internal_t), PAGE_SIZE); + attr->guard_size = BIONIC_ALIGN(attr->guard_size, PAGE_SIZE); + attr->stack_base = __create_thread_mapped_space(mmap_size, attr->guard_size); + if (attr->stack_base == NULL) { + return EAGAIN; + } + stack_top = reinterpret_cast<uint8_t*>(attr->stack_base) + mmap_size; + } else { + // Remember the mmap size is zero and we don't need to free it. + mmap_size = 0; + stack_top = reinterpret_cast<uint8_t*>(attr->stack_base) + attr->stack_size; + } + + // Mapped space(or user allocated stack) is used for: + // pthread_internal_t + // thread stack (including guard page) + + // To safely access the pthread_internal_t and thread stack, we need to find a 16-byte aligned boundary. + stack_top = reinterpret_cast<uint8_t*>( + (reinterpret_cast<uintptr_t>(stack_top) - sizeof(pthread_internal_t)) & ~0xf); + + pthread_internal_t* thread = reinterpret_cast<pthread_internal_t*>(stack_top); + attr->stack_size = stack_top - reinterpret_cast<uint8_t*>(attr->stack_base); + + thread->mmap_size = mmap_size; + thread->attr = *attr; + __init_tls(thread); + + *threadp = thread; + *child_stack = stack_top; + return 0; } static int __pthread_start(void* arg) { @@ -158,44 +216,21 @@ int pthread_create(pthread_t* thread_out, pthread_attr_t const* attr, // Inform the rest of the C library that at least one thread was created. __isthreaded = 1; - pthread_internal_t* thread = reinterpret_cast<pthread_internal_t*>(calloc(sizeof(*thread), 1)); - if (thread == NULL) { - __libc_format_log(ANDROID_LOG_WARN, "libc", "pthread_create failed: couldn't allocate thread"); - return EAGAIN; - } - + pthread_attr_t thread_attr; if (attr == NULL) { - pthread_attr_init(&thread->attr); + pthread_attr_init(&thread_attr); } else { - thread->attr = *attr; + thread_attr = *attr; attr = NULL; // Prevent misuse below. } - // Make sure the stack size and guard size are multiples of PAGE_SIZE. - thread->attr.stack_size = BIONIC_ALIGN(thread->attr.stack_size, PAGE_SIZE); - thread->attr.guard_size = BIONIC_ALIGN(thread->attr.guard_size, PAGE_SIZE); - - if (thread->attr.stack_base == NULL) { - // The caller didn't provide a stack, so allocate one. - thread->attr.stack_base = __create_thread_stack(thread); - if (thread->attr.stack_base == NULL) { - free(thread); - return EAGAIN; - } - } else { - // The caller did provide a stack, so remember we're not supposed to free it. - thread->attr.flags |= PTHREAD_ATTR_FLAG_USER_ALLOCATED_STACK; + pthread_internal_t* thread = NULL; + void* child_stack = NULL; + int result = __allocate_thread(&thread_attr, &thread, &child_stack); + if (result != 0) { + return result; } - // 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. - thread->tls = reinterpret_cast<void**>(reinterpret_cast<uint8_t*>(thread->attr.stack_base) + - thread->attr.stack_size - BIONIC_TLS_SLOTS * sizeof(void*)); - void* child_stack = thread->tls; - __init_tls(thread); - // Create a mutex for the thread in TLS to wait on once it starts so we can keep // it from doing anything until after we notify the debugger about it // @@ -212,7 +247,7 @@ int pthread_create(pthread_t* thread_out, pthread_attr_t const* attr, int flags = CLONE_VM | CLONE_FS | CLONE_FILES | CLONE_SIGHAND | CLONE_THREAD | CLONE_SYSVSEM | CLONE_SETTLS | CLONE_PARENT_SETTID | CLONE_CHILD_CLEARTID; - void* tls = thread->tls; + void* tls = reinterpret_cast<void*>(thread->tls); #if defined(__i386__) // On x86 (but not x86-64), CLONE_SETTLS takes a pointer to a struct user_desc rather than // a pointer to the TLS itself. @@ -227,26 +262,26 @@ int pthread_create(pthread_t* thread_out, pthread_attr_t const* attr, // be unblocked, but we're about to unmap the memory the mutex is stored in, so this serves as a // reminder that you can't rewrite this function to use a ScopedPthreadMutexLocker. pthread_mutex_unlock(&thread->startup_handshake_mutex); - if (!thread->user_allocated_stack()) { - munmap(thread->attr.stack_base, thread->attr.stack_size); + if (thread->mmap_size != 0) { + munmap(thread->attr.stack_base, thread->mmap_size); } - free(thread); __libc_format_log(ANDROID_LOG_WARN, "libc", "pthread_create failed: clone failed: %s", strerror(errno)); return clone_errno; } - int init_errno = __init_thread(thread, true); + int init_errno = __init_thread(thread); if (init_errno != 0) { // Mark the thread detached and replace its start_routine with a no-op. // Letting the thread run is the easiest way to clean up its resources. - thread->attr.flags |= PTHREAD_ATTR_FLAG_DETACHED; + atomic_store(&thread->join_state, THREAD_DETACHED); + __pthread_internal_add(thread); thread->start_routine = __do_nothing; pthread_mutex_unlock(&thread->startup_handshake_mutex); return init_errno; } // Publish the pthread_t and unlock the mutex to let the new thread start running. - *thread_out = reinterpret_cast<pthread_t>(thread); + *thread_out = __pthread_internal_add(thread); pthread_mutex_unlock(&thread->startup_handshake_mutex); return 0; |