diff options
author | Zack Weinberg <zackw@panix.com> | 2020-08-27 10:14:53 -0400 |
---|---|---|
committer | Zack Weinberg <zackw@panix.com> | 2020-08-28 11:19:16 -0400 |
commit | 845302b9b67f8ac10b050145c31e4463e60decb8 (patch) | |
tree | 87acb65316886e378cd1068fb9df8b7b33a8fd2d /tests/base.at | |
parent | f783dd434a077305ea5d1edb0d928f233736aadf (diff) | |
download | autoconf-845302b9b67f8ac10b050145c31e4463e60decb8.tar.gz |
AS_INIT: ensure fds 0, 1, 2 are open
A patch was recently proposed for GNU libc to make *all* processes
start up with file descriptors 0, 1, and 2 guaranteed to be open.
Part of the rationale for this patch was that configure scripts fail
catastrophically if these fds are closed, even if you just want to run
--help or --version, e.g.
$ ./configure --version <&-; echo $?
./configure: line 555: 0: Bad file descriptor
1
configure scripts cannot rely on behavior specific to GNU libc, so
whether or not that patch gets committed, it makes sense for us to
make configure scripts robust against being started up with closed
stdin/stdout/stderr.
This patch adds code to ensure fds 0, 1, and 2 are open, early in
_AS_SHELL_SANITIZE. It uses a construct, ‘(exec 3>&n)’, that’s known
not to work in very old shells, but that’s OK because those shells
will be rejected by _AS_DETECT_BETTER_SHELL anyway. The worst-case
scenario is that the “This script requires a shell more modern than
all the shells I found on your system” error message won’t get printed.
When these fds are found not to be open, we open them on /dev/null, in
the normal I/O direction (0 for reading, 1 and 2 for writing). There
is a case for opening them in the *opposite* direction so that, for
instance, writes to fd 1 will fail when fd 1 started out closed.
However, that would expose latent bugs that I think should be dealt
with *after* 2.70. (See Savannah bug #110300 for more detail.)
I also took the opportunity to rationalize the order of operations in
_AS_SHELL_SANITIZE a little. All the special shell and environment
variables that we care about are dealt with immediately after
AS_BOURNE_COMPATIBLE, and _AS_PATH_SEPARATOR_PREPARE happens
immediately before the first use of _AS_PATH_WALK.
* lib/m4sugar/m4sh.m4 (_AS_ENSURE_STANDARD_FDS): New macro.
(_AS_SHELL_SANITIZE): Move the “Unset variables that we do not need”
and “NLS nuisances” blocks immediately after setting IFS; merge the
unsetting of CDPATH into the main unsetting loop; move invocation of
_AS_PATH_SEPARATOR_PREPARE to immediately above the “Find who we are”
block; invoke _AS_ENSURE_STANDARD_FDS immediately before
_AS_PATH_SEPARATOR_PREPARE.
* tests/base.at (configure with closed standard fds): New test.
* tests/torture.at (--help and --version in unwritable directory): New test.
Diffstat (limited to 'tests/base.at')
-rw-r--r-- | tests/base.at | 73 |
1 files changed, 73 insertions, 0 deletions
diff --git a/tests/base.at b/tests/base.at index 4042a8aa..6894990a 100644 --- a/tests/base.at +++ b/tests/base.at @@ -846,3 +846,76 @@ FOO ]]) AT_CLEANUP + +## ----------------------------------- ## +## configure with closed standard fds ## +## ----------------------------------- ## + +AT_SETUP([configure with closed standard fds]) +AT_KEYWORDS([AS@&t@_INIT]) + +# Create a configure script that runs a relatively complicated but +# self-contained test. Run it. +AT_CONFIGURE_AC([[AC_PROG_CC]]) +AT_CHECK_AUTOCONF +AT_CHECK_AUTOHEADER +AT_CHECK_CONFIGURE([], [], [stdout], [stderr]) +AT_CHECK_ENV + +mv stdout stdout-expected +mv stderr stderr-expected +mv state-env.after state-env-expected +mv config.status config-status-expected +mv config.h config-h-expected + +# Run it again with stdin (fd 0) closed. +# There should be no change to the stdout or stderr output and thoe +# result of configuration should be the same. + +AT_CHECK_CONFIGURE([ 0<&- ], [], [stdout], [stderr]) +AT_CHECK_ENV +AT_CMP([stdout-expected], [stdout]) +AT_CMP([stderr-expected], [stderr]) +AT_CONFIG_CMP([state-env-expected], [state-env.after]) + +mv stdout stdout-closed-0 +mv stderr stderr-closed-0 +mv state-env.after state-env-closed-0 +mv config.status config-status-closed-0 +mv config.h config-h-closed-0 + +# Run it again with stdout (fd 1) closed. +# There should be no change to the stderr output and the +# result of configuration should be the same. (Any output +# that would have gone to stdout, of course, is lost.) + +AT_CHECK_CONFIGURE([ 1>&- ], [], [stdout], [stderr]) +AT_CHECK_ENV +AT_CHECK([test -f stdout && test ! -s stdout]) +AT_CMP([stderr-expected], [stderr]) +AT_CONFIG_CMP([state-env-expected], [state-env.after]) + +mv stdout stdout-closed-1 +mv stderr stderr-closed-1 +mv state-env.after state-env-closed-1 +mv config.status config-status-closed-1 +mv config.h config-h-closed-1 + +# Run it again with stderr (fd 2) closed. +# There should be no change to the stdout output and the +# result of configuration should be the same. (Any output +# that would have gone to stderr, of course, is lost.) + +AT_CHECK_CONFIGURE([ 2>&- ], [], [stdout], [stderr]) +AT_CHECK_ENV +AT_CMP([stdout-expected], [stdout]) +AT_CHECK([test -f stderr && test ! -s stderr]) +AT_CONFIG_CMP([state-env-expected], [state-env.after]) + +mv stdout stdout-closed-2 +mv stderr stderr-closed-2 +mv state-env.after state-env-closed-2 +mv config.status config-status-closed-2 +mv config.h config-h-closed-2 + +AT_CLEANUP |