Blame SOURCES/rsyslog-8.24.0-rhbz1858297-buffer-overflow.patch

85d676
From 493279b790a8cdace8ccbc2c5136985e820dd2fa Mon Sep 17 00:00:00 2001
85d676
From: Jiri Vymazal <jvymazal@redhat.com>
85d676
Date: Thu, 6 Aug 2020 16:21:03 +0000
85d676
Subject: [PATCH] imudp & nsdsel_ptcp: replace select() by poll()
85d676
85d676
select was used when epoll was not available. This could cause issues
85d676
if during startup fds > 1024 were generated. This is extremely unlikely
85d676
and we have never seen any such report in practice. Nevertheless, it is
85d676
better to have this fixed. The performance will probably also increase 
85d676
in most cases.
85d676
85d676
Note this is not a replacement for the epoll drivers, but a general
85d676
stability improvement when epoll() is not available for some reason.
85d676
85d676
closes https://github.com/rsyslog/rsyslog/issues/2615
85d676
closes https://github.com/rsyslog/rsyslog/issues/1728
85d676
closes https://github.com/rsyslog/rsyslog/issues/1459
85d676
---
85d676
 plugins/imudp/imudp.c |  73 ++++++++++++++++++++++++++-----------------
85d676
 runtime/nsdsel_ptcp.c | 142 ++++++++++++++++++++----------------------
85d676
 runtime/nsdsel_ptcp.h |  14 ++---
85d676
 runtime/rsyslog.h     |   1 +
85d676
 4 files changed, 116 insertions(+), 114 deletions(-)
85d676
85d676
diff --git a/plugins/imudp/imudp.c b/plugins/imudp/imudp.c
85d676
index 4303f0b952..24d90ab665 100644
85d676
--- a/plugins/imudp/imudp.c
85d676
+++ b/plugins/imudp/imudp.c
85d676
@@ -34,6 +34,7 @@
85d676
 #include <sys/socket.h>
85d676
 #include <pthread.h>
85d676
 #include <signal.h>
85d676
+#include <poll.h>
85d676
 #ifdef HAVE_SYS_EPOLL_H
85d676
 #	include <sys/epoll.h>
85d676
 #endif
85d676
@@ -836,61 +837,75 @@ rcvMainLoop(struct wrkrInfo_s *const __restrict__ pWrkr)
85d676
 }
85d676
 #else /* #if HAVE_EPOLL_CREATE1 */
85d676
 /* this is the code for the select() interface */
85d676
-static rsRetVal
85d676
+static rsRetVal
85d676
 rcvMainLoop(struct wrkrInfo_s *const __restrict__ pWrkr)
