From 4f1227bdeac3e4e18c9b3efb27623875e02d681d Mon Sep 17 00:00:00 2001 From: Eric Blake Date: Tue, 1 Sep 2009 07:41:28 -0600 Subject: [PATCH] dirent-safer: new module * modules/dirent-safer: New file. * lib/dirent--.h: Likewise. * lib/dirent-safer.h: Likewise. * lib/opendir-safer.c: Likewise. * m4/dirent-safer.m4: Likewise. * MODULES.html.sh (Enhancements for POSIX:2008): Mention it. * modules/dirent-safer-tests: New test. * tests/test-dirent-safer.c: New file. * lib/fdopendir.c (includes): Ensure fdopendir is also safe. Signed-off-by: Eric Blake --- ChangeLog | 11 +++++ MODULES.html.sh | 1 + lib/dirent--.h | 23 ++++++++++ lib/dirent-safer.h | 22 ++++++++++ lib/fdopendir.c | 4 ++ lib/opendir-safer.c | 68 +++++++++++++++++++++++++++++ m4/dirent-safer.m4 | 11 +++++ modules/dirent-safer | 28 ++++++++++++ modules/dirent-safer-tests | 11 +++++ tests/test-dirent-safer.c | 106 +++++++++++++++++++++++++++++++++++++++++++++ 10 files changed, 285 insertions(+) create mode 100644 lib/dirent--.h create mode 100644 lib/dirent-safer.h create mode 100644 lib/opendir-safer.c create mode 100644 m4/dirent-safer.m4 create mode 100644 modules/dirent-safer create mode 100644 modules/dirent-safer-tests create mode 100644 tests/test-dirent-safer.c diff --git a/ChangeLog b/ChangeLog index cf8b1155a..c30ac8e2c 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,5 +1,16 @@ 2009-09-02 Eric Blake + dirent-safer: new module + * modules/dirent-safer: New file. + * lib/dirent--.h: Likewise. + * lib/dirent-safer.h: Likewise. + * lib/opendir-safer.c: Likewise. + * m4/dirent-safer.m4: Likewise. + * MODULES.html.sh (Enhancements for POSIX:2008): Mention it. + * modules/dirent-safer-tests: New test. + * tests/test-dirent-safer.c: New file. + * lib/fdopendir.c (includes): Ensure fdopendir is also safe. + fdopendir: optimize on mingw * lib/unistd.in.h (_gl_directory_name): New prototype. * lib/fchdir.c (_gl_directory_name): Implement it. diff --git a/MODULES.html.sh b/MODULES.html.sh index 027a0bca1..bb9964269 100755 --- a/MODULES.html.sh +++ b/MODULES.html.sh @@ -2394,6 +2394,7 @@ func_all_modules () func_begin_table func_module chdir-long + func_module dirent-safer func_module dirname func_module getopt func_module iconv_open-utf diff --git a/lib/dirent--.h b/lib/dirent--.h new file mode 100644 index 000000000..088aafdc6 --- /dev/null +++ b/lib/dirent--.h @@ -0,0 +1,23 @@ +/* Like dirent.h, but redefine some names to avoid glitches. + + Copyright (C) 2009 Free Software Foundation, Inc. + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . */ + +/* Written by Eric Blake. */ + +#include "dirent-safer.h" + +#undef opendir +#define opendir opendir_safer diff --git a/lib/dirent-safer.h b/lib/dirent-safer.h new file mode 100644 index 000000000..0debe26bf --- /dev/null +++ b/lib/dirent-safer.h @@ -0,0 +1,22 @@ +/* Invoke dirent-like functions, but avoid some glitches. + + Copyright (C) 2009 Free Software Foundation, Inc. + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . */ + +/* Written by Eric Blake. */ + +#include + +DIR *opendir_safer (const char *name); diff --git a/lib/fdopendir.c b/lib/fdopendir.c index 3bc13acf4..14dc111c8 100644 --- a/lib/fdopendir.c +++ b/lib/fdopendir.c @@ -27,6 +27,10 @@ #include "openat-priv.h" #include "save-cwd.h" +#if GNULIB_DIRENT_SAFER +# include "dirent--.h" +#endif + /* Replacement for Solaris' function by the same name. First, try to simulate it via opendir ("/proc/self/fd/FD"). Failing diff --git a/lib/opendir-safer.c b/lib/opendir-safer.c new file mode 100644 index 000000000..8c399d0ca --- /dev/null +++ b/lib/opendir-safer.c @@ -0,0 +1,68 @@ +/* Invoke opendir, but avoid some glitches. + + Copyright (C) 2009 Free Software Foundation, Inc. + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . */ + +/* Written by Eric Blake. */ + +#include + +#include "dirent-safer.h" + +#include +#include +#include "unistd-safer.h" + +/* Like opendir, but do not clobber stdin, stdout, or stderr. */ + +DIR * +opendir_safer (char const *name) +{ + DIR *dp = opendir (name); + + if (dp) + { + int fd = dirfd (dp); + + if (0 <= fd && fd <= STDERR_FILENO) + { + /* If fdopendir is native (as on Linux), then it is safe to + assume dirfd(fdopendir(n))==n. If we are using the + gnulib module fdopendir, then this guarantee is not met, + but fdopendir recursively calls opendir_safer up to 3 + times to at least get a safe fd. If fdopendir is not + present but dirfd is accurate (as on cygwin 1.5.x), then + we recurse up to 3 times ourselves. Finally, if dirfd + always fails (as on mingw), then we are already safe. */ + DIR *newdp; + int e; +#if HAVE_FDOPENDIR || GNULIB_FDOPENDIR + int f = dup_safer (fd); + newdp = fdopendir (f); + e = errno; + if (! newdp) + close (f); +#else /* !FDOPENDIR */ + newdp = opendir_safer (name); + e = errno; +#endif + closedir (dp); + errno = e; + dp = newdp; + } + } + + return dp; +} diff --git a/m4/dirent-safer.m4 b/m4/dirent-safer.m4 new file mode 100644 index 000000000..6e8e6c4fc --- /dev/null +++ b/m4/dirent-safer.m4 @@ -0,0 +1,11 @@ +#serial 1 +dnl Copyright (C) 2009 Free Software Foundation, Inc. +dnl This file is free software; the Free Software Foundation +dnl gives unlimited permission to copy and/or distribute it, +dnl with or without modifications, as long as this notice is preserved. + +AC_DEFUN([gl_DIRENT_SAFER], +[ + AC_CHECK_FUNCS_ONCE([fdopendir]) + AC_LIBOBJ([opendir-safer]) +]) diff --git a/modules/dirent-safer b/modules/dirent-safer new file mode 100644 index 000000000..47db18ef2 --- /dev/null +++ b/modules/dirent-safer @@ -0,0 +1,28 @@ +Description: +Directory functions that avoid clobbering STD{IN,OUT,ERR}_FILENO. + +Files: +lib/dirent--.h +lib/dirent-safer.h +lib/opendir-safer.c +m4/dirent-safer.m4 + +Depends-on: +dirent +dirfd +unistd-safer + +configure.ac: +gl_DIRENT_SAFER +gl_MODULE_INDICATOR([dirent-safer]) + +Makefile.am: + +Include: +"dirent-safer.h" + +License: +GPL + +Maintainer: +Eric Blake diff --git a/modules/dirent-safer-tests b/modules/dirent-safer-tests new file mode 100644 index 000000000..da8777891 --- /dev/null +++ b/modules/dirent-safer-tests @@ -0,0 +1,11 @@ +Files: +tests/test-dirent-safer.c + +Depends-on: +dup2 + +configure.ac: + +Makefile.am: +TESTS += test-dirent-safer +check_PROGRAMS += test-dirent-safer diff --git a/tests/test-dirent-safer.c b/tests/test-dirent-safer.c new file mode 100644 index 000000000..aeb8342d9 --- /dev/null +++ b/tests/test-dirent-safer.c @@ -0,0 +1,106 @@ +/* Test that directory streams leave standard fds alone. + Copyright (C) 2009 Free Software Foundation, Inc. + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . */ + +/* Written by Eric Blake , 2009. */ + +#include + +#include "dirent--.h" + +#include +#include +#include +#include +#include + +#include "unistd-safer.h" + +/* This test intentionally closes stderr. So, we arrange to have fd 10 + (outside the range of interesting fd's during the test) set up to + duplicate the original stderr. */ + +#define BACKUP_STDERR_FILENO 10 +static FILE *myerr; + +#define ASSERT(expr) \ + do \ + { \ + if (!(expr)) \ + { \ + fprintf (myerr, "%s:%d: assertion failed\n", __FILE__, __LINE__); \ + fflush (myerr); \ + abort (); \ + } \ + } \ + while (0) + +int +main () +{ + int i; + DIR *dp; + /* The dirent-safer module works without the use of fdopendir (which + would also pull in fchdir and openat); but if those modules were + also used, we ensure that they are safe. In particular, the + gnulib version of fdopendir is unable to guarantee that + dirfd(fdopendir(fd))==fd, but we can at least guarantee that if + they are not equal, the fd returned by dirfd is safe. */ +#if HAVE_FDOPENDIR || GNULIB_FDOPENDIR + int dfd; +#endif + + /* We close fd 2 later, so save it in fd 10. */ + if (dup2 (STDERR_FILENO, BACKUP_STDERR_FILENO) != BACKUP_STDERR_FILENO + || (myerr = fdopen (BACKUP_STDERR_FILENO, "w")) == NULL) + return 2; + +#if HAVE_FDOPENDIR || GNULIB_FDOPENDIR + dfd = open (".", O_RDONLY); + ASSERT (STDERR_FILENO < dfd); +#endif + + /* Four iterations, with progressively more standard descriptors + closed. */ + for (i = -1; i <= STDERR_FILENO; i++) + { + if (0 <= i) + ASSERT (close (i) == 0); + dp = opendir ("."); + ASSERT (dp); + ASSERT (dirfd (dp) == -1 || STDERR_FILENO < dirfd (dp)); + ASSERT (closedir (dp) == 0); + +#if HAVE_FDOPENDIR || GNULIB_FDOPENDIR + { + int fd = dup_safer (dfd); + ASSERT (STDERR_FILENO < fd); + dp = fdopendir (fd); + ASSERT (dp); + ASSERT (dirfd (dp) == -1 || STDERR_FILENO < dirfd (dp)); + ASSERT (closedir (dp) == 0); + errno = 0; + ASSERT (close (fd) == -1); + ASSERT (errno == EBADF); + } +#endif + } + +#if HAVE_FDOPENDIR || GNULIB_FDOPENDIR + ASSERT (close (dfd) == 0); +#endif + + return 0; +} -- 2.11.0