Skip to content

Commit

Permalink
Cygwin: signal: Introduce a lock for the signal queue
Browse files Browse the repository at this point in the history
Currently, the signal queue is touched by the thread sig as well as
other threads that call sigaction_worker(). This potentially has
a possibility to destroy the signal queue chain. A possible worst
result may be a self-loop chain which causes infinite loop. With
this patch, lock()/unlock() are introduce to avoid such a situation.

Fixes: 474048c ("* sigproc.cc (pending_signals::add): Just index directly into signal array rather than treating the array as a heap.")
Suggested-by: Corinna Vinschen <corinna@vinschen.de>
Reviewed-by: Corinna Vinschen <corinna@vinschen.de>
Signed-off-by: Takashi Yano <takashi.yano@nifty.ne.jp>
(cherry picked from commit 496fa7b2ce0052550eab8900723ebb59c33d25e7)
  • Loading branch information
tyan0 authored and github-cygwin committed Dec 6, 2024
1 parent 5bd4f99 commit b38ba4b
Show file tree
Hide file tree
Showing 4 changed files with 32 additions and 14 deletions.
12 changes: 6 additions & 6 deletions winsup/cygwin/exceptions.cc
Original file line number Diff line number Diff line change
Expand Up @@ -1448,10 +1448,10 @@ _cygtls::handle_SIGCONT (threadlist_t * &tl_entry)
sigsent = true;
}
/* Clear pending stop signals */
sig_clear (SIGSTOP);
sig_clear (SIGTSTP);
sig_clear (SIGTTIN);
sig_clear (SIGTTOU);
sig_clear (SIGSTOP, false);
sig_clear (SIGTSTP, false);
sig_clear (SIGTTIN, false);
sig_clear (SIGTTOU, false);
}

int
Expand Down Expand Up @@ -1552,14 +1552,14 @@ sigpacket::process ()
goto exit_sig;
if (si.si_signo == SIGSTOP)
{
sig_clear (SIGCONT);
sig_clear (SIGCONT, false);
goto stop;
}

/* Clear pending SIGCONT on stop signals */
if (si.si_signo == SIGTSTP || si.si_signo == SIGTTIN
|| si.si_signo == SIGTTOU)
sig_clear (SIGCONT);
sig_clear (SIGCONT, false);

