[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