Skip to content

Commit 86d5a20

Browse files
authored
Merge pull request #8193 from mjstapp/fix_signals_7_5
[7.5] lib: fix signal-handling race in main event loop
2 parents 2a8e69f + b339cc1 commit 86d5a20

File tree

4 files changed

+145
-13
lines changed

4 files changed

+145
-13
lines changed

lib/sigevent.c

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -63,6 +63,33 @@ static void quagga_signal_handler(int signo)
6363
sigmaster.caught = 1;
6464
}
6565

66+
/*
67+
* Check whether any signals have been received and are pending. This is done
68+
* with the application's key signals blocked. The complete set of signals
69+
* is returned in 'setp', so the caller can restore them when appropriate.
70+
* If there are pending signals, returns 'true', 'false' otherwise.
71+
*/
72+
bool frr_sigevent_check(sigset_t *setp)
73+
{
74+
sigset_t blocked;
75+
int i;
76+
bool ret;
77+
78+
sigemptyset(setp);
79+
sigemptyset(&blocked);
80+
81+
/* Set up mask of application's signals */
82+
for (i = 0; i < sigmaster.sigc; i++)
83+
sigaddset(&blocked, sigmaster.signals[i].signal);
84+
85+
pthread_sigmask(SIG_BLOCK, &blocked, setp);
86+
87+
/* Now that the application's signals are blocked, test. */
88+
ret = (sigmaster.caught != 0);
89+
90+
return ret;
91+
}
92+
6693
/* check if signals have been caught and run appropriate handlers */
6794
int quagga_sigevent_process(void)
6895
{

lib/sigevent.h

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -48,6 +48,15 @@ struct quagga_signal_t {
4848
extern void signal_init(struct thread_master *m, int sigc,
4949
struct quagga_signal_t *signals);
5050

51+
52+
/*
53+
* Check whether any signals have been received and are pending. This is done
54+
* with the application's key signals blocked. The complete set of signals
55+
* is returned in 'setp', so the caller can restore them when appropriate.
56+
* If there are pending signals, returns 'true', 'false' otherwise.
57+
*/
58+
bool frr_sigevent_check(sigset_t *setp);
59+
5160
/* check whether there are signals to handle, process any found */
5261
extern int quagga_sigevent_process(void);
5362

lib/thread.c

Lines changed: 106 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -723,9 +723,13 @@ static void thread_free(struct thread_master *master, struct thread *thread)
723723
XFREE(MTYPE_THREAD, thread);
724724
}
725725

726-
static int fd_poll(struct thread_master *m, struct pollfd *pfds, nfds_t pfdsize,
727-
nfds_t count, const struct timeval *timer_wait)
726+
static int fd_poll(struct thread_master *m, const struct timeval *timer_wait,
727+
bool *eintr_p)
728728
{
729+
sigset_t origsigs;
730+
unsigned char trash[64];
731+
nfds_t count = m->handler.copycount;
732+
729733
/*
730734
* If timer_wait is null here, that means poll() should block
731735
* indefinitely, unless the thread_master has overridden it by setting
@@ -756,15 +760,58 @@ static int fd_poll(struct thread_master *m, struct pollfd *pfds, nfds_t pfdsize,
756760
rcu_assert_read_unlocked();
757761

758762
/* add poll pipe poker */
759-
assert(count + 1 < pfdsize);
760-
pfds[count].fd = m->io_pipe[0];
761-
pfds[count].events = POLLIN;
762-
pfds[count].revents = 0x00;
763+
assert(count + 1 < m->handler.pfdsize);
764+
m->handler.copy[count].fd = m->io_pipe[0];
765+
m->handler.copy[count].events = POLLIN;
766+
m->handler.copy[count].revents = 0x00;
767+
768+
/* We need to deal with a signal-handling race here: we
769+
* don't want to miss a crucial signal, such as SIGTERM or SIGINT,
770+
* that may arrive just before we enter poll(). We will block the
771+
* key signals, then check whether any have arrived - if so, we return
772+
* before calling poll(). If not, we'll re-enable the signals
773+
* in the ppoll() call.
774+
*/
763775

764-
num = poll(pfds, count + 1, timeout);
776+
sigemptyset(&origsigs);
777+
if (m->handle_signals) {
778+
/* Main pthread that handles the app signals */
779+
if (frr_sigevent_check(&origsigs)) {
780+
/* Signal to process - restore signal mask and return */
781+
pthread_sigmask(SIG_SETMASK, &origsigs, NULL);
782+
num = -1;
783+
*eintr_p = true;
784+
goto done;
785+
}
786+
} else {
787+
/* Don't make any changes for the non-main pthreads */
788+
pthread_sigmask(SIG_SETMASK, NULL, &origsigs);
789+
}
765790

766-
unsigned char trash[64];
767-
if (num > 0 && pfds[count].revents != 0 && num--)
791+
#if defined(HAVE_PPOLL)
792+
struct timespec ts, *tsp;
793+
794+
if (timeout >= 0) {
795+
ts.tv_sec = timeout / 1000;
796+
ts.tv_nsec = (timeout % 1000) * 1000000;
797+
tsp = &ts;
798+
} else
799+
tsp = NULL;
800+
801+
num = ppoll(m->handler.copy, count + 1, tsp, &origsigs);
802+
pthread_sigmask(SIG_SETMASK, &origsigs, NULL);
803+
#else
804+
/* Not ideal - there is a race after we restore the signal mask */
805+
pthread_sigmask(SIG_SETMASK, &origsigs, NULL);
806+
num = poll(m->handler.copy, count + 1, timeout);
807+
#endif
808+
809+
done:
810+
811+
if (num < 0 && errno == EINTR)
812+
*eintr_p = true;
813+
814+
if (num > 0 && m->handler.copy[count].revents != 0 && num--)
768815
while (read(m->io_pipe[0], &trash, sizeof(trash)) > 0)
769816
;
770817

@@ -1391,7 +1438,7 @@ struct thread *thread_fetch(struct thread_master *m, struct thread *fetch)
13911438
struct timeval zerotime = {0, 0};
13921439
struct timeval tv;
13931440
struct timeval *tw = NULL;
1394-
1441+
bool eintr_p = false;
13951442
int num = 0;
13961443

13971444
do {
@@ -1463,14 +1510,14 @@ struct thread *thread_fetch(struct thread_master *m, struct thread *fetch)
14631510

14641511
pthread_mutex_unlock(&m->mtx);
14651512
{
1466-
num = fd_poll(m, m->handler.copy, m->handler.pfdsize,
1467-
m->handler.copycount, tw);
1513+
eintr_p = false;
1514+
num = fd_poll(m, tw, &eintr_p);
14681515
}
14691516
pthread_mutex_lock(&m->mtx);
14701517

14711518
/* Handle any errors received in poll() */
14721519
if (num < 0) {
1473-
if (errno == EINTR) {
1520+
if (eintr_p) {
14741521
pthread_mutex_unlock(&m->mtx);
14751522
/* loop around to signal handler */
14761523
continue;
@@ -1656,3 +1703,49 @@ void funcname_thread_execute(struct thread_master *m,
16561703
/* Give back or free thread. */
16571704
thread_add_unuse(m, thread);
16581705
}
1706+
1707+
/* Debug signal mask - if 'sigs' is NULL, use current effective mask. */
1708+
void debug_signals(const sigset_t *sigs)
1709+
{
1710+
int i, found;
1711+
sigset_t tmpsigs;
1712+
char buf[300];
1713+
1714+
/*
1715+
* We're only looking at the non-realtime signals here, so we need
1716+
* some limit value. Platform differences mean at some point we just
1717+
* need to pick a reasonable value.
1718+
*/
1719+
#if defined SIGRTMIN
1720+
# define LAST_SIGNAL SIGRTMIN
1721+
#else
1722+
# define LAST_SIGNAL 32
1723+
#endif
1724+
1725+
1726+
if (sigs == NULL) {
1727+
sigemptyset(&tmpsigs);
1728+
pthread_sigmask(SIG_BLOCK, NULL, &tmpsigs);
1729+
sigs = &tmpsigs;
1730+
}
1731+
1732+
found = 0;
1733+
buf[0] = '\0';
1734+
1735+
for (i = 0; i < LAST_SIGNAL; i++) {
1736+
char tmp[20];
1737+
1738+
if (sigismember(sigs, i) > 0) {
1739+
if (found > 0)
1740+
strlcat(buf, ",", sizeof(buf));
1741+
snprintf(tmp, sizeof(tmp), "%d", i);
1742+
strlcat(buf, tmp, sizeof(buf));
1743+
found++;
1744+
}
1745+
}
1746+
1747+
if (found == 0)
1748+
snprintf(buf, sizeof(buf), "<none>");
1749+
1750+
zlog_debug("%s: %s", __func__, buf);
1751+
}

lib/thread.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -233,6 +233,9 @@ extern pthread_key_t thread_current;
233233
extern char *thread_timer_to_hhmmss(char *buf, int buf_size,
234234
struct thread *t_timer);
235235

236+
/* Debug signal mask */
237+
void debug_signals(const sigset_t *sigs);
238+
236239
#ifdef __cplusplus
237240
}
238241
#endif

0 commit comments

Comments
 (0)