Skip to content

Commit b38ba4b

Browse files
tyan0github-cygwin
authored andcommitted
Cygwin: signal: Introduce a lock for the signal queue
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 <[email protected]> Reviewed-by: Corinna Vinschen <[email protected]> Signed-off-by: Takashi Yano <[email protected]> (cherry picked from commit 496fa7b)
1 parent 5bd4f99 commit b38ba4b

File tree

4 files changed

+32
-14
lines changed

4 files changed

+32
-14
lines changed

winsup/cygwin/exceptions.cc

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1448,10 +1448,10 @@ _cygtls::handle_SIGCONT (threadlist_t * &tl_entry)
14481448
sigsent = true;
14491449
}
14501450
/* Clear pending stop signals */
1451-
sig_clear (SIGSTOP);
1452-
sig_clear (SIGTSTP);
1453-
sig_clear (SIGTTIN);
1454-
sig_clear (SIGTTOU);
1451+
sig_clear (SIGSTOP, false);
1452+
sig_clear (SIGTSTP, false);
1453+
sig_clear (SIGTTIN, false);
1454+
sig_clear (SIGTTOU, false);
14551455
}
14561456

14571457
int
@@ -1552,14 +1552,14 @@ sigpacket::process ()
15521552
goto exit_sig;
15531553
if (si.si_signo == SIGSTOP)
15541554
{
1555-
sig_clear (SIGCONT);
1555+
sig_clear (SIGCONT, false);
15561556
goto stop;
15571557
}
15581558

15591559
/* Clear pending SIGCONT on stop signals */
15601560
if (si.si_signo == SIGTSTP || si.si_signo == SIGTTIN
15611561
|| si.si_signo == SIGTTOU)
1562-
sig_clear (SIGCONT);
1562+
sig_clear (SIGCONT, false);
15631563

