The queue is cleaned up by removing the entries having si_signo == 0
while processing the queued signals, however, sipacket::process() may
set si_signo in the queue to 0 of the entry already processed but not
succeed by calling sig_clear(). This patch ensures the sig_clear()
to remove the entry from the queue chain.

Addresses: https://cygwin.com/pipermail/cygwin/2024-November/256744.html
Fixes: 9d2155089e87 ("(wait_sig): Define variable q to be the start of the 
signal queue.  Just iterate through sigq queue, deleting processed or zeroed 
signals")
Reported-by: Christian Franke <christian.fra...@t-online.de>
Reviewed-by: Corinna Vinschen <cori...@vinschen.de>
Signed-off-by: Takashi Yano <takashi.y...@nifty.ne.jp>
---
 winsup/cygwin/local_includes/sigproc.h |  3 +-
 winsup/cygwin/sigproc.cc               | 48 +++++++++++++++++---------
 2 files changed, 34 insertions(+), 17 deletions(-)

diff --git a/winsup/cygwin/local_includes/sigproc.h 
b/winsup/cygwin/local_includes/sigproc.h
index 46e26db19..8b7062aae 100644
--- a/winsup/cygwin/local_includes/sigproc.h
+++ b/winsup/cygwin/local_includes/sigproc.h
@@ -50,8 +50,9 @@ struct sigpacket
   {
     HANDLE wakeup;
     HANDLE thread_handle;
-    struct sigpacket *next;
   };
+  struct sigpacket *next;
+  struct sigpacket *prev;
   int process ();
   int setup_handler (void *, struct sigaction&, _cygtls *);
 };
diff --git a/winsup/cygwin/sigproc.cc b/winsup/cygwin/sigproc.cc
index 4d50a5865..8ffb90a2c 100644
--- a/winsup/cygwin/sigproc.cc
+++ b/winsup/cygwin/sigproc.cc
@@ -111,7 +111,7 @@ class pending_signals
 public:
   void add (sigpacket&);
   bool pending () {retry = !!start.next; return retry;}
-  void clear (int sig) {sigs[sig].si.si_signo = 0;}
+  void clear (int sig);
   void clear (_cygtls *tls);
   friend void sig_dispatch_pending (bool);
   friend void wait_sig (VOID *arg);
@@ -432,21 +432,35 @@ sig_clear (int sig)
   sigq.clear (sig);
 }
 
+/* Clear pending signals of specific si_signo.
+   Called from sigpacket::process(). */
+void
+pending_signals::clear (int sig)
+{
+  sigpacket *q = sigs + sig;
+  if (!sig || !q->si.si_signo)
+    return;
+  q->si.si_signo = 0;
+  q->prev->next = q->next;
+  if (q->next)
+    q->next->prev = q->prev;
+}
+
 /* Clear pending signals of specific thread.  Called under TLS lock from
    _cygtls::remove_pending_sigs. */
 void
 pending_signals::clear (_cygtls *tls)
 {
-  sigpacket *q = &start, *qnext;
+  sigpacket *q = &start;
 
-  while ((qnext = q->next))
-    if (qnext->sigtls == tls)
+  while ((q = q->next))
+    if (q->sigtls == tls)
       {
-       qnext->si.si_signo = 0;
-       q->next = qnext->next;
+       q->si.si_signo = 0;
+       q->prev->next = q->next;
+       if (q->next)
+         q->next->prev = q->prev;
       }
-    else
-      q = qnext;
 }
 
 /* Clear pending signals of specific thread.  Called from _cygtls::remove */
@@ -1299,7 +1313,10 @@ pending_signals::add (sigpacket& pack)
     return;
   *se = pack;
   se->next = start.next;
-  start.next = se;
+  se->prev = &start;
+  se->prev->next = se;
+  if (se->next)
+    se->next->prev = se;
 }
 
 /* Process signals by waiting for signal data to arrive in a pipe.
@@ -1450,17 +1467,16 @@ wait_sig (VOID *)
        case __SIGFLUSHFAST:
          if (!sig_held)
            {
-             sigpacket *qnext;
              /* Check the queue for signals.  There will always be at least one
                 thing on the queue if this was a valid signal.  */
-             while ((qnext = q->next))
+             while ((q = q->next))
                {
-                 if (qnext->si.si_signo && qnext->process () <= 0)
-                   q = qnext;
-                 else
+                 if (q->si.si_signo && q->process () > 0)
                    {
-                     q->next = qnext->next;
-                     qnext->si.si_signo = 0;
+                     q->si.si_signo = 0;
+                     q->prev->next = q->next;
+                     if (q->next)
+                       q->next->prev = q->prev;
                    }
                }
              /* At least one signal still queued?  The event is used in select
-- 
2.45.1

Reply via email to