85d676
 {
85d676
 	DEFiRet;
85d676
-	int maxfds;
85d676
 	int nfds;
85d676
-	fd_set readfds;
85d676
 	struct sockaddr_storage frominetPrev;
85d676
 	int bIsPermitted;
85d676
+	int i = 0;
85d676
 	struct lstn_s *lstn;
85d676
 
85d676
+	DBGPRINTF("imudp uses poll() [ex-select]\n");
85d676
 	/* start "name caching" algo by making sure the previous system indicator
85d676
-	 * is invalidated.
85d676
-	 */
85d676
+	 * is invalidated. */
85d676
 	bIsPermitted = 0;
85d676
 	memset(&frominetPrev, 0, sizeof(frominetPrev));
85d676
-	DBGPRINTF("imudp uses select()\n");
85d676
 
85d676
-	while(1) {
85d676
-		/* Add the Unix Domain Sockets to the list of read descriptors.
85d676
-		 */
85d676
-	        maxfds = 0;
85d676
-	        FD_ZERO(&readfds);
85d676
-
85d676
-		/* Add the UDP listen sockets to the list of read descriptors. */
85d676
-		for(lstn = lcnfRoot ; lstn != NULL ; lstn = lstn->next) {
85d676
-			if (lstn->sock != -1) {
85d676
-				if(Debug)
85d676
-					net.debugListenInfo(lstn->sock, (char*)"UDP");
85d676
-				FD_SET(lstn->sock, &readfds);
85d676
-				if(lstn->sock>maxfds) maxfds=lstn->sock;
85d676
+	/* setup poll() subsystem */
85d676
+	int nfd = 0;
85d676
+	for(lstn = lcnfRoot ; lstn != NULL ; lstn = lstn->next) {
85d676
+		if(lstn->sock != -1) {
85d676
+			if(Debug) {
85d676
+				net.debugListenInfo(lstn->sock, (char*)"UDP");
85d676
 			}
85d676
+			++nfd;
85d676
 		}
85d676
-		if(Debug) {
85d676
-			dbgprintf("--------imUDP calling select, active file descriptors (max %d): ", maxfds);
85d676
-			for (nfds = 0; nfds <= maxfds; ++nfds)
85d676
-				if(FD_ISSET(nfds, &readfds))
85d676
-					dbgprintf("%d ", nfds);
85d676
-			dbgprintf("\n");
85d676
+	}
85d676
+	struct pollfd *const pollfds = calloc(nfd, sizeof(struct pollfd));
85d676
+	CHKmalloc(pollfds);
85d676
+
85d676
+	for(lstn = lcnfRoot ; lstn != NULL ; lstn = lstn->next) {
85d676
+		assert(i < nfd);
85d676
+		if (lstn->sock != -1) {
85d676
+			pollfds[i].fd = lstn->sock;
85d676
+			pollfds[i].events = POLLIN;
85d676
+			++i;
85d676
 		}
85d676
+	}
85d676
 
85d676
-		/* wait for io to become ready */
85d676
-		nfds = select(maxfds+1, (fd_set *) &readfds, NULL, NULL, NULL);
85d676
+	while(1) {
85d676
+		DBGPRINTF("--------imudp calling poll() on %d fds\n", nfd);
85d676
+		nfds = poll(pollfds, nfd, -1);
85d676
 		if(glbl.GetGlobalInputTermState() == 1)
85d676
 			break; /* terminate input! */
85d676
 
85d676
+		if(nfds < 0) {
85d676
+			if(errno == EINTR) {
85d676
+				DBGPRINTF("imudp: EINTR occured\n");
85d676
+			} else {
85d676
+				LogMsg(errno, RS_RET_POLL_ERR, LOG_WARNING, "imudp: poll "
85d676
+					"system call failed, may cause further troubles");
85d676
+			}
85d676
+			nfds = 0;
85d676
+		}
85d676
+
85d676
+		i = 0;
85d676
 		for(lstn = lcnfRoot ; nfds && lstn != NULL ; lstn = lstn->next) {
85d676
-			if(FD_ISSET(lstn->sock, &readfds)) {
85d676
+			assert(i < nfd);
85d676
+			if(glbl.GetGlobalInputTermState() == 1)
85d676
+				ABORT_FINALIZE(RS_RET_FORCE_TERM); /* terminate input! */
85d676
+			if(pollfds[i].revents & POLLIN) {
85d676
 		       		processSocket(pWrkr, lstn, &frominetPrev, &bIsPermitted);
85d676
-			--nfds; /* indicate we have processed one descriptor */
85d676
+				--nfds;
85d676
 			}
85d676
+			++i;
85d676
 	       }
85d676
 	       /* end of a run, back to loop for next recv() */
85d676
 	}
85d676
 
85d676
+finalize_it:
85d676
 	RETiRet;
85d676
 }
85d676
 #endif /* #if HAVE_EPOLL_CREATE1 */
85d676
diff --git a/runtime/nsdsel_ptcp.c b/runtime/nsdsel_ptcp.c
85d676
index e2cfca7caa..0ea6af4a1d 100644
85d676
--- a/runtime/nsdsel_ptcp.c
85d676
+++ b/runtime/nsdsel_ptcp.c
85d676
@@ -2,7 +2,7 @@
85d676
  *
85d676
  * An implementation of the nsd select() interface for plain tcp sockets.
85d676
  * 
85d676
- * Copyright 2008 Rainer Gerhards and Adiscon GmbH.
85d676
+ * Copyright 2008-2018 Rainer Gerhards and Adiscon GmbH.
85d676
  *
85d676
  * This file is part of the rsyslog runtime library.
85d676
  *
85d676
@@ -38,70 +38,61 @@
85d676
 #include "nsdsel_ptcp.h"
85d676
 #include "unlimited_select.h"
85d676
 
85d676
+#define FDSET_INCREMENT 1024 /* increment for struct pollfds array allocation */
85d676
 /* static data */
85d676
 DEFobjStaticHelpers
85d676
-DEFobjCurrIf(errmsg)
85d676
 DEFobjCurrIf(glbl)
85d676
 
85d676
 
85d676
-/* Standard-Constructor
85d676
- */
85d676
+/* Standard-Constructor */
85d676
 BEGINobjConstruct(nsdsel_ptcp) /* be sure to specify the object type also in END macro! */
85d676
-	pThis->maxfds = 0;
85d676
-#ifdef USE_UNLIMITED_SELECT
85d676
-        pThis->pReadfds = calloc(1, glbl.GetFdSetSize());
85d676
-        pThis->pWritefds = calloc(1, glbl.GetFdSetSize());
85d676
-#else
85d676
-	FD_ZERO(&pThis->readfds);
85d676
-	FD_ZERO(&pThis->writefds);
85d676
-#endif
85d676
+	pThis->currfds = 0;
85d676
+	pThis->maxfds = FDSET_INCREMENT;
85d676
+        CHKmalloc(pThis->fds = calloc(FDSET_INCREMENT, sizeof(struct pollfd)));
85d676
+finalize_it:
85d676
 ENDobjConstruct(nsdsel_ptcp)
85d676
 
85d676
 
85d676
 /* destructor for the nsdsel_ptcp object */
85d676
 BEGINobjDestruct(nsdsel_ptcp) /* be sure to specify the object type also in END and CODESTART macros! */
85d676
 CODESTARTobjDestruct(nsdsel_ptcp)
85d676
-#ifdef USE_UNLIMITED_SELECT
85d676
-	freeFdSet(pThis->pReadfds);
85d676
-	freeFdSet(pThis->pWritefds);
85d676
-#endif
85d676
+	free(pThis->fds);
85d676
 ENDobjDestruct(nsdsel_ptcp)
85d676
 
85d676
 
85d676
 /* Add a socket to the select set */
85d676
-static rsRetVal
85d676
-Add(nsdsel_t *pNsdsel, nsd_t *pNsd, nsdsel_waitOp_t waitOp)
85d676
+static rsRetVal
85d676
+Add(nsdsel_t *const pNsdsel, nsd_t *const pNsd, const nsdsel_waitOp_t waitOp)
85d676
 {
85d676
 	DEFiRet;
85d676
-	nsdsel_ptcp_t *pThis = (nsdsel_ptcp_t*) pNsdsel;
85d676
-	nsd_ptcp_t *pSock = (nsd_ptcp_t*) pNsd;
85d676
-#ifdef USE_UNLIMITED_SELECT
85d676
-        fd_set *pReadfds = pThis->pReadfds;
85d676
-        fd_set *pWritefds = pThis->pWritefds;
85d676
-#else
85d676
-        fd_set *pReadfds = &pThis->readfds;
85d676
-        fd_set *pWritefds = &pThis->writefds;
85d676
-#endif
85d676
-
85d676
+	nsdsel_ptcp_t *const pThis = (nsdsel_ptcp_t*) pNsdsel;
85d676
+	const nsd_ptcp_t *const pSock = (nsd_ptcp_t*) pNsd;
85d676
 	ISOBJ_TYPE_assert(pSock, nsd_ptcp);
85d676
 	ISOBJ_TYPE_assert(pThis, nsdsel_ptcp);
85d676
 
85d676
+	if(pThis->currfds == pThis->maxfds) {
85d676
+		struct pollfd *newfds;
85d676
+		CHKmalloc(newfds = realloc(pThis->fds,
85d676
+			sizeof(struct pollfd) * (pThis->maxfds + FDSET_INCREMENT)));
85d676
+		pThis->maxfds += FDSET_INCREMENT;
85d676
+		pThis->fds = newfds;
85d676
+	}
85d676
+
85d676
 	switch(waitOp) {
85d676
 		case NSDSEL_RD:
85d676
-			FD_SET(pSock->sock, pReadfds);
85d676
+			pThis->fds[pThis->currfds].events = POLLIN;
85d676
 			break;
85d676
 		case NSDSEL_WR:
85d676
-			FD_SET(pSock->sock, pWritefds);
85d676
+			pThis->fds[pThis->currfds].events = POLLOUT;
85d676
 			break;
85d676
 		case NSDSEL_RDWR:
85d676
-			FD_SET(pSock->sock, pReadfds);
85d676
-			FD_SET(pSock->sock, pWritefds);
85d676
+			pThis->fds[pThis->currfds].events = POLLIN | POLLOUT;
85d676
 			break;
85d676
 	}
85d676
+	pThis->fds[pThis->currfds].fd = pSock->sock;
85d676
+	++pThis->currfds;
85d676
 
85d676
-	if(pSock->sock > pThis->maxfds)
85d676
-		pThis->maxfds = pSock->sock;
85d676
-
85d676
+finalize_it:
85d676
 	RETiRet;
85d676
 }
85d676
 
85d676
@@ -109,71 +100,77 @@ Add(nsdsel_t *pNsdsel, nsd_t *pNsd, nsdsel_waitOp_t waitOp)
85d676
 /* perform the select()  piNumReady returns how many descriptors are ready for IO 
85d676
  * TODO: add timeout!
85d676
  */
85d676
-static rsRetVal
85d676
-Select(nsdsel_t *pNsdsel, int *piNumReady)
85d676
+static rsRetVal
85d676
+Select(nsdsel_t *const pNsdsel, int *const piNumReady)
85d676
 {
85d676
 	DEFiRet;
85d676
-	int i;
85d676
 	nsdsel_ptcp_t *pThis = (nsdsel_ptcp_t*) pNsdsel;
85d676
-#ifdef USE_UNLIMITED_SELECT
85d676
-        fd_set *pReadfds = pThis->pReadfds;
85d676
-        fd_set *pWritefds = pThis->pWritefds;
85d676
-#else
85d676
-        fd_set *pReadfds = &pThis->readfds;
85d676
-        fd_set *pWritefds = &pThis->writefds;
85d676
-#endif
85d676
 
85d676
 	ISOBJ_TYPE_assert(pThis, nsdsel_ptcp);
85d676
 	assert(piNumReady != NULL);
85d676
 
85d676
-	if(Debug) { // TODO: debug setting!
85d676
-		// TODO: name in dbgprintf!
85d676
-		dbgprintf("--------<NSDSEL_PTCP> calling select, active fds (max %d): ", pThis->maxfds);
85d676
-		for(i = 0; i <= pThis->maxfds; ++i)
85d676
-			if(FD_ISSET(i, pReadfds) || FD_ISSET(i, pWritefds))
85d676
-				dbgprintf("%d ", i);
85d676
+	assert(pThis->currfds >= 1);
85d676
+	if(Debug) {
85d676
+		dbgprintf("--------<NSDSEL_PTCP> calling poll, active fds (%d): ", pThis->currfds);
85d676
+		for(uint32_t i = 0; i <= pThis->currfds; ++i)
85d676
+			dbgprintf("%d ", pThis->fds[i].fd);
85d676
 		dbgprintf("\n");
85d676
 	}
85d676
 
85d676
 	/* now do the select */
85d676
-	*piNumReady = select(pThis->maxfds+1, pReadfds, pWritefds, NULL, NULL);
85d676
+	*piNumReady = poll(pThis->fds, pThis->currfds, -1);
85d676
+	if(*piNumReady < 0) {
85d676
+		if(errno == EINTR) {
85d676
+			DBGPRINTF("nsdsel_ptcp received EINTR\n");
85d676
+		} else {
85d676
+			LogMsg(errno, RS_RET_POLL_ERR, LOG_WARNING,
85d676
+				"ndssel_ptcp: poll system call failed, may cause further troubles");
85d676
+		}
85d676
+		*piNumReady = 0;
85d676
+	}
85d676
 
85d676
 	RETiRet;
85d676
 }
85d676
 
85d676
 
85d676
 /* check if a socket is ready for IO */
85d676
-static rsRetVal
85d676
-IsReady(nsdsel_t *pNsdsel, nsd_t *pNsd, nsdsel_waitOp_t waitOp, int *pbIsReady)
85d676
+static rsRetVal
85d676
+IsReady(nsdsel_t *const pNsdsel, nsd_t *const pNsd, const nsdsel_waitOp_t waitOp, int *const pbIsReady)
85d676
 {
85d676
 	DEFiRet;
85d676
-	nsdsel_ptcp_t *pThis = (nsdsel_ptcp_t*) pNsdsel;
85d676
-	nsd_ptcp_t *pSock = (nsd_ptcp_t*) pNsd;
85d676
-#ifdef USE_UNLIMITED_SELECT
85d676
-        fd_set *pReadfds = pThis->pReadfds;
85d676
-        fd_set *pWritefds = pThis->pWritefds;
85d676
-#else
85d676
-        fd_set *pReadfds = &pThis->readfds;
85d676
-        fd_set *pWritefds = &pThis->writefds;
85d676
-#endif
85d676
-
85d676
+	const nsdsel_ptcp_t *const pThis = (nsdsel_ptcp_t*) pNsdsel;
85d676
+	const nsd_ptcp_t *const pSock = (nsd_ptcp_t*) pNsd;
85d676
 	ISOBJ_TYPE_assert(pThis, nsdsel_ptcp);
85d676
 	ISOBJ_TYPE_assert(pSock, nsd_ptcp);
85d676
-	assert(pbIsReady != NULL);
85d676
+	const int sock = pSock->sock;
85d676
+	// TODO: consider doing a binary search
85d676
 
85d676
+	uint32_t idx;
85d676
+	for(idx = 0 ; idx < pThis->currfds ; ++idx) {
85d676
+		if(pThis->fds[idx].fd == sock)
85d676
+			break;
85d676
+	}
85d676
+	if(idx >= pThis->currfds) {
85d676
+		LogMsg(0, RS_RET_INTERNAL_ERROR, LOG_ERR,
85d676
+			"ndssel_ptcp: could not find socket %d which should be present", sock);
85d676
+		ABORT_FINALIZE(RS_RET_INTERNAL_ERROR);
85d676
+	}
85d676
+
85d676
+	const short revent = pThis->fds[idx].revents;
85d676
+	assert(!(revent & POLLNVAL));
85d676
 	switch(waitOp) {
85d676
 		case NSDSEL_RD:
85d676
-			*pbIsReady = FD_ISSET(pSock->sock, pReadfds);
85d676
+			*pbIsReady = revent & POLLIN;
85d676
 			break;
85d676
 		case NSDSEL_WR:
85d676
-			*pbIsReady = FD_ISSET(pSock->sock, pWritefds);
85d676
+			*pbIsReady = revent & POLLOUT;
85d676
 			break;
85d676
 		case NSDSEL_RDWR:
85d676
-			*pbIsReady =   FD_ISSET(pSock->sock, pReadfds)
85d676
-				     | FD_ISSET(pSock->sock, pWritefds);
85d676
+			*pbIsReady = revent & (POLLIN | POLLOUT);
85d676
 			break;
85d676
 	}
85d676
 
85d676
+finalize_it:
85d676
 	RETiRet;
85d676
 }
85d676
 
85d676
@@ -208,7 +205,6 @@ BEGINObjClassExit(nsdsel_ptcp, OBJ_IS_CORE_MODULE) /* CHANGE class also in END M
85d676
 CODESTARTObjClassExit(nsdsel_ptcp)
85d676
 	/* release objects we no longer need */
85d676
 	objRelease(glbl, CORE_COMPONENT);
85d676
-	objRelease(errmsg, CORE_COMPONENT);
85d676
 ENDObjClassExit(nsdsel_ptcp)
85d676
 
85d676
 
85d676
@@ -217,11 +213,5 @@ ENDObjClassExit(nsdsel_ptcp)
85d676
  * rgerhards, 2008-02-19
85d676
  */
85d676
 BEGINObjClassInit(nsdsel_ptcp, 1, OBJ_IS_CORE_MODULE) /* class, version */
85d676
-	/* request objects we use */
85d676
-	CHKiRet(objUse(errmsg, CORE_COMPONENT));
85d676
 	CHKiRet(objUse(glbl, CORE_COMPONENT));
85d676
-
85d676
-	/* set our own handlers */
85d676
 ENDObjClassInit(nsdsel_ptcp)
85d676
-/* vi:set ai:
85d676
- */
85d676
diff --git a/runtime/nsdsel_ptcp.h b/runtime/nsdsel_ptcp.h
85d676
index f9ec8210c2..87270c9f9f 100644
85d676
--- a/runtime/nsdsel_ptcp.h
85d676
+++ b/runtime/nsdsel_ptcp.h
85d676
@@ -24,20 +24,16 @@
85d676
 #ifndef INCLUDED_NSDSEL_PTCP_H
85d676
 #define INCLUDED_NSDSEL_PTCP_H
85d676
 
85d676
+#include <poll.h>
85d676
 #include "nsd.h"
85d676
 typedef nsdsel_if_t nsdsel_ptcp_if_t; /* we just *implement* this interface */
85d676
 
85d676
 /* the nsdsel_ptcp object */
85d676
 struct nsdsel_ptcp_s {
85d676
-	BEGINobjInstance;	/* Data to implement generic object - MUST be the first data element! */
85d676
-	int maxfds;
85d676
-#ifdef USE_UNLIMITED_SELECT
85d676
-	fd_set *pReadfds;
85d676
-	fd_set *pWritefds;
85d676
-#else
85d676
-	fd_set readfds;
85d676
-	fd_set writefds;
85d676
-#endif
85d676
+	BEGINobjInstance;
85d676
+	uint32_t maxfds;
85d676
+	uint32_t currfds;
85d676
+	struct pollfd *fds;
85d676
 };
85d676
 
85d676
 /* interface is defined in nsd.h, we just implement it! */
85d676
diff --git a/runtime/rsyslog.h b/runtime/rsyslog.h
85d676
index 9b40ba2159..e0dbab9af2 100644
85d676
--- a/runtime/rsyslog.h
85d676
+++ b/runtime/rsyslog.h
85d676
@@ -478,6 +478,7 @@
85d676
 	RS_RET_FILE_CHOWN_ERROR = -2434, /**< error during chown() */
85d676
 	RS_RET_RENAME_TMP_QI_ERROR = -2435, /**< renaming temporary .qi file failed */
85d676
 	RS_RET_ERR_SETENV = -2436, /**< error setting an environment variable */
85d676
+	RS_RET_POLL_ERR = -2444, /**< error in poll() system call */
85d676
 
85d676
 	/* RainerScript error messages (range 1000.. 1999) */
85d676
 	RS_RET_SYSVAR_NOT_FOUND = 1001, /**< system variable could not be found (maybe misspelled) */