Mercurial > hg > octave-nkf > gnulib-hg
changeset 12246:b156d2d1b827
freopen-safer: new module
* modules/freopen-safer: New module.
* m4/stdio-safer.m4 (gl_FREOPEN_SAFER): New macro.
* lib/freopen-safer.c (freopen_safer): New file.
* lib/stdio-safer.h (freopen_safer): New declaration.
* lib/stdio--.h (freopen): New override.
* MODULES.html.sh (File stream based Input/Output): Mention it.
* doc/posix-functions/freopen.texi (freopen): Mention pitfalls and
freopen-safer module.
* doc/posix-functions/stderr.texi (stderr): Likewise.
* doc/posix-functions/stdin.texi (stdin): Likewise.
* doc/posix-functions/stdout.texi (stdout): Likewise.
* modules/freopen-safer-tests: New test.
* tests/test-reopen-safer.c: New file.
Signed-off-by: Eric Blake <ebb9@byu.net>
author | Eric Blake <ebb9@byu.net> |
---|---|
date | Thu, 05 Nov 2009 15:13:00 -0700 |
parents | 86929e33bdad |
children | 963fbd66201a |
files | ChangeLog MODULES.html.sh doc/posix-functions/freopen.texi doc/posix-functions/stderr.texi doc/posix-functions/stdin.texi doc/posix-functions/stdout.texi lib/freopen-safer.c lib/stdio--.h lib/stdio-safer.h m4/stdio-safer.m4 modules/freopen-safer modules/freopen-safer-tests tests/test-freopen-safer.c |
diffstat | 13 files changed, 316 insertions(+), 1 deletions(-) [+] |
line wrap: on
line diff
--- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,20 @@ +2009-11-05 Eric Blake <ebb9@byu.net> + + freopen-safer: new module + * modules/freopen-safer: New module. + * m4/stdio-safer.m4 (gl_FREOPEN_SAFER): New macro. + * lib/freopen-safer.c (freopen_safer): New file. + * lib/stdio-safer.h (freopen_safer): New declaration. + * lib/stdio--.h (freopen): New override. + * MODULES.html.sh (File stream based Input/Output): Mention it. + * doc/posix-functions/freopen.texi (freopen): Mention pitfalls and + freopen-safer module. + * doc/posix-functions/stderr.texi (stderr): Likewise. + * doc/posix-functions/stdin.texi (stdin): Likewise. + * doc/posix-functions/stdout.texi (stdout): Likewise. + * modules/freopen-safer-tests: New test. + * tests/test-reopen-safer.c: New file. + 2009-11-05 Jim Meyering <meyering@redhat.com> maint.mk: Prohibit inclusion of "close-stream.h" without use.
--- a/MODULES.html.sh +++ b/MODULES.html.sh @@ -2558,6 +2558,7 @@ func_module freading func_module freadptr func_module freadseek + func_module freopen-safer func_module fwritable func_module fwriting func_module getpass
--- a/doc/posix-functions/freopen.texi +++ b/doc/posix-functions/freopen.texi @@ -23,4 +23,9 @@ and (without the slash) names a nonexistent file or a file that is not a directory, on some platforms: HP-UX 11.00, Solaris 9, Irix 5.3. +@item +Applications should not assume that @code{fileno(f)} will be the same +before and after a call to @code{freopen(name,mode,f)}. However, the +module freopen-safer can at least protect @code{stdin}, @code{stdout}, +and @code{stderr}. @end itemize
--- a/doc/posix-functions/stderr.texi +++ b/doc/posix-functions/stderr.texi @@ -17,4 +17,11 @@ One workaround is to use freopen(NULL, ``r+'', stderr) on Cygwin 1.5.21 or newer. Another is to use the gnulib ftello module and do ftello(stderr). +@item +POSIX states that a setuid application can guarantee that fd 2 is +open, but some systems guarantee this even for non-setuid programs. +If an application is executed with fd 2 closed, use of @code{stderr} +can affect an unrelated file that happened to be assigned to fd 2. +The gnulib *-safer modules may be used to guarantee that fd 2 stays +reserved for @code{stderr}. @end itemize
--- a/doc/posix-functions/stdin.texi +++ b/doc/posix-functions/stdin.texi @@ -17,4 +17,11 @@ One workaround is to use freopen(NULL, ``r'', stdin) on Cygwin 1.5.21 or newer. Another is to use the gnulib ftello module and do ftello(stdin). +@item +POSIX states that a setuid application can guarantee that fd 0 is +open, but some systems guarantee this even for non-setuid programs. +If an application is executed with fd 0 closed, use of @code{stdin} +can affect an unrelated file that happened to be assigned to fd 0. +The gnulib *-safer modules may be used to guarantee that fd 0 stays +reserved for @code{stdin}. @end itemize
--- a/doc/posix-functions/stdout.texi +++ b/doc/posix-functions/stdout.texi @@ -17,4 +17,11 @@ One workaround is to use freopen(NULL, ``w'', stdout) on Cygwin 1.5.21 or newer. Another is to use the gnulib ftello module and do ftello(stdout). +@item +POSIX states that a setuid application can guarantee that fd 1 is +open, but some systems guarantee this even for non-setuid programs. +If an application is executed with fd 1 closed, use of @code{stdout} +can affect an unrelated file that happened to be assigned to fd 1. +The gnulib *-safer modules may be used to guarantee that fd 1 stays +reserved for @code{stdout}. @end itemize
new file mode 100644 --- /dev/null +++ b/lib/freopen-safer.c @@ -0,0 +1,103 @@ +/* Invoke freopen, 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 <http://www.gnu.org/licenses/>. */ + +/* Written by Eric Blake. */ + +#include <config.h> + +#include "stdio-safer.h" + +#include <errno.h> +#include <fcntl.h> +#include <stdbool.h> +#include <unistd.h> + +/* Guarantee that FD is open; all smaller FDs must already be open. + Return true if successful. */ +static bool +protect_fd (int fd) +{ + int value = open ("/dev/null", O_RDONLY); + if (value != fd) + { + if (0 <= value) + { + close (value); + errno = EBADF; /* Unexpected; this is as good as anything else. */ + } + return false; + } + return true; +} + +/* Like freopen, but guarantee that reopening stdin, stdout, or stderr + preserves the invariant that STDxxx_FILENO==fileno(stdxxx), and + that no other stream will interfere with the standard streams. + This is necessary because most freopen implementations will change + the associated fd of a stream to the lowest available slot. */ + +FILE * +freopen_safer (char const *name, char const *mode, FILE *f) +{ + /* Unfortunately, we cannot use the fopen_safer approach of using + fdopen (dup_safer (fileno (freopen (cmd, mode, f)))), because we + need to return f itself. The implementation of freopen(NULL,m,f) + is system-dependent, so the best we can do is guarantee that all + lower-valued standard fds are open prior to the freopen call, + even though this puts more pressure on open fds. */ + bool protect_in = false; + bool protect_out = false; + bool protect_err = false; + int saved_errno; + + switch (fileno (f)) + { + default: /* -1 or not a standard stream. */ + if (dup2 (STDERR_FILENO, STDERR_FILENO) != STDERR_FILENO) + protect_err = true; + /* fall through */ + case STDERR_FILENO: + if (dup2 (STDOUT_FILENO, STDOUT_FILENO) != STDOUT_FILENO) + protect_out = true; + /* fall through */ + case STDOUT_FILENO: + if (dup2 (STDIN_FILENO, STDIN_FILENO) != STDIN_FILENO) + protect_in = true; + /* fall through */ + case STDIN_FILENO: + /* Nothing left to protect. */ + break; + } + if (protect_in && !protect_fd (STDIN_FILENO)) + f = NULL; + else if (protect_out && !protect_fd (STDOUT_FILENO)) + f = NULL; + else if (protect_err && !protect_fd (STDERR_FILENO)) + f = NULL; + else + f = freopen (name, mode, f); + saved_errno = errno; + if (protect_err) + close (STDERR_FILENO); + if (protect_out) + close (STDOUT_FILENO); + if (protect_in) + close (STDIN_FILENO); + if (!f) + errno = saved_errno; + return f; +}
--- a/lib/stdio--.h +++ b/lib/stdio--.h @@ -25,6 +25,11 @@ # define fopen fopen_safer #endif +#if GNULIB_FREOPEN_SAFER +# undef freopen +# define freopen freopen_safer +#endif + #if GNULIB_TMPFILE_SAFER # undef tmpfile # define tmpfile tmpfile_safer
--- a/lib/stdio-safer.h +++ b/lib/stdio-safer.h @@ -19,6 +19,18 @@ #include <stdio.h> +#if GNULIB_FOPEN_SAFER FILE *fopen_safer (char const *, char const *); +#endif + +#if GNULIB_FREOPEN_SAFER +FILE *freopen_safer (char const *, char const *, FILE *); +#endif + +#if GNULIB_POPEN_SAFER FILE *popen_safer (char const *, char const *); +#endif + +#if GNULIB_TMPFILE_SAFER FILE *tmpfile_safer (void); +#endif
--- a/m4/stdio-safer.m4 +++ b/m4/stdio-safer.m4 @@ -1,4 +1,4 @@ -#serial 11 +#serial 12 dnl Copyright (C) 2002, 2005-2007, 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, @@ -9,6 +9,11 @@ AC_LIBOBJ([fopen-safer]) ]) +AC_DEFUN([gl_FREOPEN_SAFER], +[ + AC_LIBOBJ([freopen-safer]) +]) + AC_DEFUN([gl_POPEN_SAFER], [ AC_LIBOBJ([popen-safer])
new file mode 100644 --- /dev/null +++ b/modules/freopen-safer @@ -0,0 +1,29 @@ +Description: +freopen function that avoids clobbering std{in,out,err}. + +Files: +lib/stdio--.h +lib/stdio-safer.h +lib/freopen-safer.c +m4/stdio-safer.m4 + +Depends-on: +dup2 +freopen +open +stdbool + +configure.ac: +gl_FREOPEN_SAFER +gl_MODULE_INDICATOR([freopen-safer]) + +Makefile.am: + +Include: +"stdio-safer.h" + +License: +GPL + +Maintainer: +Eric Blake
new file mode 100644 --- /dev/null +++ b/modules/freopen-safer-tests @@ -0,0 +1,10 @@ +Files: +tests/test-freopen-safer.c + +Depends-on: + +configure.ac: + +Makefile.am: +TESTS += test-freopen-safer +check_PROGRAMS += test-freopen-safer
new file mode 100644 --- /dev/null +++ b/tests/test-freopen-safer.c @@ -0,0 +1,107 @@ +/* Test of reopening a stream. + 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 <http://www.gnu.org/licenses/>. */ + +/* Written by Eric Blake <ebb9@byu.net>, 2009. */ + +#include <config.h> + +/* Specification. */ +#include "stdio--.h" + +/* Helpers. */ +#include <stdlib.h> +#include <unistd.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 argc, char **argv) +{ + FILE *fp; + + /* 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; + + { + FILE *tmp; + ASSERT (tmp = fopen ("/dev/null", "r")); + ASSERT (STDERR_FILENO < fileno (tmp)); + ASSERT (fp = fopen ("/dev/null", "w")); + ASSERT (fileno (tmp) < fileno (fp)); + ASSERT (fclose (tmp) == 0); + } + + /* Gap in fds. */ + ASSERT (freopen ("/dev/null", "r+", fp) == fp); + ASSERT (STDERR_FILENO < fileno (fp)); + + ASSERT (freopen ("/dev/null", "r", stdin) == stdin); + ASSERT (STDIN_FILENO == fileno (stdin)); + + ASSERT (freopen ("/dev/null", "w", stdout) == stdout); + ASSERT (STDOUT_FILENO == fileno (stdout)); + + ASSERT (freopen ("/dev/null", "w", stderr) == stderr); + ASSERT (STDERR_FILENO == fileno (stderr)); + + /* fd 0 closed. */ + ASSERT (close (STDIN_FILENO) == 0); + + ASSERT (freopen ("/dev/null", "w", stdout) == stdout); + ASSERT (STDOUT_FILENO == fileno (stdout)); + + ASSERT (freopen ("/dev/null", "w", stderr) == stderr); + ASSERT (STDERR_FILENO == fileno (stderr)); + + ASSERT (freopen ("/dev/null", "a", fp) == fp); + ASSERT (STDERR_FILENO < fileno (fp)); + + /* fd 1 closed. */ + ASSERT (close (STDOUT_FILENO) == 0); + + ASSERT (freopen ("/dev/null", "w", stderr) == stderr); + ASSERT (STDERR_FILENO == fileno (stderr)); + + ASSERT (freopen ("/dev/null", "a+", fp) == fp); + ASSERT (STDERR_FILENO < fileno (fp)); + + /* fd 2 closed. */ + ASSERT (close (STDERR_FILENO) == 0); + + ASSERT (freopen ("/dev/null", "w+", fp) == fp); + ASSERT (STDERR_FILENO < fileno (fp)); + + return 0; +}