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

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