About a month ago, we noticed that the 'fatal-signal' module was not really
multithread-safe — because it used sigprocmask, assuming that sigprocmask
would block some signals process-wide. An assumption that just does not hold.

This patch now fixes it, thanks to the 'sigdelay' module.


2026-05-16  Bruno Haible  <[email protected]>

        fatal-signal: Make really multithread-safe.
        * lib/fatal-signal.h (block_fatal_signals): Document a constraint
        regarding thread creation.
        * lib/fatal-signal.c: Include sigdelay.h.
        (fatal_signals_block_initially_mt): New variable.
        (block_fatal_signals, unblock_fatal_signals): In a multithreaded
        process, use sigdelay instead of pthread_sigmask.
        * modules/fatal-signal (Depends-on): Add sigdelay.

diff --git a/lib/fatal-signal.c b/lib/fatal-signal.c
index 33c99d5b8c..c8fb328bbb 100644
--- a/lib/fatal-signal.c
+++ b/lib/fatal-signal.c
@@ -29,6 +29,7 @@
 #include "glthread/lock.h"
 #include "glthread/once.h"
 #include "thread-optim.h"
+#include "sigdelay.h"
 #include "sig-handler.h"
 
 /* ========================================================================= */
@@ -299,6 +300,8 @@ init_fatal_signal_set (void)
    to occur in different threads and even overlap in time.  */
 gl_lock_define_initialized (static, fatal_signals_block_lock)
 static unsigned int fatal_signals_block_counter = 0;
+/* For correct operation in the face of thread-optim.h.  */
+static bool fatal_signals_block_initially_mt;
 
 /* Temporarily delay the catchable fatal signals.  */
 void
@@ -310,8 +313,22 @@ block_fatal_signals (void)
 
   if (fatal_signals_block_counter++ == 0)
     {
+      fatal_signals_block_initially_mt = mt;
       init_fatal_signal_set ();
-      pthread_sigmask (SIG_BLOCK, &fatal_signal_set, NULL);
+      if (mt)
+        sigdelay (SIG_BLOCK, &fatal_signal_set, NULL);
+      else
+        pthread_sigmask (SIG_BLOCK, &fatal_signal_set, NULL);
+    }
+  else
+    {
+      if (!fatal_signals_block_initially_mt && mt)
+        {
+          /* The process was single-threaded and has become multithreaded
+             before the matching unblock_fatal_signals() call.  This is
+             a constraint violation.  */
+          abort ();
+        }
     }
 
   if (mt) gl_lock_unlock (fatal_signals_block_lock);
@@ -331,8 +348,18 @@ unblock_fatal_signals (void)
     abort ();
   if (--fatal_signals_block_counter == 0)
     {
+      if (!fatal_signals_block_initially_mt && mt)
+        {
+          /* The process was single-threaded and has become multithreaded
+             at the matching unblock_fatal_signals() call.  This is a
+             constraint violation.  */
+          abort ();
+        }
       init_fatal_signal_set ();
-      pthread_sigmask (SIG_UNBLOCK, &fatal_signal_set, NULL);
+      if (fatal_signals_block_initially_mt)
+        sigdelay (SIG_UNBLOCK, &fatal_signal_set, NULL);
+      else
+        pthread_sigmask (SIG_UNBLOCK, &fatal_signal_set, NULL);
     }
 
   if (mt) gl_lock_unlock (fatal_signals_block_lock);
diff --git a/lib/fatal-signal.h b/lib/fatal-signal.h
index 5cc3c25152..fa21308a1d 100644
--- a/lib/fatal-signal.h
+++ b/lib/fatal-signal.h
@@ -77,7 +77,11 @@ extern int at_fatal_signal (_GL_ASYNC_SAFE void (*function) 
(int sig));
 /* Temporarily delay the catchable fatal signals.
    The signals will be blocked (= delayed) until the next call to
    unblock_fatal_signals().  If the signals are already blocked, a further
-   call to block_fatal_signals() has no effect.  */
+   call to block_fatal_signals() has no effect.
+   The program must obey the following constraint: If at the moment of a
+   block_fatal_signals() call the process is single-threaded, it MUST NOT
+   create additional threads until the matching unblock_fatal_signals()
+   call.  */
 extern void block_fatal_signals (void);
 
 /* Stop delaying the catchable fatal signals.  */
diff --git a/modules/fatal-signal b/modules/fatal-signal
index 8741641bf5..1561b17433 100644
--- a/modules/fatal-signal
+++ b/modules/fatal-signal
@@ -15,6 +15,7 @@ sigaction
 lock
 once
 thread-optim
+sigdelay
 pthread_sigmask
 raise
 stdcountof-h




Reply via email to