Please review this patch for inclusion in OpenSSL.

Following the recent thread in openssl-users here is a patch allow poll() interface to be used instead of select() in rand_unix.c.

This patch is necessary for big-fd users of openssl, as the libc implementation of FD_SET() may inadvertently scribble on memory in cases where the number of active file descriptors is greater than the standard maximum the FD_SET() macros expect. This is a fairly rare situation where the RNG is initialized during first use from the best available source of entropy, but nonetheless scribbling over memory is a serious defect.


Per platform portability issues:

* Please feel free to extend the first "#if defined(OPENSSL_SYS_XXXXXX)" with your Unix platform that also support the poll() kernel interface.

* If your platform is a select() user then please verify the IOWAIT_FD_SETSIZE is calculated correctly at compile time for your platform.


Best Regards,

Darryl


--
Darryl L. Miles


--- openssl-0.9.8b/crypto/rand/rand_unix.c	2005-08-29 00:20:48.000000000 +0100
+++ openssl-0.9.8b-me/crypto/rand/rand_unix.c	2006-06-27 02:07:22.000000000 +0100
@@ -126,6 +126,52 @@
 #include <unistd.h>
 #include <time.h>
 
+#if defined(OPENSSL_SYS_LINUX)
+ /* lets use poll() */
+ #include <sys/poll.h>
+ #define IOWAIT_VARS		struct pollfd pset; struct timeval t
+ #define IOWAIT_INIT(f, t)	do {							\
+					pset.fd = (f);					\
+					pset.events = POLLIN;				\
+					pset.revents = 0;				\
+					(t)->tv_sec = 0;				\
+					(t)->tv_usec = 10*1000;				\
+					/* Spend 10ms on each file. */			\
+				} while(0)
+ #define IOWAIT_FUNC(f, t)	poll(&pset, 1, ((t)->tv_sec * 1000) + ((t)->tv_usec / 1000))
+ #define IOWAIT_CHECK(f)	((pset.revents & POLLIN) != 0)
+#else
+ /* lets use select() */
+
+ /* For each platform we could do with making a guess at
+  *  how many FDs we support.  With glibc/Linux its possible
+  *  to use FD_SETSIZE directly, but this may not be very
+  *  portable. Another options was to use _POSIX_OPEN_MAX
+  *  but that value is a tad dull on modern hardware.  So
+  *  I ended up trying sizeof(fd_set)/8 which should be
+  *  closer to the real value.
+  * If this causes a problem on your platform because we
+  *  can not guess correctly then set it to zero.
+  */
+ #if defined(OPENSSL_SYS_LINUX)
+  #define IOWAIT_FD_SETSIZE	(FD_SETSIZE)
+ #else
+  /* fallback method */
+  #define IOWAIT_FD_SETSIZE	(sizeof(fd_set) / 8)
+ #endif
+ #define IOWAIT_VARS		fd_set fset; struct timeval t
+ #define IOWAIT_INIT(f, t)	do {							\
+					FD_ZERO(&fset);					\
+					if(IOWAIT_FD_SETSIZE > 0 && (f) >= IOWAIT_FD_SETSIZE) { break; }	\
+					FD_SET((f), &fset);				\
+					(t)->tv_sec = 0;				\
+					(t)->tv_usec = 10*1000;				\
+					/* Spend 10ms on each file. */			\
+				} while(0)
+ #define IOWAIT_FUNC(f, t)	select((f)+1,&fset,NULL,NULL,(t))
+ #define IOWAIT_CHECK(f)	FD_ISSET((f), &fset)
+#endif
+
 #ifdef __OpenBSD__
 int RAND_poll(void)
 {
@@ -184,11 +230,9 @@
 #endif
 			)) >= 0)
 			{
-			struct timeval t = { 0, 10*1000 }; /* Spend 10ms on
-							      each file. */
 			int r;
 			size_t j;
-			fd_set fset;
+			IOWAIT_VARS;
 			struct stat *st=&randomstats[i];
 
 			/* Avoid using same input... Used to be O_NOFOLLOW
@@ -204,13 +248,12 @@
 
 			do
 				{
-				FD_ZERO(&fset);
-				FD_SET(fd, &fset);
 				r = -1;
+				IOWAIT_INIT(fd, &t);
 
-				if (select(fd+1,&fset,NULL,NULL,&t) < 0)
+				if (IOWAIT_FUNC(fd, &t) < 0)
 					t.tv_usec=0;
-				else if (FD_ISSET(fd, &fset))
+				else if (IOWAIT_CHECK(fd))
 					{
 					r=read(fd,(unsigned char *)tmpbuf+n,
 					       ENTROPY_NEEDED-n);

Reply via email to