[llvm-branch-commits] [SafeStack] Allocate unsafe sigaltstack (PR #196969)
via llvm-branch-commits
llvm-branch-commits at lists.llvm.org
Fri Jun 12 02:33:48 PDT 2026
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
More information about the llvm-branch-commits
mailing list