The PID_STOPPED flag in _ponfo::process_state is sometimes accidentally
cleared due to a race condition when modifying it with the "|=" or "&="
operators. This patch uses InterlockedOr/And() instead to avoid the
race condition.

Addresses: https://cygwin.com/pipermail/cygwin/2025-February/257473.html
Fixes: 1fd5e000ace55 ("import winsup-2000-02-17 snapshot")
Reported-by: Christian Franke <christian.fra...@t-online.de>
Reviewed-by:
Signed-off-by: Takashi Yano <takashi.y...@nifty.ne.jp>
---
 winsup/cygwin/exceptions.cc          |  6 +++---
 winsup/cygwin/fork.cc                |  5 +++--
 winsup/cygwin/local_includes/pinfo.h |  4 ++--
 winsup/cygwin/pinfo.cc               | 11 ++++++-----
 winsup/cygwin/signal.cc              |  6 ++++--
 winsup/cygwin/sigproc.cc             |  2 +-
 winsup/cygwin/spawn.cc               |  6 +++---
 7 files changed, 22 insertions(+), 18 deletions(-)

diff --git a/winsup/cygwin/exceptions.cc b/winsup/cygwin/exceptions.cc
index c6e82b6c5..45c71cbdf 100644
--- a/winsup/cygwin/exceptions.cc
+++ b/winsup/cygwin/exceptions.cc
@@ -882,7 +882,7 @@ sig_handle_tty_stop (int sig, siginfo_t *, void *)
   /* Silently ignore attempts to suspend if there is no accommodating
      cygwin parent to deal with this behavior. */
   if (!myself->cygstarted)
-    myself->process_state &= ~PID_STOPPED;
+    InterlockedAnd ((LONG *) &myself->process_state, ~PID_STOPPED);
   else
     {
       _my_tls.incyg = 1;
@@ -948,7 +948,7 @@ _cygtls::interrupt_setup (siginfo_t& si, void *handler, 
struct sigaction& siga)
   if (handler == sig_handle_tty_stop)
     {
       myself->stopsig = 0;
-      myself->process_state |= PID_STOPPED;
+      InterlockedOr ((LONG *) &myself->process_state, PID_STOPPED);
     }
 
   infodata = si;
@@ -1435,7 +1435,7 @@ _cygtls::handle_SIGCONT ()
     yield ();
 
   myself->stopsig = 0;
-  myself->process_state &= ~PID_STOPPED;
+  InterlockedAnd ((LONG *) &myself->process_state, ~PID_STOPPED);
 
   /* Clear pending stop signals */
   sig_clear (SIGSTOP, false);
diff --git a/winsup/cygwin/fork.cc b/winsup/cygwin/fork.cc
index 41a533705..783971b76 100644
--- a/winsup/cygwin/fork.cc
+++ b/winsup/cygwin/fork.cc
@@ -678,8 +678,9 @@ dofork (void **proc, bool *with_forkables)
 
   if (ischild)
     {
-      myself->process_state |= PID_ACTIVE;
-      myself->process_state &= ~(PID_INITIALIZING | PID_EXITED | PID_REAPED);
+      InterlockedOr ((LONG *) &myself->process_state, PID_ACTIVE);
+      InterlockedAnd ((LONG *) &myself->process_state,
+                     ~(PID_INITIALIZING | PID_EXITED | PID_REAPED));
     }
   else if (res < 0)
     {
diff --git a/winsup/cygwin/local_includes/pinfo.h 
b/winsup/cygwin/local_includes/pinfo.h
index 4de0f80dd..3086f9941 100644
--- a/winsup/cygwin/local_includes/pinfo.h
+++ b/winsup/cygwin/local_includes/pinfo.h
@@ -271,9 +271,9 @@ public:
   push_process_state (int add_flag)
   {
     flag = add_flag;
-    myself->process_state |= flag;
+    InterlockedOr ((LONG *) &myself->process_state, flag);
   }
-  void pop () { myself->process_state &= ~(flag); }
+  void pop () { InterlockedAnd ((LONG *) &myself->process_state, ~(flag)); }
   ~push_process_state () { pop (); }
 };
 
diff --git a/winsup/cygwin/pinfo.cc b/winsup/cygwin/pinfo.cc
index 1f26a3ccd..c69213f9a 100644
--- a/winsup/cygwin/pinfo.cc
+++ b/winsup/cygwin/pinfo.cc
@@ -69,7 +69,7 @@ pinfo::thisproc (HANDLE h)
      child_info_spawn::handle_spawn. */
 
   init (cygheap->pid, flags, h);
-  procinfo->process_state |= PID_IN_USE;
+  InterlockedOr ((LONG *) &procinfo->process_state, PID_IN_USE);
   procinfo->dwProcessId = myself_initial.dwProcessId;
   procinfo->sendsig = myself_initial.sendsig;
   wcscpy (procinfo->progname, myself_initial.progname);
@@ -89,7 +89,7 @@ pinfo_init (char **envp, int envc)
     {
       environ_init (envp, envc);
       /* spawn has already set up a pid structure for us so we'll use that */
-      myself->process_state |= PID_CYGPARENT;
+      InterlockedOr ((LONG *) &myself->process_state, PID_CYGPARENT);
     }
   else
     {
@@ -108,10 +108,11 @@ pinfo_init (char **envp, int envc)
       debug_printf ("Set nice to %d", myself->nice);
     }
 
-  myself->process_state |= PID_ACTIVE;
-  myself->process_state &= ~(PID_INITIALIZING | PID_EXITED | PID_REAPED);
+  InterlockedOr ((LONG *) &myself->process_state, PID_ACTIVE);
+  InterlockedAnd ((LONG *) &myself->process_state,
+                 ~(PID_INITIALIZING | PID_EXITED | PID_REAPED));
   if (being_debugged ())
-    myself->process_state |= PID_DEBUGGED;
+    InterlockedOr ((LONG *) &myself->process_state, PID_DEBUGGED);
   myself.preserve ();
   debug_printf ("pid %d, pgid %d, process_state %y",
                myself->pid, myself->pgid, myself->process_state);
diff --git a/winsup/cygwin/signal.cc b/winsup/cygwin/signal.cc
index 0bd64963f..f8ba67e75 100644
--- a/winsup/cygwin/signal.cc
+++ b/winsup/cygwin/signal.cc
@@ -456,9 +456,11 @@ sigaction_worker (int sig, const struct sigaction *newact,
                sig_clear (sig, true);
              if (sig == SIGCHLD)
                {
-                 myself->process_state &= ~PID_NOCLDSTOP;
+                 InterlockedAnd ((LONG *)&myself->process_state,
+                                 ~PID_NOCLDSTOP);
                  if (gs.sa_flags & SA_NOCLDSTOP)
-                   myself->process_state |= PID_NOCLDSTOP;
+                   InterlockedOr ((LONG *) &myself->process_state,
+                                  PID_NOCLDSTOP);
                }
            }
 
diff --git a/winsup/cygwin/sigproc.cc b/winsup/cygwin/sigproc.cc
index 1ffe00a94..8739f18f5 100644
--- a/winsup/cygwin/sigproc.cc
+++ b/winsup/cygwin/sigproc.cc
@@ -252,7 +252,7 @@ proc_subproc (DWORD what, uintptr_t val)
          vchild->sid = myself->sid;
          vchild->ctty = myself->ctty;
          vchild->cygstarted = true;
-         vchild->process_state |= PID_INITIALIZING;
+         InterlockedOr ((LONG *)&vchild->process_state, PID_INITIALIZING);
          vchild->ppid = myself->pid;   /* always set last */
        }
       break;
