[libc-commits] [libc] [llvm] [libc] Block signals and clear tid address earlier in thread_exit (PR #232783)

Jeff Bailey via libc-commits libc-commits at lists.llvm.org
Fri Oct 16 07:47:07 PDT 2026


https://github.com/kaladron created https://github.com/llvm/llvm-project/pull/232783

In thread_exit, signals are currently only blocked inside the detached cleanup branch.  This leaves a window on joinable threads after call_atexit_callbacks() has destroyed thread-local state and after detach_state transitions to DetachState::EXITING.  A signal handler running in that window can access destroyed thread-local objects or self-deadlock if it calls pthread_detach(pthread_self()).

Detached threads also clear the kernel tid address after cleanup_thread_resources() has already unmapped the stack where attrib->clear_tid lives.  If the thread is terminated between unmapping its stack and calling SYS_set_tid_address, the kernel will write to unmapped memory.

Call signals::block_all() immediately after call_atexit_callbacks() so all exiting threads block signals before updating detach_state.  Move SYS_set_tid_address(0) before cleanup_thread_resources() so the kernel tid address is cleared while the stack is still mapped.

Assisted-by: Automated tooling, human reviewed.

>From 923c969a91b08e3450d04e001512b875a41c5f39 Mon Sep 17 00:00:00 2001
From: Jeff Bailey <jbailey at raspberryginger.com>
Date: Fri, 16 Oct 2026 13:53:15 +0100
Subject: [PATCH] [libc] Block signals and clear tid address earlier in
 thread_exit

In thread_exit, signals are currently only blocked inside the detached
cleanup branch.  This leaves a window on joinable threads after
call_atexit_callbacks() has destroyed thread-local state and after
detach_state transitions to DetachState::EXITING.  A signal handler
running in that window can access destroyed thread-local objects or
self-deadlock if it calls pthread_detach(pthread_self()).

Detached threads also clear the kernel tid address after
cleanup_thread_resources() has already unmapped the stack where
attrib->clear_tid lives.  If the thread is terminated between unmapping
its stack and calling SYS_set_tid_address, the kernel will write to
unmapped memory.

Call signals::block_all() immediately after call_atexit_callbacks() so
all exiting threads block signals before updating detach_state.  Move
SYS_set_tid_address(0) before cleanup_thread_resources() so the kernel
tid address is cleared while the stack is still mapped.

Assisted-by: Automated tooling, human reviewed.
---
 .../__support/threads/linux/CMakeLists.txt    |  4 +--
 libc/src/__support/threads/linux/thread.cpp   | 28 +++++++++----------
 .../llvm-project-overlay/libc/BUILD.bazel     |  4 +--
 3 files changed, 16 insertions(+), 20 deletions(-)

diff --git a/libc/src/__support/threads/linux/CMakeLists.txt b/libc/src/__support/threads/linux/CMakeLists.txt
index a106a6b13734a..78ba94353686b 100644
--- a/libc/src/__support/threads/linux/CMakeLists.txt
+++ b/libc/src/__support/threads/linux/CMakeLists.txt
@@ -68,13 +68,13 @@ 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
     libc.src.__support.CPP.string_view
     libc.src.__support.common
     libc.src.__support.error_or
+    libc.src.__support.OSUtil.linux.sigmask
     libc.src.__support.OSUtil.linux.syscall_wrappers.clone
     libc.src.__support.OSUtil.linux.syscall_wrappers.close
     libc.src.__support.OSUtil.linux.syscall_wrappers.exit
@@ -84,7 +84,6 @@ 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
@@ -92,7 +91,6 @@ 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 c2d7413b2f902..2fbf34d22514c 100644
--- a/libc/src/__support/threads/linux/thread.cpp
+++ b/libc/src/__support/threads/linux/thread.cpp
@@ -16,6 +16,7 @@
 #include "src/__support/CPP/atomic.h"
 #include "src/__support/CPP/string_view.h"
 #include "src/__support/CPP/stringstream.h"
+#include "src/__support/OSUtil/linux/sigmask.h"
 #include "src/__support/OSUtil/linux/syscall_wrappers/clone.h"
 #include "src/__support/OSUtil/linux/syscall_wrappers/close.h"
 #include "src/__support/OSUtil/linux/syscall_wrappers/exit.h"
@@ -25,7 +26,6 @@
 #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"
@@ -39,7 +39,6 @@
 #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"
@@ -47,10 +46,9 @@
 #include "hdr/signal_macros.h"
 #include "hdr/stdint_proxy.h"
 #include "hdr/sys_mman_macros.h" // For PROT_* and MAP_* definitions.
-#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.
+#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 {
 
@@ -504,21 +502,23 @@ void thread_exit(ThreadReturnValue retval, ThreadStyle style) {
   // be called by the thread which owns them.
   internal::call_atexit_callbacks();
 
+  // Block all signals now that thread-local destructors have run. This
+  // prevents signal handlers from observing destroyed thread-local state,
+  // ensures threads in DetachState::EXITING cannot handle signals, and
+  // prevents detached threads from running a signal handler after unmapping
+  // their own stack.
+  (void)signals::block_all();
+
   uint32_t joinable_state = uint32_t(DetachState::JOINABLE);
   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);
+    // Set the CLEAR_TID address to nullptr before unmapping the stack to
+    // prevent the kernel from signalling at a non-existent futex location.
+    LIBC_NAMESPACE::syscall_impl<long>(SYS_set_tid_address, 0);
 
     cleanup_thread_resources(attrib);
 
-    // Set the CLEAR_TID address to nullptr to prevent the kernel
-    // from signalling at a non-existent futex location.
-    LIBC_NAMESPACE::syscall_impl<long>(SYS_set_tid_address, 0);
     // Return value for detached thread should be unused. We need to avoid
     // referencing `style` or `retval.*` because they may be stored on the stack
     // and we have deallocated our stack!
diff --git a/utils/bazel/llvm-project-overlay/libc/BUILD.bazel b/utils/bazel/llvm-project-overlay/libc/BUILD.bazel
index 54846d2465143..1041bee79d384 100644
--- a/utils/bazel/llvm-project-overlay/libc/BUILD.bazel
+++ b/utils/bazel/llvm-project-overlay/libc/BUILD.bazel
@@ -4739,6 +4739,7 @@ libc_support_library(
         ":__support_libc_errno",
         ":__support_macros_attributes",
         ":__support_macros_config",
+        ":__support_osutil_linux_sigmask",
         ":__support_osutil_linux_syscall_wrappers_clone",
         ":__support_osutil_linux_syscall_wrappers_close",
         ":__support_osutil_linux_syscall_wrappers_exit",
@@ -4748,7 +4749,6 @@ 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",
@@ -4769,8 +4769,6 @@ 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