summaryrefslogtreecommitdiff
path: root/lib/safestack
diff options
context:
space:
mode:
authorVlad Tsyrklevich <vlad@tsyrklevich.net>2018-08-09 22:56:41 +0000
committerVlad Tsyrklevich <vlad@tsyrklevich.net>2018-08-09 22:56:41 +0000
commitdafd5b461e1808fce4a76da13b81065968eff6aa (patch)
tree38857c1c1ca0e1ce3ab93f031e934b07f70e7eb6 /lib/safestack
parent2669a4feea0d67bf7e3041f3ddad7615859762b5 (diff)
downloadcompiler-rt-dafd5b461e1808fce4a76da13b81065968eff6aa.tar.gz
SafeStack: Delay thread stack clean-up
Summary: glibc can call SafeStack instrumented code even after the last pthread data destructor has run. Delay cleaning-up unsafe stacks for threads until the thread is dead by having future threads clean-up prior threads stacks. Reviewers: pcc, eugenis Reviewed By: eugenis Subscribers: cryptoad, eugenis, kubamracek, delcypher, llvm-commits, #sanitizers, kcc Differential Revision: https://reviews.llvm.org/D50406 git-svn-id: https://llvm.org/svn/llvm-project/compiler-rt/trunk@339405 91177308-0d34-0410-b5e6-96231b3b80d8
Diffstat (limited to 'lib/safestack')
-rw-r--r--lib/safestack/safestack.cc78
1 files changed, 57 insertions, 21 deletions
diff --git a/lib/safestack/safestack.cc b/lib/safestack/safestack.cc
index 8af93624b..920b89b5e 100644
--- a/lib/safestack/safestack.cc
+++ b/lib/safestack/safestack.cc
@@ -14,11 +14,13 @@
//
//===----------------------------------------------------------------------===//
+#include <errno.h>
#include <limits.h>
#include <pthread.h>
#include <stddef.h>
#include <stdint.h>
#include <unistd.h>
+#include <stdlib.h>
#include <sys/resource.h>
#include <sys/types.h>
#if !defined(__NetBSD__)
@@ -115,14 +117,6 @@ static inline void unsafe_stack_setup(void *start, size_t size, size_t guard) {
unsafe_stack_guard = guard;
}
-static void unsafe_stack_free() {
- if (unsafe_stack_start) {
- UnmapOrDie((char *)unsafe_stack_start - unsafe_stack_guard,
- unsafe_stack_size + unsafe_stack_guard);
- }
- unsafe_stack_start = nullptr;
-}
-
/// Thread data for the cleanup handler
static pthread_key_t thread_cleanup_key;
@@ -149,26 +143,68 @@ static void *thread_start(void *arg) {
tinfo->unsafe_stack_guard);
// Make sure out thread-specific destructor will be called
- // FIXME: we can do this only any other specific key is set by
- // intercepting the pthread_setspecific function itself
pthread_setspecific(thread_cleanup_key, (void *)1);
return start_routine(start_routine_arg);
}
-/// Thread-specific data destructor
+/// Linked list used to store exiting threads stack/thread information.
+struct thread_stack_ll {
+ struct thread_stack_ll *next;
+ void *stack_base;
+ pid_t pid;
+ tid_t tid;
+};
+
+/// Linked list of unsafe stacks for threads that are exiting. We delay
+/// unmapping them until the thread exits.
+static thread_stack_ll *thread_stacks = nullptr;
+static pthread_mutex_t thread_stacks_mutex = PTHREAD_MUTEX_INITIALIZER;
+
+/// Thread-specific data destructor. We want to free the unsafe stack only after
+/// this thread is terminated. libc can call functions in safestack-instrumented
+/// code (like free) after thread-specific data destructors have run.
static void thread_cleanup_handler(void *_iter) {
- // We want to free the unsafe stack only after all other destructors
- // have already run. We force this function to be called multiple times.
- // User destructors that might run more then PTHREAD_DESTRUCTOR_ITERATIONS-1
- // times might still end up executing after the unsafe stack is deallocated.
- size_t iter = (size_t)_iter;
- if (iter < PTHREAD_DESTRUCTOR_ITERATIONS) {
- pthread_setspecific(thread_cleanup_key, (void *)(iter + 1));
- } else {
- // This is the last iteration
- unsafe_stack_free();
+ CHECK_NE(unsafe_stack_start, nullptr);
+ pthread_setspecific(thread_cleanup_key, NULL);
+
+ pthread_mutex_lock(&thread_stacks_mutex);
+ // Temporary list to hold the previous threads stacks so we don't hold the
+ // thread_stacks_mutex for long.
+ thread_stack_ll *temp_stacks = thread_stacks;
+ thread_stacks = nullptr;
+ pthread_mutex_unlock(&thread_stacks_mutex);
+
+ pid_t pid = getpid();
+ tid_t tid = GetTid();
+
+ // Free stacks for dead threads
+ thread_stack_ll **stackp = &temp_stacks;
+ while (*stackp) {
+ thread_stack_ll *stack = *stackp;
+ if (stack->pid != pid || TgKill(stack->pid, stack->tid, 0) == -ESRCH) {
+ UnmapOrDie(stack->stack_base, unsafe_stack_size + unsafe_stack_guard);
+ *stackp = stack->next;
+ free(stack);
+ } else
+ stackp = &stack->next;
}
+
+ thread_stack_ll *cur_stack =
+ (thread_stack_ll *)malloc(sizeof(thread_stack_ll));
+ cur_stack->stack_base = (char *)unsafe_stack_start - unsafe_stack_guard;
+ cur_stack->pid = pid;
+ cur_stack->tid = tid;
+
+ pthread_mutex_lock(&thread_stacks_mutex);
+ // Merge thread_stacks with the current thread's stack and any remaining
+ // temp_stacks
+ *stackp = thread_stacks;
+ cur_stack->next = temp_stacks;
+ thread_stacks = cur_stack;
+ pthread_mutex_unlock(&thread_stacks_mutex);
+
+ unsafe_stack_start = nullptr;
}
static void EnsureInterceptorsInitialized();