https://sourceware.org/git/gitweb.cgi?p=newlib-cygwin.git;h=9b02451f6e4ad1feddb998e136ac7108bda6c948
commit 9b02451f6e4ad1feddb998e136ac7108bda6c948 Author: Takashi Yano <[email protected]> Date: Wed Mar 12 09:09:36 2025 +0900 Cygwin: signal: Do not send __SIGFLUSHFAST if the pipe/queue is full If __SIGFLUSHFAST is sent while the signal queue or the pipe is full, it causes deadlock. The mechanism is as follows. When sending __SIGFLUSHFAST, sig_send() waits for response (wakeup event) from wait_sig(). Therefore, the main thread does not process signals while waiting the wakeup event. However, if main thread does not process signals, signals in the queue are not dequeued. As a result, the sigpacket for __SIGFLUSHFAST stays in the signal pipe and not being processed. Thus the wakeup event will never be triggered. This did not occur with old signal queue design, because the queue never becomes full. This patch use alternative way for flushing the queue if the queue and the pipe is full, i.e., sending a event to trigger flushing in wait_sig(). Do the same for __SIGFLUSH as well. Addresses: https://cygwin.com/pipermail/cygwin-patches/2025q1/013461.html Fixes: 8ac22f8f16a8 ("Cygwin: signal: Redesign signal queue handling") Reported-by: Christian Franke <[email protected]> Reviewed-by: Corinna Vinschen <[email protected]> Signed-off-by: Takashi Yano <[email protected]> Diff: --- winsup/cygwin/sigproc.cc | 49 +++++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 48 insertions(+), 1 deletion(-) diff --git a/winsup/cygwin/sigproc.cc b/winsup/cygwin/sigproc.cc index e399c686b..fc28be956 100644 --- a/winsup/cygwin/sigproc.cc +++ b/winsup/cygwin/sigproc.cc @@ -96,6 +96,10 @@ static NO_COPY HANDLE my_readsig; /* Used in select if a signalfd is part of the read descriptor set */ HANDLE NO_COPY my_pendingsigs_evt; +/* Used by sig_send() with __SIGFLUSHFAST */ +static NO_COPY HANDLE sigflush_evt; +static NO_COPY HANDLE sigflush_done_evt; + /* Function declarations */ static int checkstate (waitq *); static __inline__ bool get_proc_lock (DWORD, DWORD); @@ -123,6 +127,7 @@ public: void clear (_cygtls *tls); friend void sig_dispatch_pending (bool); friend void wait_sig (VOID *arg); + friend sigset_t sig_send (_pinfo *p, siginfo_t& si, _cygtls *tls); }; static NO_COPY pending_signals sigq; @@ -531,6 +536,8 @@ sigproc_init () ProtectHandle (my_readsig); myself->sendsig = my_sendsig; my_pendingsigs_evt = CreateEvent (NULL, TRUE, FALSE, NULL); + sigflush_evt = CreateEvent (NULL, FALSE, FALSE, NULL); + sigflush_done_evt = CreateEvent (NULL, FALSE, FALSE, NULL); if (!my_pendingsigs_evt) api_fatal ("couldn't create pending signal event, %E"); @@ -599,6 +606,7 @@ sig_send (_pinfo *p, siginfo_t& si, _cygtls *tls) int rc = 1; bool its_me; HANDLE sendsig; + HANDLE mtx; sigpacket pack; bool communing = si.si_signo == __SIGCOMMUNE; @@ -757,6 +765,38 @@ sig_send (_pinfo *p, siginfo_t& si, _cygtls *tls) unsigned cw_mask; cw_mask = pack.si.si_signo == __SIGFLUSHFAST ? 0 : cw_sig_restart; + char mtx_name[MAX_PATH]; + shared_name (mtx_name, "sig_send", p->pid); + mtx = CreateMutex (&sec_none_nih, FALSE, mtx_name); + cygwait (mtx, INFINITE, cw_mask); + + if (its_me && (si.si_signo == __SIGFLUSHFAST || si.si_signo == __SIGFLUSH)) + { + /* Currently, __SIGFLUSH is automatically processed in wait_sig() by + itself if pending signals exist. Therefore, sending __SIGFLUSH* is + not absolutely necessary. So, if there is not enough space in the + queue or the pipe, do not send __SIGFLUSHFAST to avoid deadlock. + Do the same for __SIGFLUSH. */ + IO_STATUS_BLOCK io; + FILE_PIPE_LOCAL_INFORMATION fpli; + fpli.WriteQuotaAvailable = 0; + NtQueryInformationFile (my_sendsig, &io, &fpli, sizeof (fpli), + FilePipeLocalInformation); + int pkts_in_pipe = + PIPE_DEPTH - fpli.WriteQuotaAvailable / sizeof (sigpacket); + if (sigq.queue_left < pkts_in_pipe + 2 + || fpli.WriteQuotaAvailable < sizeof (sigpacket)) + { + ReleaseMutex (mtx); + CloseHandle (mtx); + ResetEvent (sigflush_done_evt); + SetEvent (sigflush_evt); + cygwait (sigflush_done_evt, INFINITE, cw_mask); + rc = 0; + goto out; + } + } + DWORD nb; BOOL res; /* Try multiple times to send if packsize != nb since that probably @@ -766,9 +806,13 @@ sig_send (_pinfo *p, siginfo_t& si, _cygtls *tls) res = WriteFile (sendsig, leader, packsize, &nb, NULL); if (!res || packsize == nb) break; + ReleaseMutex (mtx); cygwait (NULL, 10, cw_mask); + cygwait (mtx, INFINITE, cw_mask); res = 0; } + ReleaseMutex (mtx); + CloseHandle (mtx); if (!res) { @@ -1433,7 +1477,9 @@ wait_sig (VOID *) else if (sigq.start.next && PeekNamedPipe (my_readsig, NULL, 0, NULL, &nb, NULL) && !nb) { - Sleep ((sig_held || GetTickCount () - t0 > 10) ? 1 : 0); + yield (); + if (sig_held || GetTickCount () - t0 > 10) + WaitForSingleObject (sigflush_evt, 1); pack.si.si_signo = __SIGFLUSH; } else if (!ReadFile (my_readsig, &pack, sizeof (pack), &nb, NULL)) @@ -1608,6 +1654,7 @@ wait_sig (VOID *) if (clearwait && !have_execed) proc_subproc (PROC_CLEARWAIT, 0); skip_process_signal: + SetEvent (sigflush_done_evt); if (pack.wakeup) { sigproc_printf ("signalling pack.wakeup %p", pack.wakeup);
