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