rpcbind: Fix race in signal termination

If a signal was delivered while the main thread was not in poll(2) and after
check was performed, we could reenter poll and never detect termination. Fix
this with the pipefd trick.  (This race was introduced very recently, in
r327482.)

PR:		224503
Reported by:	kib
Reviewed by:	kib, markj
Sponsored by:	Dell EMC Isilon
This commit is contained in:
Conrad Meyer 2018-01-02 17:25:13 +00:00
parent 8b081eaa55
commit 35f85edc80
Notes: svn2git 2020-12-20 02:59:44 +00:00
svn path=/head/; revision=327495
3 changed files with 37 additions and 12 deletions

View file

@ -1101,7 +1101,7 @@ void
my_svc_run(void)
{
size_t nfds;
struct pollfd pollfds[FD_SETSIZE];
struct pollfd pollfds[FD_SETSIZE + 1];
int poll_ret, check_ret;
int n;
#ifdef SVC_RUN_DEBUG
@ -1112,6 +1112,9 @@ my_svc_run(void)
for (;;) {
p = pollfds;
p->fd = terminate_rfd;
p->events = MASKVAL;
p++;
for (n = 0; n <= svc_maxfd; n++) {
if (FD_ISSET(n, &svc_fdset)) {
p->fd = n;
@ -1130,23 +1133,26 @@ my_svc_run(void)
fprintf(stderr, ">\n");
}
#endif
switch (poll_ret = poll(pollfds, nfds, 30 * 1000)) {
poll_ret = poll(pollfds, nfds, 30 * 1000);
if (doterminate != 0) {
close(rpcbindlockfd);
#ifdef WARMSTART
syslog(LOG_ERR,
"rpcbind terminating on signal %d. Restart with \"rpcbind -w\"",
(int)doterminate);
write_warmstart(); /* Dump yourself */
#endif
exit(2);
}
switch (poll_ret) {
case -1:
/*
* We ignore all errors, continuing with the assumption
* that it was set by the signal handlers (or any
* other outside event) and not caused by poll().
*/
if (doterminate != 0) {
close(rpcbindlockfd);
#ifdef WARMSTART
syslog(LOG_ERR,
"rpcbind terminating on signal %d. Restart with \"rpcbind -w\"",
(int)doterminate);
write_warmstart(); /* Dump yourself */
#endif
exit(2);
}
case 0:
cleanfds = svc_fdset;
__svc_clean_idle(&cleanfds, 30, FALSE);

View file

@ -79,6 +79,7 @@ static char sccsid[] = "@(#)rpcbind.c 1.35 89/04/21 Copyr 1984 Sun Micro";
/* Global variables */
int debugging = 0; /* Tell me what's going on */
int doabort = 0; /* When debugging, do an abort on errors */
int terminate_rfd; /* Pipefd to wake on signal */
volatile sig_atomic_t doterminate = 0; /* Terminal signal received */
rpcblist_ptr list_rbl; /* A list of version 3/4 rpcbind services */
int rpcbindlockfd;
@ -101,6 +102,7 @@ static struct sockaddr **bound_sa;
static int ipv6_only = 0;
static int nhosts = 0;
static int on = 1;
static int terminate_wfd;
#ifdef WARMSTART
/* Local Variable */
@ -133,6 +135,7 @@ main(int argc, char *argv[])
void *nc_handle; /* Net config handle */
struct rlimit rl;
int maxrec = RPC_MAXDATASIZE;
int error, fds[2];
parseargs(argc, argv);
@ -192,6 +195,16 @@ main(int argc, char *argv[])
}
endnetconfig(nc_handle);
/*
* Allocate pipe fd to wake main thread from signal handler in non-racy
* way.
*/
error = pipe(fds);
if (error != 0)
err(1, "pipe failed");
terminate_rfd = fds[0];
terminate_wfd = fds[1];
/* catch the usual termination signals for graceful exit */
(void) signal(SIGCHLD, reap);
(void) signal(SIGINT, terminate);
@ -761,8 +774,13 @@ rbllist_add(rpcprog_t prog, rpcvers_t vers, struct netconfig *nconf,
static void
terminate(int signum)
{
char c = '\0';
ssize_t wr;
doterminate = signum;
wr = write(terminate_wfd, &c, 1);
if (wr < 1)
_exit(2);
}
void

View file

@ -70,6 +70,7 @@ struct r_rmtcall_args {
extern int debugging;
extern int doabort;
extern int terminate_rfd;
extern volatile sig_atomic_t doterminate;
#ifdef LIBWRAP
extern int libwrap;