Skip site navigation (1)Skip section navigation (2)
Date:      Tue, 2 Jan 2018 17:25:13 +0000 (UTC)
From:      Conrad Meyer <cem@FreeBSD.org>
To:        src-committers@freebsd.org, svn-src-all@freebsd.org, svn-src-head@freebsd.org
Subject:   svn commit: r327495 - head/usr.sbin/rpcbind
Message-ID:  <201801021725.w02HPDaj068477@repo.freebsd.org>

next in thread | raw e-mail | index | archive | help
Author: cem
Date: Tue Jan  2 17:25:13 2018
New Revision: 327495
URL: https://svnweb.freebsd.org/changeset/base/327495

Log:
  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

Modified:
  head/usr.sbin/rpcbind/rpcb_svc_com.c
  head/usr.sbin/rpcbind/rpcbind.c
  head/usr.sbin/rpcbind/rpcbind.h

Modified: head/usr.sbin/rpcbind/rpcb_svc_com.c
==============================================================================
--- head/usr.sbin/rpcbind/rpcb_svc_com.c	Tue Jan  2 16:50:57 2018	(r327494)
+++ head/usr.sbin/rpcbind/rpcb_svc_com.c	Tue Jan  2 17:25:13 2018	(r327495)
@@ -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);

Modified: head/usr.sbin/rpcbind/rpcbind.c
==============================================================================
--- head/usr.sbin/rpcbind/rpcbind.c	Tue Jan  2 16:50:57 2018	(r327494)
+++ head/usr.sbin/rpcbind/rpcbind.c	Tue Jan  2 17:25:13 2018	(r327495)
@@ -79,6 +79,7 @@ static	char sccsid[] = "@(#)rpcbind.c 1.35 89/04/21 Co
 /* 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 net
 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

Modified: head/usr.sbin/rpcbind/rpcbind.h
==============================================================================
--- head/usr.sbin/rpcbind/rpcbind.h	Tue Jan  2 16:50:57 2018	(r327494)
+++ head/usr.sbin/rpcbind/rpcbind.h	Tue Jan  2 17:25:13 2018	(r327495)
@@ -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;



Want to link to this message? Use this URL: <https://mail-archive.FreeBSD.org/cgi/mid.cgi?201801021725.w02HPDaj068477>