if (handler == (void *) SIG_DFL)
{
Expand Down
2 changes: 1 addition & 1 deletion winsup/cygwin/local_includes/sigproc.h
Original file line number Diff line number Diff line change
Expand Up @@ -63,7 +63,7 @@ void set_signal_mask (sigset_t&, sigset_t);
int handle_sigprocmask (int sig, const sigset_t *set,
sigset_t *oldset, sigset_t& opmask);

void sig_clear (int);
void sig_clear (int, bool);
void sig_set_pending (int);
int handle_sigsuspend (sigset_t);

Expand Down
4 changes: 2 additions & 2 deletions winsup/cygwin/signal.cc
Original file line number Diff line number Diff line change
Expand Up @@ -451,9 +451,9 @@ sigaction_worker (int sig, const struct sigaction *newact,
if (!(gs.sa_flags & SA_NODEFER))
gs.sa_mask |= SIGTOMASK(sig);
if (gs.sa_handler == SIG_IGN)
sig_clear (sig);
sig_clear (sig, true);
if (gs.sa_handler == SIG_DFL && sig == SIGCHLD)
sig_clear (sig);
sig_clear (sig, true);
if (sig == SIGCHLD)
{
myself->process_state &= ~PID_NOCLDSTOP;
Expand Down
28 changes: 23 additions & 5 deletions winsup/cygwin/sigproc.cc
Original file line number Diff line number Diff line change
Expand Up @@ -106,12 +106,16 @@ class pending_signals
{
sigpacket sigs[_NSIG + 1];
sigpacket start;
SRWLOCK queue_lock;
bool retry;
void lock () { AcquireSRWLockExclusive (&queue_lock); }
void unlock () { ReleaseSRWLockExclusive (&queue_lock); }

public:
pending_signals (): queue_lock (SRWLOCK_INIT) {}
void add (sigpacket&);
bool pending () {retry = !!start.next; return retry;}
void clear (int sig);
void clear (int sig, bool need_lock);
void clear (_cygtls *tls);
friend void sig_dispatch_pending (bool);
friend void wait_sig (VOID *arg);
Expand Down Expand Up @@ -427,23 +431,27 @@ proc_terminate ()

/* Clear pending signal */
void
sig_clear (int sig)
sig_clear (int sig, bool need_lock)
{
sigq.clear (sig);
sigq.clear (sig, need_lock);
}

/* Clear pending signals of specific si_signo.
Called from sigpacket::process(). */
void
pending_signals::clear (int sig)
pending_signals::clear (int sig, bool need_lock)
{
sigpacket *q = sigs + sig;
if (!sig || !q->si.si_signo)
return;
if (need_lock)
lock ();
q->si.si_signo = 0;
q->prev->next = q->next;
if (q->next)
q->next->prev = q->prev;
if (need_lock)
unlock ();
}

/* Clear pending signals of specific thread. Called under TLS lock from
Expand All @@ -453,6 +461,7 @@ pending_signals::clear (_cygtls *tls)
{
sigpacket *q = &start;

lock ();
while ((q = q->next))
if (q->sigtls == tls)
{
Expand All @@ -461,6 +470,7 @@ pending_signals::clear (_cygtls *tls)
if (q->next)
q->next->prev = q->prev;
}
unlock ();
}

/* Clear pending signals of specific thread. Called from _cygtls::remove */
Expand Down Expand Up @@ -1313,11 +1323,13 @@ pending_signals::add (sigpacket& pack)
if (se->si.si_signo)
return;
*se = pack;
lock ();
se->next = start.next;
se->prev = &start;
se->prev->next = se;
if (se->next)
se->next->prev = se;
unlock ();
}

/* Process signals by waiting for signal data to arrive in a pipe.
Expand Down Expand Up @@ -1398,6 +1410,7 @@ wait_sig (VOID *)
bool issig_wait;

*pack.mask = 0;
sigq.lock ();
while ((q = q->next))
{
_cygtls *sigtls = q->sigtls ?: _main_tls;
Expand All @@ -1411,6 +1424,7 @@ wait_sig (VOID *)
}
}
}
sigq.unlock ();
}
break;
case __SIGPENDING:
Expand All @@ -1419,6 +1433,7 @@ wait_sig (VOID *)

*pack.mask = 0;
tl_entry = cygheap->find_tls (pack.sigtls);
sigq.lock ();
while ((q = q->next))
{
/* Skip thread-specific signals for other threads. */
Expand All @@ -1427,6 +1442,7 @@ wait_sig (VOID *)
if (pack.sigtls->sigmask & (bit = SIGTOMASK (q->si.si_signo)))
*pack.mask |= bit;
}
sigq.unlock ();
cygheap->unlock_tls (tl_entry);
}
break;
Expand Down Expand Up @@ -1461,7 +1477,7 @@ wait_sig (VOID *)
break;
default: /* Normal (positive) signal */
if (pack.si.si_signo < 0)
sig_clear (-pack.si.si_signo);
sig_clear (-pack.si.si_signo, true);
else
sigq.add (pack);
fallthrough;
Expand All @@ -1474,6 +1490,7 @@ wait_sig (VOID *)
{
/* Check the queue for signals. There will always be at least one
thing on the queue if this was a valid signal. */
sigq.lock ();
while ((q = q->next))
{
if (q->si.si_signo && q->process () > 0)
Expand All @@ -1484,6 +1501,7 @@ wait_sig (VOID *)
q->next->prev = q->prev;
}
}
sigq.unlock ();
/* At least one signal still queued? The event is used in select
only, and only to decide if WFMO should wake up in case a
signalfd is waiting via select/poll for being ready to read a
Expand Down

0 comments on commit b38ba4b

Please sign in to comment.