15641564
if (handler == (void *) SIG_DFL)
15651565
{

winsup/cygwin/local_includes/sigproc.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -63,7 +63,7 @@ void set_signal_mask (sigset_t&, sigset_t);
6363
int handle_sigprocmask (int sig, const sigset_t *set,
6464
sigset_t *oldset, sigset_t& opmask);
6565

66-
void sig_clear (int);
66+
void sig_clear (int, bool);
6767
void sig_set_pending (int);
6868
int handle_sigsuspend (sigset_t);
6969

winsup/cygwin/signal.cc

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -451,9 +451,9 @@ sigaction_worker (int sig, const struct sigaction *newact,
451451
if (!(gs.sa_flags & SA_NODEFER))
452452
gs.sa_mask |= SIGTOMASK(sig);
453453
if (gs.sa_handler == SIG_IGN)
454-
sig_clear (sig);
454+
sig_clear (sig, true);
455455
if (gs.sa_handler == SIG_DFL && sig == SIGCHLD)
456-
sig_clear (sig);
456+
sig_clear (sig, true);
457457
if (sig == SIGCHLD)
458458
{
459459
myself->process_state &= ~PID_NOCLDSTOP;

winsup/cygwin/sigproc.cc

Lines changed: 23 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -106,12 +106,16 @@ class pending_signals
106106
{
107107
sigpacket sigs[_NSIG + 1];
108108
sigpacket start;
109+
SRWLOCK queue_lock;
109110
bool retry;
111+
void lock () { AcquireSRWLockExclusive (&queue_lock); }
112+
void unlock () { ReleaseSRWLockExclusive (&queue_lock); }
110113

111114
public:
115+
pending_signals (): queue_lock (SRWLOCK_INIT) {}
112116
void add (sigpacket&);
113117
bool pending () {retry = !!start.next; return retry;}
114-
void clear (int sig);
118+
void clear (int sig, bool need_lock);
115119
void clear (_cygtls *tls);
116120
friend void sig_dispatch_pending (bool);
117121
friend void wait_sig (VOID *arg);
@@ -427,23 +431,27 @@ proc_terminate ()
427431

428432
/* Clear pending signal */
429433
void
430-
sig_clear (int sig)
434+
sig_clear (int sig, bool need_lock)
431435
{
432-
sigq.clear (sig);
436+
sigq.clear (sig, need_lock);
433437
}
434438

435439
/* Clear pending signals of specific si_signo.
436440
Called from sigpacket::process(). */
437441
void
438-
pending_signals::clear (int sig)
442+
pending_signals::clear (int sig, bool need_lock)
439443
{
440444
sigpacket *q = sigs + sig;
441445
if (!sig || !q->si.si_signo)
442446
return;
447+
if (need_lock)
448+
lock ();
443449
q->si.si_signo = 0;
444450
q->prev->next = q->next;
445451
if (q->next)
446452
q->next->prev = q->prev;
453+
if (need_lock)
454+
unlock ();
447455
}
448456

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

464+
lock ();
456465
while ((q = q->next))
457466
if (q->sigtls == tls)
458467
{
@@ -461,6 +470,7 @@ pending_signals::clear (_cygtls *tls)
461470
if (q->next)
462471
q->next->prev = q->prev;
463472
}
473+
unlock ();
464474
}
465475

466476
/* Clear pending signals of specific thread. Called from _cygtls::remove */
@@ -1313,11 +1323,13 @@ pending_signals::add (sigpacket& pack)
13131323
if (se->si.si_signo)
13141324
return;
13151325
*se = pack;
1326+
lock ();
13161327
se->next = start.next;
13171328
se->prev = &start;
13181329
se->prev->next = se;
13191330
if (se->next)
13201331
se->next->prev = se;
1332+
unlock ();
13211333
}
13221334

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

14001412
*pack.mask = 0;
1413+
sigq.lock ();
14011414
while ((q = q->next))
14021415
{
14031416
_cygtls *sigtls = q->sigtls ?: _main_tls;
@@ -1411,6 +1424,7 @@ wait_sig (VOID *)
14111424
}
14121425
}
14131426
}
1427+
sigq.unlock ();
14141428
}
14151429
break;
14161430
case __SIGPENDING:
@@ -1419,6 +1433,7 @@ wait_sig (VOID *)
14191433

14201434
*pack.mask = 0;
14211435
tl_entry = cygheap->find_tls (pack.sigtls);
1436+
sigq.lock ();
14221437
while ((q = q->next))
14231438
{
14241439
/* Skip thread-specific signals for other threads. */
@@ -1427,6 +1442,7 @@ wait_sig (VOID *)
14271442
if (pack.sigtls->sigmask & (bit = SIGTOMASK (q->si.si_signo)))
14281443
*pack.mask |= bit;
14291444
}
1445+
sigq.unlock ();
14301446
cygheap->unlock_tls (tl_entry);
14311447
}
14321448
break;
@@ -1461,7 +1477,7 @@ wait_sig (VOID *)
14611477
break;
14621478
default: /* Normal (positive) signal */
14631479
if (pack.si.si_signo < 0)
1464-
sig_clear (-pack.si.si_signo);
1480+
sig_clear (-pack.si.si_signo, true);
14651481
else
14661482
sigq.add (pack);
14671483
fallthrough;
@@ -1474,6 +1490,7 @@ wait_sig (VOID *)
14741490
{
14751491
/* Check the queue for signals. There will always be at least one
14761492
thing on the queue if this was a valid signal. */
1493+
sigq.lock ();
14771494
while ((q = q->next))
14781495
{
14791496
if (q->si.si_signo && q->process () > 0)
@@ -1484,6 +1501,7 @@ wait_sig (VOID *)
14841501
q->next->prev = q->prev;
14851502
}
14861503
}
1504+
sigq.unlock ();
14871505
/* At least one signal still queued? The event is used in select
14881506
only, and only to decide if WFMO should wake up in case a
14891507
signalfd is waiting via select/poll for being ready to read a

0 commit comments

Comments
 (0)