Previously, two bugs exist in sigtimedwait(). One is, that since
_my_tls.sigwait_mask was left non-zero if the signal arrives after
the timeout, sigpacket::process() would wrongly try to handle it.
The other is if a timeout occurs after sigpacket::process() is
called, but not completed yet, the signal handler can be called
accidentally. If the signal handler is set to SIG_DFL or SIG_IGN,
access violation will occur in both cases.

With this patch, in sigwait_common(), check if sigwait_mask == 0
to confirm that sigpacket::process() cleared it. In this case,
do not treat it as WAIT_TIMEOUT, but as WAIT_SIGNALED. Moreover,
sigpacket::process() checks sigwait_mask to know whether timeout
occurs in sigwait_common() and if timeout already happens, do not
treat the signal as waited. In both cases, to avoid race issues,
the code is guarded by cygtls::lock().

Addresses: https://cygwin.com/pipermail/cygwin/2024-November/256762.html
Fixes: 24ff42d79aab ("Cygwin: Implement sigtimedwait")
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/exceptions.cc |  5 ++++-
 winsup/cygwin/release/3.5.5 |  3 +++
 winsup/cygwin/signal.cc     | 17 ++++++++++++++---
 3 files changed, 21 insertions(+), 4 deletions(-)

diff --git a/winsup/cygwin/exceptions.cc b/winsup/cygwin/exceptions.cc
index 3195d5719..60c1f594f 100644
--- a/winsup/cygwin/exceptions.cc
+++ b/winsup/cygwin/exceptions.cc
@@ -1527,11 +1527,14 @@ sigpacket::process ()
   if ((HANDLE) *tls)
     tls->signal_debugger (si);
 
-  if (issig_wait)
+  tls->lock ();
+  if (issig_wait && tls->sigwait_mask != 0)
     {
       tls->sigwait_mask = 0;
+      tls->unlock ();
       goto dosig;
     }
+  tls->unlock ();
 
   if (handler == SIG_IGN)
     {
diff --git a/winsup/cygwin/release/3.5.5 b/winsup/cygwin/release/3.5.5
index 115496c18..77e60d1d7 100644
--- a/winsup/cygwin/release/3.5.5
+++ b/winsup/cygwin/release/3.5.5
@@ -39,3 +39,6 @@ Fixes:
 
 - Fix NtCreateEvent() error in create_lock_ob() called from flock().
   Addresses: https://cygwin.com/pipermail/cygwin/2024-November/256750.html
+
+- Fix segfault when timeout is used in sigtimedwait().
+  Addresses: https://cygwin.com/pipermail/cygwin/2024-November/256762.html
diff --git a/winsup/cygwin/signal.cc b/winsup/cygwin/signal.cc
index 77152910b..eca536e90 100644
--- a/winsup/cygwin/signal.cc
+++ b/winsup/cygwin/signal.cc
@@ -618,6 +618,20 @@ sigwait_common (const sigset_t *set, siginfo_t *info, 
PLARGE_INTEGER waittime)
       switch (cygwait (NULL, waittime,
                       cw_sig_eintr | cw_cancel | cw_cancel_self))
        {
+       case WAIT_TIMEOUT:
+         _my_tls.lock ();
+         if (_my_tls.sigwait_mask != 0)
+           {
+             /* Normal timeout */
+             _my_tls.sigwait_mask = 0;
+             _my_tls.unlock ();
+             set_errno (EAGAIN);
+             break;
+           }
+         /* sigpacket::process() already started.
+            Go through to WAIT_SIGNALED case. */
+         _my_tls.unlock ();
+         fallthrough;
        case WAIT_SIGNALED:
          if (!sigismember (set, _my_tls.infodata.si_signo))
            set_errno (EINTR);
@@ -639,9 +653,6 @@ sigwait_common (const sigset_t *set, siginfo_t *info, 
PLARGE_INTEGER waittime)
              _my_tls.unlock ();
            }
          break;
-       case WAIT_TIMEOUT:
-         set_errno (EAGAIN);
-         break;
        default:
          __seterrno ();
          break;
-- 
2.45.1

Reply via email to