[newlib-cygwin] Cygwin: fork: Call pthread::atforkprepare() in lock_pthread()

Takashi Yano via Cygwin-cvs <[email protected]> Fri, 29 May 2026 02:34:52 +0000 (GMT)
Newsgroups gmane.os.cygwin.cvs
Message-ID <[email protected]>
https://sourceware.org/git/gitweb.cgi?p=3Dnewlib-cygwin.git;h=3D52d033095e6=
7d52e23a185c029e40fd2edbfe30f

commit 52d033095e67d52e23a185c029e40fd2edbfe30f
Author: Takashi Yano <[email protected]>
Date:   Tue May 19 16:43:21 2026 +0900

    Cygwin: fork: Call pthread::atforkprepare() in lock_pthread()
   =20
    Since the commit 5f515cf3d6e3, if one thread calls fclose() while
    another thread calls fork(), a deadlock can occur. The mechanism
    is as follows.
      1) fclose() first calls __sfp_lock_acquire() and, then calls
         lock_process::locker.acquire() via cygheap_fdget().
      2) fork() first calls lock_process::locker.acquire() via the
         constructor of the lock_process class in the hold_everything
         class, and then calls __sfp_lock_acquire() via __fp_lock_all()
         in atforkprepare().
      3) As a result, the thread calling fclose() tries to acquire the
         lock_process lock while holding __sfp_lock, and the thread
         calling fork() tries to acquire __sfp_lock while holding the
         lock_process lock.
    This leads to a deadlock. Before the commit 5f515cf3d6e3, __sfp_lock
    was acquired in the constructor of the lock_pthread class in the
    hold_everything class, and since lock_pthread is defined before
    lock_process, this deadlock did not occur.
   =20
    This patch moves the atforkprepare() call back into the constructor
    of the lock_pthread class, restoring the previous lock acquisition
    order.
   =20
    Fixes: 5f515cf3d6e3 ("Cygwin: add _Fork() system call per POSIX.1-2024")
    Signed-off-by: Takashi Yano <[email protected]>
    Reviewed-by: Johannes Schindelin <[email protected]>

Diff:
---
 winsup/cygwin/local_includes/sigproc.h | 21 +++++++++++----------
 1 file changed, 11 insertions(+), 10 deletions(-)

diff --git a/winsup/cygwin/local_includes/sigproc.h b/winsup/cygwin/local_i=
ncludes/sigproc.h
index 92cda94dc..21367877c 100644
--- a/winsup/cygwin/local_includes/sigproc.h
+++ b/winsup/cygwin/local_includes/sigproc.h
@@ -131,7 +131,15 @@ class lock_pthread
 {
   bool bother;
 public:
-  lock_pthread (): bother (1) {}
+  lock_pthread (bool do_atfork_handlers): bother (1)
+  {
+    /* POSIX.1-2024: _Fork() does not call any handler established
+		     by pthread_atfork(). */
+    if (do_atfork_handlers)
+      dont_bother ();
+    else
+      prepare ();
+  }
   void prepare ()
   {
     pthread::atforkprepare ();
@@ -166,15 +174,8 @@ class hold_everything
   lock_process process;
=20
 public:
-  hold_everything (bool& x, bool do_atfork_handlers): ischild (x)
-  {
-    /* POSIX.1-2024: _Fork() does not call any handler established
-		     by pthread_atfork(). */
-    if (do_atfork_handlers)
-      pthread.dont_bother ();
-    else
-      pthread.prepare ();
-  }
+  hold_everything (bool& x, bool do_atfork_handlers): ischild (x),
+  pthread (do_atfork_handlers) {}
   operator int () const {return signals;}
=20
   ~hold_everything()