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

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