diff options
author | Rich Felker <dalias@aerifal.cx> | 2014-03-24 16:57:11 -0400 |
---|---|---|
committer | Rich Felker <dalias@aerifal.cx> | 2014-03-24 16:57:11 -0400 |
commit | dab441aea240f3b7c18a26d2ef51979ea36c301c (patch) | |
tree | 9bb6c68bc3b025020a15d0d92b2b6aeff84a4d8c /src/env | |
parent | 98221c36119d2abfc55fe1d919705f625709fe3b (diff) | |
download | musl-dab441aea240f3b7c18a26d2ef51979ea36c301c.tar.gz musl-dab441aea240f3b7c18a26d2ef51979ea36c301c.tar.xz musl-dab441aea240f3b7c18a26d2ef51979ea36c301c.zip |
always initialize thread pointer at program start
this is the first step in an overhaul aimed at greatly simplifying and optimizing everything dealing with thread-local state. previously, the thread pointer was initialized lazily on first access, or at program startup if stack protector was in use, or at certain random places where inconsistent state could be reached if it were not initialized early. while believed to be fully correct, the logic was fragile and non-obvious. in the first phase of the thread pointer overhaul, support is retained (and in some cases improved) for systems/situation where loading the thread pointer fails, e.g. old kernels. some notes on specific changes: - the confusing use of libc.main_thread as an indicator that the thread pointer is initialized is eliminated in favor of an explicit has_thread_pointer predicate. - sigaction no longer needs to ensure that the thread pointer is initialized before installing a signal handler (this was needed to prevent a situation where the signal handler caused the thread pointer to be initialized and the subsequent sigreturn cleared it again) but it still needs to ensure that implementation-internal thread-related signals are not blocked. - pthread tsd initialization for the main thread is deferred in a new manner to minimize bloat in the static-linked __init_tp code. - pthread_setcancelstate no longer needs special handling for the situation before the thread pointer is initialized. it simply fails on systems that cannot support a thread pointer, which are non-conforming anyway. - pthread_cleanup_push/pop now check for missing thread pointer and nop themselves out in this case, so stdio no longer needs to avoid the cancellable path when the thread pointer is not available. a number of cases remain where certain interfaces may crash if the system does not support a thread pointer. at this point, these should be limited to pthread interfaces, and the number of such cases should be fewer than before.
Diffstat (limited to 'src/env')
-rw-r--r-- | src/env/__init_tls.c | 55 | ||||
-rw-r--r-- | src/env/__stack_chk_fail.c | 8 |
2 files changed, 50 insertions, 13 deletions
diff --git a/src/env/__init_tls.c b/src/env/__init_tls.c index dbfe62e7..8ac0036b 100644 --- a/src/env/__init_tls.c +++ b/src/env/__init_tls.c @@ -7,8 +7,28 @@ #include "atomic.h" #include "syscall.h" +int __init_tp(void *p) +{ + pthread_t td = p; + td->self = td; + if (__set_thread_area(TP_ADJ(p)) < 0) + return -1; + td->tid = td->pid = __syscall(SYS_set_tid_address, &td->tid); + td->errno_ptr = &td->errno_val; + /* Currently, both of these predicates depend in the same thing: + * successful initialization of the thread pointer. However, in + * the future, we may support setups where setting the thread + * pointer is possible but threads other than the main thread + * cannot work, so it's best to keep the predicates separate. */ + libc.has_thread_pointer = 1; + libc.can_do_threads = 1; + return 0; +} + #ifndef SHARED +static long long builtin_tls[(sizeof(struct pthread) + 64)/sizeof(long long)]; + struct tls_image { void *image; size_t len, size, align; @@ -62,10 +82,11 @@ typedef Elf64_Phdr Phdr; void __init_tls(size_t *aux) { - unsigned char *p, *mem; + unsigned char *p; size_t n; Phdr *phdr, *tls_phdr=0; size_t base = 0; + void *mem; libc.tls_size = sizeof(struct pthread); @@ -76,28 +97,38 @@ void __init_tls(size_t *aux) if (phdr->p_type == PT_TLS) tls_phdr = phdr; } - if (!tls_phdr) return; - T.image = (void *)(base + tls_phdr->p_vaddr); - T.len = tls_phdr->p_filesz; - T.size = tls_phdr->p_memsz; - T.align = tls_phdr->p_align; + if (tls_phdr) { + T.image = (void *)(base + tls_phdr->p_vaddr); + T.len = tls_phdr->p_filesz; + T.size = tls_phdr->p_memsz; + T.align = tls_phdr->p_align; + } T.size += (-T.size - (uintptr_t)T.image) & (T.align-1); if (T.align < 4*sizeof(size_t)) T.align = 4*sizeof(size_t); libc.tls_size = 2*sizeof(void *)+T.size+T.align+sizeof(struct pthread); - mem = (void *)__syscall( + if (libc.tls_size > sizeof builtin_tls) { + mem = (void *)__syscall( #ifdef SYS_mmap2 - SYS_mmap2, + SYS_mmap2, #else - SYS_mmap, + SYS_mmap, #endif - 0, libc.tls_size, PROT_READ|PROT_WRITE, - MAP_ANONYMOUS|MAP_PRIVATE, -1, 0); + 0, libc.tls_size, PROT_READ|PROT_WRITE, + MAP_ANONYMOUS|MAP_PRIVATE, -1, 0); + /* -4095...-1 cast to void * will crash on dereference anyway, + * so don't bloat the init code checking for error codes and + * explicitly calling a_crash(). */ + } else { + mem = builtin_tls; + } - if (!__install_initial_tls(__copy_tls(mem))) a_crash(); + /* Failure to initialize thread pointer is fatal if TLS is used. */ + if (__init_tp(__copy_tls(mem)) < 0 && tls_phdr) + a_crash(); } #else void __init_tls(size_t *auxv) { } diff --git a/src/env/__stack_chk_fail.c b/src/env/__stack_chk_fail.c index daa1b078..00634d38 100644 --- a/src/env/__stack_chk_fail.c +++ b/src/env/__stack_chk_fail.c @@ -7,7 +7,13 @@ uintptr_t __stack_chk_guard; void __init_ssp(void *entropy) { - pthread_t self = __pthread_self_init(); + /* Here the thread pointer is used without checking whether + * it is available; this will crash if it's not. However, + * this function is only meant to be called if the program + * being run uses stack protector, and in that case, it would + * crash without a thread pointer anyway, so it's better to + * crash early before there is state to be lost on crash. */ + pthread_t self = __pthread_self(); uintptr_t canary; if (entropy) memcpy(&canary, entropy, sizeof canary); else canary = (uintptr_t)&canary * 1103515245; |