Blame SOURCES/dnsmasq-2.81-optimize-fds-close.patch

f8d027
commit 98c6998116e33f9f34b798682e0695f4166bd86d
f8d027
Author: Simon Kelley <simon@thekelleys.org.uk>
f8d027
Date:   Mon Mar 2 17:10:25 2020 +0000
f8d027
f8d027
    Optimise closing file descriptors.
f8d027
    
f8d027
    Dnsmasq needs to close all the file descriptors it inherits, for security
f8d027
    reasons. This is traditionally done by calling close() on every possible
f8d027
    file descriptor (most of which won't be open.) On big servers where
f8d027
    "every possible file descriptor" is a rather large set, this gets
f8d027
    rather slow, so we use the /proc/<pid>/fd directory to get a list
f8d027
    of the fds which are acually open.
f8d027
    
f8d027
    This only works on Linux. On other platforms, and on Linux systems
f8d027
    without a /proc filesystem, we fall back to the old way.
f8d027
f8d027
diff --git a/src/dnsmasq.c b/src/dnsmasq.c
f8d027
index 573aac0..10f19ea 100644
f8d027
--- a/src/dnsmasq.c
f8d027
+++ b/src/dnsmasq.c
f8d027
@@ -138,20 +138,18 @@ int main (int argc, char **argv)
f8d027
     }
f8d027
 #endif
f8d027
   
f8d027
-  /* Close any file descriptors we inherited apart from std{in|out|err} 
f8d027
-     
f8d027
-     Ensure that at least stdin, stdout and stderr (fd 0, 1, 2) exist,
f8d027
+  /* Ensure that at least stdin, stdout and stderr (fd 0, 1, 2) exist,
f8d027
      otherwise file descriptors we create can end up being 0, 1, or 2 
f8d027
      and then get accidentally closed later when we make 0, 1, and 2 
f8d027
      open to /dev/null. Normally we'll be started with 0, 1 and 2 open, 
f8d027
      but it's not guaranteed. By opening /dev/null three times, we 
f8d027
      ensure that we're not using those fds for real stuff. */
f8d027
-  for (i = 0; i < max_fd; i++)
f8d027
-    if (i != STDOUT_FILENO && i != STDERR_FILENO && i != STDIN_FILENO)
f8d027
-      close(i);
f8d027
-    else
f8d027
-      open("/dev/null", O_RDWR); 
f8d027
-
f8d027
+  for (i = 0; i < 3; i++)
f8d027
+    open("/dev/null", O_RDWR); 
f8d027
+  
f8d027
+  /* Close any file descriptors we inherited apart from std{in|out|err} */
f8d027
+  close_fds(max_fd, -1, -1, -1);
f8d027
+  
f8d027
 #ifndef HAVE_LINUX_NETWORK
f8d027
 #  if !(defined(IP_RECVDSTADDR) && defined(IP_RECVIF) && defined(IP_SENDSRCADDR))
f8d027
   if (!option_bool(OPT_NOWILD))
f8d027
diff --git a/src/dnsmasq.h b/src/dnsmasq.h
f8d027
index 6103eb5..c46bfeb 100644
f8d027
--- a/src/dnsmasq.h
f8d027
+++ b/src/dnsmasq.h
f8d027
@@ -1283,7 +1283,7 @@ int memcmp_masked(unsigned char *a, unsigned char *b, int len,
f8d027
 int expand_buf(struct iovec *iov, size_t size);
f8d027
 char *print_mac(char *buff, unsigned char *mac, int len);
f8d027
 int read_write(int fd, unsigned char *packet, int size, int rw);
f8d027
-
f8d027
+void close_fds(long max_fd, int spare1, int spare2, int spare3);
f8d027
 int wildcard_match(const char* wildcard, const char* match);
f8d027
 int wildcard_matchn(const char* wildcard, const char* match, int num);
f8d027
 
f8d027
diff --git a/src/helper.c b/src/helper.c
f8d027
index 1b260a1..7072cf4 100644
f8d027
--- a/src/helper.c
f8d027
+++ b/src/helper.c
f8d027
@@ -131,12 +131,8 @@ int create_helper(int event_fd, int err_fd, uid_t uid, gid_t gid, long max_fd)
f8d027
      Don't close err_fd, in case the lua-init fails.
f8d027
      Note that we have to do this before lua init
f8d027
      so we don't close any lua fds. */
f8d027
-  for (max_fd--; max_fd >= 0; max_fd--)
f8d027
-    if (max_fd != STDOUT_FILENO && max_fd != STDERR_FILENO && 
f8d027
-	max_fd != STDIN_FILENO && max_fd != pipefd[0] && 
f8d027
-	max_fd != event_fd && max_fd != err_fd)
f8d027
-      close(max_fd);
f8d027
-
f8d027
+  close_fds(max_fd, pipefd[0], event_fd, err_fd);
f8d027
+  
f8d027
 #ifdef HAVE_LUASCRIPT
f8d027
   if (daemon->luascript)
f8d027
     {
f8d027
diff --git a/src/util.c b/src/util.c
f8d027
index 73bf62a..f058c92 100644
f8d027
--- a/src/util.c
f8d027
+++ b/src/util.c
f8d027
@@ -705,6 +705,47 @@ int read_write(int fd, unsigned char *packet, int size, int rw)
f8d027
   return 1;
f8d027
 }
f8d027
 
f8d027
+/* close all fds except STDIN, STDOUT and STDERR, spare1, spare2 and spare3 */
f8d027
+void close_fds(long max_fd, int spare1, int spare2, int spare3) 
f8d027
+{
f8d027
+  /* On Linux, use the /proc/ filesystem to find which files
f8d027
+     are actually open, rather than iterate over the whole space,
f8d027
+     for efficiency reasons. If this fails we drop back to the dumb code. */
f8d027
+#ifdef HAVE_LINUX_NETWORK 
f8d027
+  DIR *d;
f8d027
+  
f8d027
+  if ((d = opendir("/proc/self/fd")))
f8d027
+    {
f8d027
+      struct dirent *de;
f8d027
+
f8d027
+      while ((de = readdir(d)))
f8d027
+	{
f8d027
+	  long fd;
f8d027
+	  char *e = NULL;
f8d027
+	  
f8d027
+	  errno = 0;
f8d027
+	  fd = strtol(de->d_name, &e, 10);
f8d027
+	  	  
f8d027
+      	  if (errno != 0 || !e || *e || fd == dirfd(d) ||
f8d027
+	      fd == STDOUT_FILENO || fd == STDERR_FILENO || fd == STDIN_FILENO ||
f8d027
+	      fd == spare1 || fd == spare2 || fd == spare3)
f8d027
+	    continue;
f8d027
+	  
f8d027
+	    close(fd);
f8d027
+	}
f8d027
+      
f8d027
+      closedir(d);
f8d027
+      return;
f8d027
+  }
f8d027
+#endif
f8d027
+
f8d027
+  /* fallback, dumb code. */
f8d027
+  for (max_fd--; max_fd >= 0; max_fd--)
f8d027
+    if (max_fd != STDOUT_FILENO && max_fd != STDERR_FILENO && max_fd != STDIN_FILENO &&
f8d027
+	max_fd != spare1 && max_fd != spare2 && max_fd != spare3)
f8d027
+      close(max_fd);
f8d027
+}
f8d027
+
f8d027
 /* Basically match a string value against a wildcard pattern.  */
f8d027
 int wildcard_match(const char* wildcard, const char* match)
f8d027
 {