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