llvmorg-github-actions[bot] wrote:
<!--LLVM PR SUMMARY COMMENT--> @llvm/pr-subscribers-compiler-rt-sanitizer Author: Jakob Koschel (jakos-sec) <details> <summary>Changes</summary> This introduces a new function `unsafe_sigaltstack(size_t ss_size)` that can be used by a user to allocate the unsafe sigaltstack. We cannot intercept sigaltstack to do that automatically, since allocating memory for the unsafe stack would not be async-signal-safe. * https://github.com/llvm/llvm-project/pull/196968 * ➤ https://github.com/llvm/llvm-project/pull/196969 * https://github.com/llvm/llvm-project/pull/196970 * https://github.com/llvm/llvm-project/pull/196971 --- Full diff: https://github.com/llvm/llvm-project/pull/196969.diff 3 Files Affected: - (modified) compiler-rt/include/sanitizer/safestack_interface.h (+15) - (modified) compiler-rt/lib/safestack/safestack.cpp (+70) - (modified) compiler-rt/test/safestack/sigaltstack.c (+20) ``````````diff diff --git a/compiler-rt/include/sanitizer/safestack_interface.h b/compiler-rt/include/sanitizer/safestack_interface.h index 68d2fed6ccef6..f4827c6a0128e 100644 --- a/compiler-rt/include/sanitizer/safestack_interface.h +++ b/compiler-rt/include/sanitizer/safestack_interface.h @@ -28,6 +28,21 @@ const void *SANITIZER_CDECL __safestack_get_unsafe_stack_bottom(void); /// Returns a pointer to the top of the unsafe stack of the current thread. const void *SANITIZER_CDECL __safestack_get_unsafe_stack_top(void); +/// Returns a pointer to the top of the unsafe sigalt stack of the current +/// thread. +const void *SANITIZER_CDECL __safestack_get_unsafe_sigalt_stack_ptr(void); + +/// Returns a pointer to the bottom of the unsafe sigalt stack of the current +/// thread. +const void *SANITIZER_CDECL __safestack_get_unsafe_sigalt_stack_bottom(void); + +/// Returns a pointer to the top of the unsafe sigalt stack of the current +/// thread. +const void *SANITIZER_CDECL __safestack_get_unsafe_sigalt_stack_top(void); + +/// Set a new unsafe signal stack context to be used if SA_ONSTACK is set. +int SANITIZER_CDECL __safestack_unsafe_sigaltstack(size_t ss_size); + #ifdef __cplusplus } // extern "C" #endif diff --git a/compiler-rt/lib/safestack/safestack.cpp b/compiler-rt/lib/safestack/safestack.cpp index 739bac59c5d3f..1724e6d6ae08e 100644 --- a/compiler-rt/lib/safestack/safestack.cpp +++ b/compiler-rt/lib/safestack/safestack.cpp @@ -113,6 +113,14 @@ __thread void *unsafe_stack_start = nullptr; __thread size_t unsafe_stack_size = 0; __thread size_t unsafe_stack_guard = 0; +// Per-thread unsafe stack information used for the unsafe stack during signal +// handling if sigaltstack is used. Without, only the safe stack is switched by +// the operating system. When the program indicates to use a separate stack for +// signal handling, this should also include the unsafe stack component. +__thread void* unsafe_sigalt_stack_ptr = nullptr; +__thread void* unsafe_sigalt_stack_start = nullptr; +__thread size_t unsafe_sigalt_stack_size = 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, @@ -134,6 +142,16 @@ inline void unsafe_stack_setup(void *start, size_t size, size_t guard) { unsafe_stack_guard = guard; } +inline void unsafe_sigalt_stack_setup(void* start, size_t size) { + SFS_CHECK((uintptr_t)start + size >= (uintptr_t)start); + void* stack_ptr = (void*)((uintptr_t)start + size); + SFS_CHECK((((uintptr_t)stack_ptr) & (kStackAlign - 1)) == 0); + + unsafe_sigalt_stack_ptr = stack_ptr; + unsafe_sigalt_stack_start = start; + unsafe_sigalt_stack_size = size; +} + /// Thread data for the cleanup handler pthread_key_t thread_cleanup_key; @@ -225,6 +243,14 @@ void thread_cleanup_handler(void *_iter) { pthread_mutex_unlock(&thread_stacks_mutex); unsafe_stack_start = nullptr; + + // In case the sigalt stack was allocated, we need to unmap the used memory. + if (unsafe_sigalt_stack_start) { + unsafe_sigalt_stack_ptr = nullptr; + Munmap(unsafe_sigalt_stack_start, unsafe_sigalt_stack_size); + unsafe_sigalt_stack_start = nullptr; + unsafe_sigalt_stack_size = 0; + } } void EnsureInterceptorsInitialized(); @@ -287,6 +313,30 @@ INTERCEPTOR(int, sigaction, int sig, const struct sigaction* act, return REAL(sigaction)(sig, act, oldact); } +// Since sigaltstack is required to be async-signal-safe, we cannot simply +// intercept it to allocate the the unsafe stack. Instead if the users wishes to +// setup an unsafe sigalt stack can call unsafe_sigaltstack(ss_size size) +// explicitliy. +int setup_unsafe_sigaltstack(size_t ss_size) { + EnsureInterceptorsInitialized(); + + SFS_CHECK(ss_size); + ss_size = RoundUpTo(ss_size, kStackAlign); + + // For now always map a new unsafe sigaltstack when setting a new + // sigaltstack. Potentially if the size is identical, this step can be + // skipped. + void* prev_sigalt_stack_start = unsafe_sigalt_stack_start; + size_t prev_sigalt_stack_size = unsafe_sigalt_stack_size; + void* sigalt_addr = unsafe_stack_alloc(ss_size, 0); + unsafe_sigalt_stack_setup(sigalt_addr, ss_size); + if (prev_sigalt_stack_start != nullptr) { + Munmap(prev_sigalt_stack_start, prev_sigalt_stack_size); + } + + return 0; +} + pthread_mutex_t interceptor_init_mutex = PTHREAD_MUTEX_INITIALIZER; bool interceptors_inited = false; @@ -354,6 +404,26 @@ __safestack_get_unsafe_stack_ptr() { return __safestack_unsafe_stack_ptr; } +extern "C" SANITIZER_INTERFACE_ATTRIBUTE const void* +__safestack_get_unsafe_sigalt_stack_bottom() { + return unsafe_sigalt_stack_start; +} + +extern "C" SANITIZER_INTERFACE_ATTRIBUTE const void* +__safestack_get_unsafe_sigalt_stack_top() { + return (char*)unsafe_sigalt_stack_start + unsafe_sigalt_stack_size; +} + +extern "C" SANITIZER_INTERFACE_ATTRIBUTE const void* +__safestack_get_unsafe_sigalt_stack_ptr() { + return unsafe_sigalt_stack_ptr; +} + +extern "C" SANITIZER_INTERFACE_ATTRIBUTE int __safestack_unsafe_sigaltstack( + size_t ss_size) { + return setup_unsafe_sigaltstack(ss_size); +} + // Compatibility aliases extern "C" SANITIZER_INTERFACE_ATTRIBUTE void* __get_unsafe_stack_bottom() { return const_cast<void*>(__safestack_get_unsafe_stack_bottom()); diff --git a/compiler-rt/test/safestack/sigaltstack.c b/compiler-rt/test/safestack/sigaltstack.c index 3c9fd61acff91..5432ca70ff04b 100644 --- a/compiler-rt/test/safestack/sigaltstack.c +++ b/compiler-rt/test/safestack/sigaltstack.c @@ -49,6 +49,7 @@ void *t1_start(void *ptr) { sigstk.ss_size = ss_size; sigstk.ss_sp = ss_sp; + __safestack_unsafe_sigaltstack(sigstk.ss_size); sigaltstack(&sigstk, NULL); // Test that after sigaltstack is set, it signal handling still works. @@ -64,6 +65,9 @@ int main() { char c[] = "hello world"; puts(c); + // Make sure no sigaltstack is allocated by default. + assert(!__safestack_get_unsafe_sigalt_stack_ptr()); + stack_t sigstk = {}; size_t ss_size = 4096 * 4; void *ss_sp = mmap(NULL, sigstk.ss_size, PROT_READ | PROT_WRITE, @@ -71,8 +75,17 @@ int main() { sigstk.ss_size = ss_size; sigstk.ss_sp = ss_sp; + __safestack_unsafe_sigaltstack(sigstk.ss_size); sigaltstack(&sigstk, NULL); + // Make sure __safestack_unsafe_sigaltstack allocated the unsafe sigaltstack with the + // correct size. + assert(__safestack_get_unsafe_sigalt_stack_ptr()); + assert(__safestack_get_unsafe_sigalt_stack_ptr() == + __safestack_get_unsafe_sigalt_stack_top()); + assert((__safestack_get_unsafe_sigalt_stack_top() - + __safestack_get_unsafe_sigalt_stack_bottom()) == sigstk.ss_size); + // Make sure retrieving the sigaltstack works without problems. sigaltstack(NULL, &sigstk); @@ -82,9 +95,16 @@ int main() { new_sigstk.ss_sp = mmap(NULL, new_sigstk.ss_size, PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANONYMOUS | MAP_STACK, -1, 0); + __safestack_unsafe_sigaltstack(new_sigstk.ss_size); sigaltstack(&new_sigstk, NULL); munmap(ss_sp, ss_size); + // Make sure updating the size of the unsafe sigaltstack also updates when + // setting a new sigaltstack. + assert(__safestack_get_unsafe_sigalt_stack_ptr()); + assert((__safestack_get_unsafe_sigalt_stack_top() - + __safestack_get_unsafe_sigalt_stack_bottom()) == new_sigstk.ss_size); + struct sigaction sa; sa.sa_handler = signal_handler; sigemptyset(&sa.sa_mask); `````````` </details> https://github.com/llvm/llvm-project/pull/196969 _______________________________________________ llvm-branch-commits mailing list [email protected] https://lists.llvm.org/cgi-bin/mailman/listinfo/llvm-branch-commits
