From a1afba1b2122388e9a120b6a1f011367e6db1518 Mon Sep 17 00:00:00 2001 From: Eric Blake Date: Mon, 7 Dec 2009 10:17:07 -0700 Subject: [PATCH] unistd-safer: add unit test Add more unit tests. Meanwhile, fix compilation error on mingw when testing unistd-safer and fchdir together; and avoid gcc warning on platforms without setmode. * modules/unistd-safer-tests: New file. * tests/test-dup-safer.c: Likewise. * tests/test-cloexec.c (setmode): Avoid compiler warning. * tests/test-dup2.c (setmode): Likewise. * lib/cloexec.c (dup_cloexec): Fix mingw compile error. Signed-off-by: Eric Blake --- ChangeLog | 7 ++ lib/cloexec.c | 2 +- modules/unistd-safer-tests | 13 ++++ tests/test-cloexec.c | 3 +- tests/test-dup-safer.c | 186 +++++++++++++++++++++++++++++++++++++++++++++ tests/test-dup2.c | 3 +- 6 files changed, 211 insertions(+), 3 deletions(-) create mode 100644 modules/unistd-safer-tests create mode 100644 tests/test-dup-safer.c diff --git a/ChangeLog b/ChangeLog index 3cf1bdbfd..73e6faa98 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,5 +1,12 @@ 2009-12-07 Eric Blake + unistd-safer: add unit test + * modules/unistd-safer-tests: New file. + * tests/test-dup-safer.c: Likewise. + * tests/test-cloexec.c (setmode): Avoid compiler warning. + * tests/test-dup2.c (setmode): Likewise. + * lib/cloexec.c (dup_cloexec): Fix mingw compile error. + cloexec: preserve text vs. binary across dup_cloexec * lib/cloexec.c (dup_cloexec) [W32]: Query and use translation mode. diff --git a/lib/cloexec.c b/lib/cloexec.c index 57e0be59e..18985cb23 100644 --- a/lib/cloexec.c +++ b/lib/cloexec.c @@ -128,7 +128,7 @@ dup_cloexec (int fd) # if REPLACE_FCHDIR if (0 <= nfd) - result = _gl_register_dup (fd, nfd); + nfd = _gl_register_dup (fd, nfd); # endif return nfd; diff --git a/modules/unistd-safer-tests b/modules/unistd-safer-tests new file mode 100644 index 000000000..17b17a035 --- /dev/null +++ b/modules/unistd-safer-tests @@ -0,0 +1,13 @@ +Files: +tests/test-dup-safer.c + +Depends-on: +binary-io +cloexec +stdbool + +configure.ac: + +Makefile.am: +TESTS += test-dup-safer +check_PROGRAMS += test-dup-safer diff --git a/tests/test-cloexec.c b/tests/test-cloexec.c index a29d1be73..1f4301497 100644 --- a/tests/test-cloexec.c +++ b/tests/test-cloexec.c @@ -69,7 +69,8 @@ is_inheritable (int fd) } #if !O_BINARY -# define setmode(f,m) 0 +# define setmode(f,m) zero () +static int zero (void) { return 0; } #endif /* Return non-zero if FD is open in the given MODE, which is either diff --git a/tests/test-dup-safer.c b/tests/test-dup-safer.c new file mode 100644 index 000000000..24cc9e5f3 --- /dev/null +++ b/tests/test-dup-safer.c @@ -0,0 +1,186 @@ +/* Test that dup_safer leaves 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 "unistd--.h" + +#include +#include +#include +#include +#include +#include + +#include "binary-io.h" +#include "cloexec.h" + +#if (defined _WIN32 || defined __WIN32__) && ! defined __CYGWIN__ +/* Get declarations of the Win32 API functions. */ +# define WIN32_LEAN_AND_MEAN +# include +#endif + +#if !O_BINARY +# define setmode(f,m) zero () +static int zero (void) { return 0; } +#endif +#ifndef O_CLOEXEC +# define O_CLOEXEC 0 +#endif + +/* 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) + +/* Return true if FD is open. */ +static bool +is_open (int fd) +{ +#if (defined _WIN32 || defined __WIN32__) && ! defined __CYGWIN__ + /* On Win32, the initial state of unassigned standard file + descriptors is that they are open but point to an + INVALID_HANDLE_VALUE, and there is no fcntl. */ + return (HANDLE) _get_osfhandle (fd) != INVALID_HANDLE_VALUE; +#else +# ifndef F_GETFL +# error Please port fcntl to your platform +# endif + return 0 <= fcntl (fd, F_GETFL); +#endif +} + +/* Return true if FD is open and inheritable across exec/spawn. */ +static bool +is_inheritable (int fd) +{ +#if (defined _WIN32 || defined __WIN32__) && ! defined __CYGWIN__ + /* On Win32, the initial state of unassigned standard file + descriptors is that they are open but point to an + INVALID_HANDLE_VALUE, and there is no fcntl. */ + HANDLE h = (HANDLE) _get_osfhandle (fd); + DWORD flags; + if (h == INVALID_HANDLE_VALUE || GetHandleInformation (h, &flags) == 0) + return 0; + return (flags & HANDLE_FLAG_INHERIT) != 0; +#else +# ifndef F_GETFD +# error Please port fcntl to your platform +# endif + int i = fcntl (fd, F_GETFD); + return 0 <= i && (i & FD_CLOEXEC) == 0; +#endif +} + +/* Return true if FD is open in the given MODE, which is either + O_TEXT or O_BINARY. */ +static bool +is_mode (int fd, int mode) +{ + int value = setmode (fd, O_BINARY); + setmode (fd, value); + return mode == value; +} + +#define witness "test-dup-safer.txt" + +int +main (void) +{ + int i; + int fd; + + /* 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; + + /* Create file for later checks. */ + fd = creat (witness, 0600); + ASSERT (STDERR_FILENO < fd); + + /* Four iterations, with progressively more standard descriptors + closed. */ + for (i = -1; i <= STDERR_FILENO; i++) + { + if (0 <= i) + ASSERT (close (i) == 0); + + /* Detect errors. */ + errno = 0; + ASSERT (dup (-1) == -1); + ASSERT (errno == EBADF); + errno = 0; + ASSERT (dup (10000000) == -1); + ASSERT (errno == EBADF); + close (fd + 1); + errno = 0; + ASSERT (dup (fd + 1) == -1); + ASSERT (errno == EBADF); + + /* Preserve text vs. binary. */ + setmode (fd, O_BINARY); + ASSERT (dup (fd) == fd + 1); + ASSERT (is_open (fd + 1)); + ASSERT (is_inheritable (fd + 1)); + ASSERT (is_mode (fd + 1, O_BINARY)); + + ASSERT (close (fd + 1) == 0); + setmode (fd, O_TEXT); + ASSERT (dup (fd) == fd + 1); + ASSERT (is_open (fd + 1)); + ASSERT (is_inheritable (fd + 1)); + ASSERT (is_mode (fd + 1, O_TEXT)); + + /* Create cloexec copy. */ + ASSERT (close (fd + 1) == 0); + ASSERT (fd_safer_flag (dup_cloexec (fd), O_CLOEXEC) == fd + 1); + ASSERT (set_cloexec_flag (fd + 1, true) == 0); + ASSERT (is_open (fd + 1)); + ASSERT (!is_inheritable (fd + 1)); + ASSERT (close (fd) == 0); + + /* dup always creates inheritable copies. Also, check that + earliest slot past std fds is used. */ + ASSERT (dup (fd + 1) == fd); + ASSERT (is_open (fd)); + ASSERT (is_inheritable (fd)); + ASSERT (close (fd + 1) == 0); + } + + /* Cleanup. */ + ASSERT (close (fd) == 0); + ASSERT (unlink (witness) == 0); + + return 0; +} diff --git a/tests/test-dup2.c b/tests/test-dup2.c index e6d3c6292..b22d1e6a5 100644 --- a/tests/test-dup2.c +++ b/tests/test-dup2.c @@ -86,7 +86,8 @@ is_inheritable (int fd) } #if !O_BINARY -# define setmode(f,m) 0 +# define setmode(f,m) zero () +static int zero (void) { return 0; } #endif /* Return non-zero if FD is open in the given MODE, which is either -- 2.11.0