[libc-commits] [libc] [llvm] [libc] Block signals before a detached thread unmaps its stack (PR #232649)
via libc-commits
libc-commits at lists.llvm.org
Fri Oct 16 02:10:12 PDT 2026
llvmorg-github-actions[bot] wrote:
<!--LLVM PR SUMMARY COMMENT-->
@llvm/pr-subscribers-libc
Author: Pavel Labath (labath)
<details>
<summary>Changes</summary>
When a detached thread terminates, it cleans up its own resources in thread_exit(), which includes unmapping its stack with munmap(2). Between the unmap and the final exit(2) syscall, the thread continues to execute without a valid stack. If a signal is delivered to the thread during this window, the kernel will attempt to set up a signal frame on the unmapped stack, leading to a crash.
To prevent this, block all signals using rt_sigprocmask before calling cleanup_thread_resources().
No test case because this situation is pretty much impossible to reproduce deterministically -- even though it shows up regularly when running an application with thousands of threads and a SIGPROF-based profiler.
---
Full diff: https://github.com/llvm/llvm-project/pull/232649.diff
3 Files Affected:
- (modified) libc/src/__support/threads/linux/CMakeLists.txt (+3)
- (modified) libc/src/__support/threads/linux/thread.cpp (+12-3)
- (modified) utils/bazel/llvm-project-overlay/libc/BUILD.bazel (+3)
``````````diff
diff --git a/libc/src/__support/threads/linux/CMakeLists.txt b/libc/src/__support/threads/linux/CMakeLists.txt
index 145796c9bc7a5f..a106a6b13734a7 100644
--- a/libc/src/__support/threads/linux/CMakeLists.txt
+++ b/libc/src/__support/threads/linux/CMakeLists.txt
@@ -68,6 +68,7 @@ add_object_library(
libc.hdr.sched_macros
libc.hdr.signal_macros
libc.hdr.sys_mman_macros
+ libc.hdr.types.sigset_t
libc.src.errno.errno
libc.src.__support.CPP.atomic
libc.src.__support.CPP.stringstream
@@ -83,6 +84,7 @@ add_object_library(
libc.src.__support.OSUtil.linux.syscall_wrappers.munmap
libc.src.__support.OSUtil.linux.syscall_wrappers.open
libc.src.__support.OSUtil.linux.syscall_wrappers.read
+ libc.src.__support.OSUtil.linux.syscall_wrappers.rt_sigprocmask
libc.src.__support.OSUtil.linux.syscall_wrappers.sched_getparam
libc.src.__support.OSUtil.linux.syscall_wrappers.sched_getscheduler
libc.src.__support.OSUtil.linux.syscall_wrappers.sched_setscheduler
@@ -90,6 +92,7 @@ add_object_library(
libc.src.__support.OSUtil.linux.syscall_wrappers.write
libc.src.__support.threads.thread_common
libc.src.__support.threads.thread_attributes
+ libc.src.string.memory_utils.inline_memset
COMPILE_OPTIONS
${libc_opt_high_flag}
)
diff --git a/libc/src/__support/threads/linux/thread.cpp b/libc/src/__support/threads/linux/thread.cpp
index a67f7355a12d3e..c2d7413b2f9029 100644
--- a/libc/src/__support/threads/linux/thread.cpp
+++ b/libc/src/__support/threads/linux/thread.cpp
@@ -25,6 +25,7 @@
#include "src/__support/OSUtil/linux/syscall_wrappers/munmap.h"
#include "src/__support/OSUtil/linux/syscall_wrappers/open.h"
#include "src/__support/OSUtil/linux/syscall_wrappers/read.h"
+#include "src/__support/OSUtil/linux/syscall_wrappers/rt_sigprocmask.h"
#include "src/__support/OSUtil/linux/syscall_wrappers/sched_getparam.h"
#include "src/__support/OSUtil/linux/syscall_wrappers/sched_getscheduler.h"
#include "src/__support/OSUtil/linux/syscall_wrappers/sched_setscheduler.h"
@@ -38,6 +39,7 @@
#include "src/__support/threads/linux/futex_utils.h" // For FutexWordType
#include "src/__support/threads/linux/futex_word.h"
#include "src/__support/threads/thread_attributes.h"
+#include "src/string/memory_utils/inline_memset.h"
#include "hdr/errno_macros.h"
#include "hdr/fcntl_macros.h"
@@ -45,9 +47,10 @@
#include "hdr/signal_macros.h"
#include "hdr/stdint_proxy.h"
#include "hdr/sys_mman_macros.h" // For PROT_* and MAP_* definitions.
-#include <linux/param.h> // For EXEC_PAGESIZE.
-#include <linux/prctl.h> // For PR_SET_NAME
-#include <sys/syscall.h> // For syscall numbers.
+#include "hdr/types/sigset_t.h"
+#include <linux/param.h> // For EXEC_PAGESIZE.
+#include <linux/prctl.h> // For PR_SET_NAME
+#include <sys/syscall.h> // For syscall numbers.
namespace LIBC_NAMESPACE_DECL {
@@ -505,6 +508,12 @@ void thread_exit(ThreadReturnValue retval, ThreadStyle style) {
if (!attrib->detach_state.compare_exchange_strong(
joinable_state, uint32_t(DetachState::EXITING))) {
// Thread is detached so cleanup the resources.
+ // Signals must be blocked before unmapping the stack to prevent any signal
+ // handler from running without a stack.
+ sigset_t set;
+ inline_memset(&set, 0xff, sizeof(sigset_t));
+ (void)linux_syscalls::rt_sigprocmask(SIG_BLOCK, &set, nullptr);
+
cleanup_thread_resources(attrib);
// Set the CLEAR_TID address to nullptr to prevent the kernel
diff --git a/utils/bazel/llvm-project-overlay/libc/BUILD.bazel b/utils/bazel/llvm-project-overlay/libc/BUILD.bazel
index 457de6f7048762..4855313c944457 100644
--- a/utils/bazel/llvm-project-overlay/libc/BUILD.bazel
+++ b/utils/bazel/llvm-project-overlay/libc/BUILD.bazel
@@ -4708,6 +4708,7 @@ libc_support_library(
":__support_osutil_linux_syscall_wrappers_munmap",
":__support_osutil_linux_syscall_wrappers_open",
":__support_osutil_linux_syscall_wrappers_read",
+ ":__support_osutil_linux_syscall_wrappers_rt_sigprocmask",
":__support_osutil_linux_syscall_wrappers_sched_getparam",
":__support_osutil_linux_syscall_wrappers_sched_getscheduler",
":__support_osutil_linux_syscall_wrappers_sched_setscheduler",
@@ -4728,6 +4729,8 @@ libc_support_library(
":hdr_signal_macros",
":hdr_stdint_proxy",
":hdr_sys_mman_macros",
+ ":string_memory_utils",
+ ":types_sigset_t",
],
)
``````````
</details>
https://github.com/llvm/llvm-project/pull/232649
More information about the libc-commits
mailing list