diff options
Diffstat (limited to 'lib/safestack')
-rw-r--r-- | lib/safestack/CMakeLists.txt | 2 | ||||
-rw-r--r-- | lib/safestack/safestack.cc | 130 | ||||
-rw-r--r-- | lib/safestack/safestack_platform.h | 124 | ||||
-rw-r--r-- | lib/safestack/safestack_util.h | 49 |
4 files changed, 234 insertions, 71 deletions
diff --git a/lib/safestack/CMakeLists.txt b/lib/safestack/CMakeLists.txt index cc874a3fe..aa259e8d6 100644 --- a/lib/safestack/CMakeLists.txt +++ b/lib/safestack/CMakeLists.txt @@ -12,8 +12,6 @@ foreach(arch ${SAFESTACK_SUPPORTED_ARCH}) ARCHS ${arch} SOURCES ${SAFESTACK_SOURCES} $<TARGET_OBJECTS:RTInterception.${arch}> - $<TARGET_OBJECTS:RTSanitizerCommon.${arch}> - $<TARGET_OBJECTS:RTSanitizerCommonNoLibc.${arch}> CFLAGS ${SAFESTACK_CFLAGS} PARENT_TARGET safestack) endforeach() diff --git a/lib/safestack/safestack.cc b/lib/safestack/safestack.cc index e68208015..f713d5e68 100644 --- a/lib/safestack/safestack.cc +++ b/lib/safestack/safestack.cc @@ -1,9 +1,8 @@ //===-- safestack.cc ------------------------------------------------------===// // -// The LLVM Compiler Infrastructure -// -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // //===----------------------------------------------------------------------===// // @@ -14,21 +13,31 @@ // //===----------------------------------------------------------------------===// +#include "safestack_platform.h" +#include "safestack_util.h" + #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__) -#include <sys/user.h> -#endif #include "interception/interception.h" -#include "sanitizer_common/sanitizer_common.h" + +using namespace safestack; + +// TODO: To make accessing the unsafe stack pointer faster, we plan to +// eventually store it directly in the thread control block data structure on +// platforms where this structure is pointed to by %fs or %gs. This is exactly +// the same mechanism as currently being used by the traditional stack +// protector pass to store the stack guard (see getStackCookieLocation() +// function above). Doing so requires changing the tcbhead_t struct in glibc +// on Linux and tcb struct in libc on FreeBSD. +// +// For now, store it in a thread-local variable. +extern "C" { +__attribute__((visibility( + "default"))) __thread void *__safestack_unsafe_stack_ptr = nullptr; +} + +namespace { // TODO: The runtime library does not currently protect the safe stack beyond // relying on the system-enforced ASLR. The protection of the (safe) stack can @@ -73,43 +82,26 @@ const unsigned kStackAlign = 16; /// size rlimit is set to infinity. const unsigned kDefaultUnsafeStackSize = 0x2800000; -/// Runtime page size obtained through sysconf -static unsigned pageSize; - -// TODO: To make accessing the unsafe stack pointer faster, we plan to -// eventually store it directly in the thread control block data structure on -// platforms where this structure is pointed to by %fs or %gs. This is exactly -// the same mechanism as currently being used by the traditional stack -// protector pass to store the stack guard (see getStackCookieLocation() -// function above). Doing so requires changing the tcbhead_t struct in glibc -// on Linux and tcb struct in libc on FreeBSD. -// -// For now, store it in a thread-local variable. -extern "C" { -__attribute__((visibility( - "default"))) __thread void *__safestack_unsafe_stack_ptr = nullptr; -} - // Per-thread unsafe stack information. It's not frequently accessed, so there // it can be kept out of the tcb in normal thread-local variables. -static __thread void *unsafe_stack_start = nullptr; -static __thread size_t unsafe_stack_size = 0; -static __thread size_t unsafe_stack_guard = 0; - -using namespace __sanitizer; - -static inline void *unsafe_stack_alloc(size_t size, size_t guard) { - CHECK_GE(size + guard, size); - void *addr = MmapOrDie(size + guard, "unsafe_stack_alloc"); - MprotectNoAccess((uptr)addr, (uptr)guard); +__thread void *unsafe_stack_start = nullptr; +__thread size_t unsafe_stack_size = 0; +__thread size_t unsafe_stack_guard = 0; + +inline void *unsafe_stack_alloc(size_t size, size_t guard) { + SFS_CHECK(size + guard >= size); + void *addr = Mmap(nullptr, size + guard, PROT_READ | PROT_WRITE, + MAP_PRIVATE | MAP_ANON, -1, 0); + SFS_CHECK(MAP_FAILED != addr); + Mprotect(addr, guard, PROT_NONE); return (char *)addr + guard; } -static inline void unsafe_stack_setup(void *start, size_t size, size_t guard) { - CHECK_GE((char *)start + size, (char *)start); - CHECK_GE((char *)start + guard, (char *)start); +inline void unsafe_stack_setup(void *start, size_t size, size_t guard) { + SFS_CHECK((char *)start + size >= (char *)start); + SFS_CHECK((char *)start + guard >= (char *)start); void *stack_ptr = (char *)start + size; - CHECK_EQ((((size_t)stack_ptr) & (kStackAlign - 1)), 0); + SFS_CHECK((((size_t)stack_ptr) & (kStackAlign - 1)) == 0); __safestack_unsafe_stack_ptr = stack_ptr; unsafe_stack_start = start; @@ -118,7 +110,7 @@ static inline void unsafe_stack_setup(void *start, size_t size, size_t guard) { } /// Thread data for the cleanup handler -static pthread_key_t thread_cleanup_key; +pthread_key_t thread_cleanup_key; /// Safe stack per-thread information passed to the thread_start function struct tinfo { @@ -132,7 +124,7 @@ struct tinfo { /// Wrap the thread function in order to deallocate the unsafe stack when the /// thread terminates by returning from its main function. -static void *thread_start(void *arg) { +void *thread_start(void *arg) { struct tinfo *tinfo = (struct tinfo *)arg; void *(*start_routine)(void *) = tinfo->start_routine; @@ -154,19 +146,19 @@ struct thread_stack_ll { void *stack_base; size_t size; pid_t pid; - tid_t tid; + ThreadId 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_stack_ll *thread_stacks = nullptr; +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) { - CHECK_NE(unsafe_stack_start, nullptr); +void thread_cleanup_handler(void *_iter) { + SFS_CHECK(unsafe_stack_start != nullptr); pthread_setspecific(thread_cleanup_key, NULL); pthread_mutex_lock(&thread_stacks_mutex); @@ -177,17 +169,15 @@ static void thread_cleanup_handler(void *_iter) { pthread_mutex_unlock(&thread_stacks_mutex); pid_t pid = getpid(); - tid_t tid = GetTid(); + ThreadId tid = GetTid(); // Free stacks for dead threads thread_stack_ll **stackp = &temp_stacks; while (*stackp) { thread_stack_ll *stack = *stackp; - int error; if (stack->pid != pid || - (internal_iserror(TgKill(stack->pid, stack->tid, 0), &error) && - error == ESRCH)) { - UnmapOrDie(stack->stack_base, stack->size); + (-1 == TgKill(stack->pid, stack->tid, 0) && errno == ESRCH)) { + Munmap(stack->stack_base, stack->size); *stackp = stack->next; free(stack); } else @@ -212,7 +202,7 @@ static void thread_cleanup_handler(void *_iter) { unsafe_stack_start = nullptr; } -static void EnsureInterceptorsInitialized(); +void EnsureInterceptorsInitialized(); /// Intercept thread creation operation to allocate and setup the unsafe stack INTERCEPTOR(int, pthread_create, pthread_t *thread, @@ -234,11 +224,12 @@ INTERCEPTOR(int, pthread_create, pthread_t *thread, pthread_attr_destroy(&tmpattr); } - CHECK_NE(size, 0); - CHECK_EQ((size & (kStackAlign - 1)), 0); - CHECK_EQ((guard & (pageSize - 1)), 0); + SFS_CHECK(size); + size = RoundUpTo(size, kStackAlign); void *addr = unsafe_stack_alloc(size, guard); + // Put tinfo at the end of the buffer. guard may be not page aligned. + // If that is so then some bytes after addr can be mprotected. struct tinfo *tinfo = (struct tinfo *)(((char *)addr) + size - sizeof(struct tinfo)); tinfo->start_routine = start_routine; @@ -250,12 +241,13 @@ INTERCEPTOR(int, pthread_create, pthread_t *thread, return REAL(pthread_create)(thread, attr, thread_start, tinfo); } -static BlockingMutex interceptor_init_lock(LINKER_INITIALIZED); -static bool interceptors_inited = false; +pthread_mutex_t interceptor_init_mutex = PTHREAD_MUTEX_INITIALIZER; +bool interceptors_inited = false; -static void EnsureInterceptorsInitialized() { - BlockingMutexLock lock(&interceptor_init_lock); - if (interceptors_inited) return; +void EnsureInterceptorsInitialized() { + MutexLock lock(interceptor_init_mutex); + if (interceptors_inited) + return; // Initialize pthread interceptors for thread allocation INTERCEPT_FUNCTION(pthread_create); @@ -263,6 +255,8 @@ static void EnsureInterceptorsInitialized() { interceptors_inited = true; } +} // namespace + extern "C" __attribute__((visibility("default"))) #if !SANITIZER_CAN_USE_PREINIT_ARRAY // On ELF platforms, the constructor is invoked using .preinit_array (see below) @@ -279,9 +273,7 @@ void __safestack_init() { // Allocate unsafe stack for main thread void *addr = unsafe_stack_alloc(size, guard); - unsafe_stack_setup(addr, size, guard); - pageSize = sysconf(_SC_PAGESIZE); // Setup the cleanup handler pthread_key_create(&thread_cleanup_key, thread_cleanup_handler); diff --git a/lib/safestack/safestack_platform.h b/lib/safestack/safestack_platform.h new file mode 100644 index 000000000..81e4c2645 --- /dev/null +++ b/lib/safestack/safestack_platform.h @@ -0,0 +1,124 @@ +//===-- safestack_platform.h ----------------------------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// +// +// This file implements platform specific parts of SafeStack runtime. +// +//===----------------------------------------------------------------------===// + +#ifndef SAFESTACK_PLATFORM_H +#define SAFESTACK_PLATFORM_H + +#include "safestack_util.h" +#include "sanitizer_common/sanitizer_platform.h" + +#include <dlfcn.h> +#include <stdint.h> +#include <stdio.h> +#include <stdlib.h> +#include <sys/mman.h> +#include <sys/syscall.h> +#include <sys/types.h> +#include <unistd.h> + +#if !(SANITIZER_NETBSD || SANITIZER_FREEBSD || SANITIZER_LINUX) +#error "Support for your platform has not been implemented" +#endif + +#if SANITIZER_NETBSD +#include <lwp.h> + +extern "C" void *__mmap(void *, size_t, int, int, int, int, off_t); +#endif + +#if SANITIZER_FREEBSD +#include <sys/thr.h> +#endif + +namespace safestack { + +#if SANITIZER_NETBSD +static void *GetRealLibcAddress(const char *symbol) { + void *real = dlsym(RTLD_NEXT, symbol); + if (!real) + real = dlsym(RTLD_DEFAULT, symbol); + if (!real) { + fprintf(stderr, "safestack GetRealLibcAddress failed for symbol=%s", + symbol); + abort(); + } + return real; +} + +#define _REAL(func, ...) real##_##func(__VA_ARGS__) +#define DEFINE__REAL(ret_type, func, ...) \ + static ret_type (*real_##func)(__VA_ARGS__) = NULL; \ + if (!real_##func) { \ + real_##func = (ret_type(*)(__VA_ARGS__))GetRealLibcAddress(#func); \ + } \ + SFS_CHECK(real_##func); +#endif + +using ThreadId = uint64_t; + +inline ThreadId GetTid() { +#if SANITIZER_NETBSD + DEFINE__REAL(int, _lwp_self); + return _REAL(_lwp_self); +#elif SANITIZER_FREEBSD + long Tid; + thr_self(&Tid); + return Tid; +#else + return syscall(SYS_gettid); +#endif +} + +inline int TgKill(pid_t pid, ThreadId tid, int sig) { +#if SANITIZER_NETBSD + DEFINE__REAL(int, _lwp_kill, int a, int b); + (void)pid; + return _REAL(_lwp_kill, tid, sig); +#elif SANITIZER_FREEBSD + return syscall(SYS_thr_kill2, pid, tid, sig); +#else + return syscall(SYS_tgkill, pid, tid, sig); +#endif +} + +inline void *Mmap(void *addr, size_t length, int prot, int flags, int fd, + off_t offset) { +#if SANITIZER_NETBSD + return __mmap(addr, length, prot, flags, fd, 0, offset); +#elif defined(__x86_64__) && (SANITIZER_FREEBSD) + return (void *)__syscall(SYS_mmap, addr, length, prot, flags, fd, offset); +#else + return (void *)syscall(SYS_mmap, addr, length, prot, flags, fd, offset); +#endif +} + +inline int Munmap(void *addr, size_t length) { +#if SANITIZER_NETBSD + DEFINE__REAL(int, munmap, void *a, size_t b); + return _REAL(munmap, addr, length); +#else + return syscall(SYS_munmap, addr, length); +#endif +} + +inline int Mprotect(void *addr, size_t length, int prot) { +#if SANITIZER_NETBSD + DEFINE__REAL(int, mprotect, void *a, size_t b, int c); + return _REAL(mprotect, addr, length, prot); +#else + return syscall(SYS_mprotect, addr, length, prot); +#endif +} + +} // namespace safestack + +#endif // SAFESTACK_PLATFORM_H diff --git a/lib/safestack/safestack_util.h b/lib/safestack/safestack_util.h new file mode 100644 index 000000000..da3f11b54 --- /dev/null +++ b/lib/safestack/safestack_util.h @@ -0,0 +1,49 @@ +//===-- safestack_util.h --------------------------------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// +// +// This file contains utility code for SafeStack implementation. +// +//===----------------------------------------------------------------------===// + +#ifndef SAFESTACK_UTIL_H +#define SAFESTACK_UTIL_H + +#include <pthread.h> +#include <stdio.h> +#include <stdlib.h> + +namespace safestack { + +#define SFS_CHECK(a) \ + do { \ + if (!(a)) { \ + fprintf(stderr, "safestack CHECK failed: %s:%d %s\n", __FILE__, \ + __LINE__, #a); \ + abort(); \ + }; \ + } while (false) + +inline size_t RoundUpTo(size_t size, size_t boundary) { + SFS_CHECK((boundary & (boundary - 1)) == 0); + return (size + boundary - 1) & ~(boundary - 1); +} + +class MutexLock { + public: + explicit MutexLock(pthread_mutex_t &mutex) : mutex_(&mutex) { + pthread_mutex_lock(mutex_); + } + ~MutexLock() { pthread_mutex_unlock(mutex_); } + + private: + pthread_mutex_t *mutex_ = nullptr; +}; + +} // namespace safestack + +#endif // SAFESTACK_UTIL_H |