diff --git a/winsup/cygwin/spawn.cc b/winsup/cygwin/spawn.cc
index 8016f0864..06b84236d 100644
--- a/winsup/cygwin/spawn.cc
+++ b/winsup/cygwin/spawn.cc
@@ -543,7 +543,7 @@ child_info_spawn::worker (const char *prog_arg, const char 
*const *argv,
       refresh_cygheap ();
 
       if (c_flags & CREATE_NEW_PROCESS_GROUP)
-       myself->process_state |= PID_NEW_PG;
+       InterlockedOr ((LONG *) &myself->process_state, PID_NEW_PG);
 
       if (mode == _P_DETACH)
        /* all set */;
@@ -603,7 +603,7 @@ child_info_spawn::worker (const char *prog_arg, const char 
*const *argv,
       ::cygheap->user.deimpersonate ();
 
       if (!real_path.iscygexec () && mode == _P_OVERLAY)
-       myself->process_state |= PID_NOTCYGWIN;
+       InterlockedOr ((LONG *) &myself->process_state, PID_NOTCYGWIN);
 
       cygpid = (mode != _P_OVERLAY) ? create_cygwin_pid () : myself->pid;
 
@@ -705,7 +705,7 @@ child_info_spawn::worker (const char *prog_arg, const char 
*const *argv,
              myself->sendsig = myself->exec_sendsig;
              myself->exec_sendsig = NULL;
            }
-         myself->process_state &= ~PID_NOTCYGWIN;
+         InterlockedAnd ((LONG *) &myself->process_state, ~PID_NOTCYGWIN);
          /* Reset handle inheritance to default when the execution of a'
             non-Cygwin process fails.  Only need to do this for _P_OVERLAY
             since the handle will be closed otherwise.  Don't need to do
-- 
2.45.1

Reply via email to