Commit ccb9c679 authored by Eric Blake's avatar Eric Blake
Browse files

popen-safer: prevent popen from clobbering std descriptors



* modules/popen-safer: New file.
* lib/popen-safer.c: Likewise.
* m4/stdio-safer.m4 (gl_POPEN_SAFER): New macro.
* lib/stdio--.h (popen): Provide override.
* lib/stdio-safer.h (popen_safer): Provide declaration.
* tests/test-popen.c (includes): Partially test this.
* modules/popen-safer-tests: New file, for more tests.
* tests/test-popen-safer.c: Likewise.
* MODULES.html.sh (file stream based Input/Output): Mention it.
Signed-off-by: default avatarEric Blake <ebb9@byu.net>
parent fb8c836a
2009-08-19 Eric Blake <ebb9@byu.net>
 
popen-safer: prevent popen from clobbering std descriptors
* modules/popen-safer: New file.
* lib/popen-safer.c: Likewise.
* m4/stdio-safer.m4 (gl_POPEN_SAFER): New macro.
* lib/stdio--.h (popen): Provide override.
* lib/stdio-safer.h (popen_safer): Provide declaration.
* tests/test-popen.c (includes): Partially test this.
* modules/popen-safer-tests: New file, for more tests.
* tests/test-popen-safer.c: Likewise.
* MODULES.html.sh (file stream based Input/Output): Mention it.
tests: test some of the *-safer modules
* modules/fopen-safer (Depends-on): Add fopen.
* modules/fcntl-safer (Depends-on): Add fcntl.
......@@ -2538,6 +2538,7 @@ func_all_modules ()
func_module fwriting
func_module getpass
func_module getpass-gnu
func_module popen-safer
func_module stdlib-safer
func_module tmpfile-safer
func_end_table
......
/* Invoke popen, 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 <unistd.h>
#include "cloexec.h"
#if (defined _WIN32 || defined __WIN32__) && ! defined __CYGWIN__
# define O_CLOEXEC O_NOINHERIT
#elif !defined O_CLOEXEC
# define O_CLOEXEC 0
#endif
/* Like open (name, flags | O_CLOEXEC), although not necessarily
atomic. FLAGS must not include O_CREAT. */
static int
open_noinherit (char const *name, int flags)
{
int fd = open (name, flags | O_CLOEXEC);
if (0 <= fd && !O_CLOEXEC && set_cloexec_flag (fd, true) != 0)
{
int saved_errno = errno;
close (fd);
fd = -1;
errno = saved_errno;
}
return fd;
}
/* Like popen, but do not return stdin, stdout, or stderr. */
FILE *
popen_safer (char const *cmd, char const *mode)
{
/* Unfortunately, we cannot use the fopen_safer approach of using
fdopen (dup_safer (fileno (popen (cmd, mode)))), because stdio
libraries maintain hidden state tying the original fd to the pid
to wait on when using pclose (this hidden state is also used to
avoid fd leaks in subsequent popen calls). So, we instead
guarantee that all standard streams are open prior to the popen
call (even though this puts more pressure on open fds), so that
the original fd created by popen is safe. */
FILE *fp;
int fd = open_noinherit ("/dev/null", O_RDONLY);
if (0 <= fd && fd <= STDERR_FILENO)
{
/* Maximum recursion depth is 3. */
int saved_errno;
fp = popen_safer (cmd, mode);
saved_errno = errno;
close (fd);
errno = saved_errno;
}
else
{
/* Either all fd's are tied up, or fd is safe and the real popen
will reuse it. */
close (fd);
fp = popen (cmd, mode);
}
return fp;
}
......@@ -29,3 +29,8 @@
# undef tmpfile
# define tmpfile tmpfile_safer
#endif
#if GNULIB_POPEN_SAFER
# undef popen
# define popen popen_safer
#endif
/* Invoke stdio functions, but avoid some glitches.
Copyright (C) 2001, 2003, 2006 Free Software Foundation, Inc.
Copyright (C) 2001, 2003, 2006, 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
......@@ -20,4 +20,5 @@
#include <stdio.h>
FILE *fopen_safer (char const *, char const *);
FILE *popen_safer (char const *, char const *);
FILE *tmpfile_safer (void);
#serial 10
dnl Copyright (C) 2002, 2005-2007 Free Software Foundation, Inc.
#serial 11
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,
dnl with or without modifications, as long as this notice is preserved.
......@@ -9,6 +9,11 @@ AC_DEFUN([gl_FOPEN_SAFER],
AC_LIBOBJ([fopen-safer])
])
AC_DEFUN([gl_POPEN_SAFER],
[
AC_LIBOBJ([popen-safer])
])
AC_DEFUN([gl_TMPFILE_SAFER],
[
AC_LIBOBJ([tmpfile-safer])
......
Description:
popen function that avoids clobbering std{in,out,err}.
Files:
lib/stdio--.h
lib/stdio-safer.h
lib/popen-safer.c
m4/stdio-safer.m4
Depends-on:
cloexec
open
popen
unistd-safer
configure.ac:
gl_POPEN_SAFER
gl_MODULE_INDICATOR([popen-safer])
Makefile.am:
Include:
"stdio-safer.h"
License:
GPL
Maintainer:
Eric Blake
Files:
tests/test-popen-safer.c
Depends-on:
dup2
sys_wait
configure.ac:
Makefile.am:
TESTS += test-popen-safer
check_PROGRAMS += test-popen-safer
/* Test of opening a subcommand 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 <sys/wait.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;
int status;
/* 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;
ASSERT (fp = popen ("exit 0", "r"));
ASSERT (STDERR_FILENO < fileno (fp));
status = pclose (fp);
ASSERT (WIFEXITED (status));
ASSERT (!WEXITSTATUS (status));
ASSERT (fclose (stdin) == 0);
ASSERT (fp = popen ("exit 0", "r"));
ASSERT (STDERR_FILENO < fileno (fp));
status = pclose (fp);
ASSERT (WIFEXITED (status));
ASSERT (!WEXITSTATUS (status));
ASSERT (fclose (stdout) == 0);
ASSERT (fp = popen ("exit 0", "r"));
ASSERT (STDERR_FILENO < fileno (fp));
status = pclose (fp);
ASSERT (WIFEXITED (status));
ASSERT (!WEXITSTATUS (status));
ASSERT (fclose (stderr) == 0);
ASSERT (fp = popen ("exit 0", "r"));
ASSERT (STDERR_FILENO < fileno (fp));
status = pclose (fp);
ASSERT (WIFEXITED (status));
ASSERT (!WEXITSTATUS (status));
return 0;
}
......@@ -27,6 +27,10 @@
#include <sys/wait.h>
#include <unistd.h>
#if GNULIB_POPEN_SAFER
# include "stdio--.h"
#endif
#define ASSERT(expr) \
do \
{ \
......
Supports Markdown
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment