[libc-commits] [libc] [llvm] [libc] Block signals before a detached thread unmaps its stack (PR #232649)

Pavel Labath via libc-commits libc-commits at lists.llvm.org
Fri Oct 16 02:09:19 PDT 2026


https://github.com/labath created https://github.com/llvm/llvm-project/pull/232649

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.

>From 0d6739b8feaa292e7f1c271a60c8e00fb01b83e2 Mon Sep 17 00:00:00 2001
From: Pavel Labath <pavel at labath.sk>
Date: Fri, 16 Oct 2026 09:04:57 +0000
Subject: [PATCH] [libc] Block signals before a detached thread unmaps its
 stack

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.
---
 libc/src/__support/threads/linux/CMakeLists.txt   |  3 +++
 libc/src/__support/threads/linux/thread.cpp       | 15 ++++++++++++---
 utils/bazel/llvm-project-overlay/libc/BUILD.bazel |  3 +++
 3 files changed, 18 insertions(+), 3 deletions(-)

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",
     ],
 )
 



More information about the libc-commits mailing list