Subject: bug#33965: [bug-diffutils] bug#33965: bug#33965: handling of closed file descriptors

From: Paul Eggert <[email-masked]>
Date: Fri, 4 Jan 2019 14:42:21 -0800

I'd rather think of this as a bug in the test case, not a bug in 'diff'. 
This is because the test case assumes more about the shell's "<&-" 
construct than what POSIX requires.

The bigger picture here is that invoking programs with stdin closed is 
trouble, and although Gnulib provides several modules to work around the 
trouble these modules are a hassle and many applications don't use them. 
We should encourage platforms like HP-UX that try to work around the 
trouble, instead of having diffutils attempt to simulate the traditional 
but inferior exec semantics. Although the Linux kernel currently 
implements the traditional semantics, in this particular case I think 
I'd rather have diffutils adjust to what the platform provides, rather 
than attempt to simulate Linux behavior even on non-Linux kernels.

So I propose the attached further patch.
From fd3c978dd929453dfbe0bf8eaa9295321274ba33 Mon Sep 17 00:00:00 2001
From: Paul Eggert <[email-masked]>
Date: Fri, 4 Jan 2019 13:43:55 -0800
Subject: [PATCH] diff: move HP-UX workaround into test case
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

* src/diff.c (compare_files) [__hpux]: Remove recently-introduced
special case for HP-UX exec with stdin closed.
* tests/new-file: Don’t assume stdin stays closed after a
successful exec, as POSIX doesn’t guarantee this and it is
not true on HP-UX.
---
 src/diff.c     |  7 -------
 tests/new-file | 34 +++++++++++++++++++++++-----------
 2 files changed, 23 insertions(+), 18 deletions(-)

diff --git a/src/diff.c b/src/diff.c
index 2ed3ae5..6e8c6be 100644
--- a/src/diff.c
+++ b/src/diff.c
@@ -1172,13 +1172,6 @@ compare_files (struct comparison const *parent,
 	      cmp.file[f].desc = STDIN_FILENO;
 	      if (binary && ! isatty (STDIN_FILENO))
 		set_binary_mode (STDIN_FILENO, O_BINARY);
-#ifdef __hpux
-	      /* Recognize file descriptors closed by the parent on HP-UX.  */
-	      int flags = fcntl (STDIN_FILENO, F_GETFL, NULL);
-	      if (flags >= 0 && (flags & FD_CLOEXEC) != 0)
-		cmp.file[f].desc = ERRNO_ENCODE (EBADF);
-	      else
-#endif
 	      if (fstat (STDIN_FILENO, &cmp.file[f].stat) != 0)
 		cmp.file[f].desc = ERRNO_ENCODE (errno);
 	      else
diff --git a/tests/new-file b/tests/new-file
index 4400051..9b2ada4 100755
--- a/tests/new-file
+++ b/tests/new-file
@@ -10,11 +10,21 @@ echo a > a || fail=1
 echo '0a1
 > a' > exp || fail=1
 
-returns_ 1 diff -N - a <&- > out || fail=1
-compare exp out || fail=1
-
-returns_ 1 diff --unidirectional-new-file - a <&- > out || fail=1
-compare exp out || fail=1
+# POSIX allows a closed stdin to be opened by the kernel after an exec,
+# so do some tests only on platforms where stdin stays closed then.
+if returns_ 2 cmp - - <&- >/dev/null 2>&1; then
+  stdin_stays_closed=true
+else
+  stdin_stays_closed=false
+fi
+
+if $stdin_stays_closed; then
+  returns_ 1 diff -N - a <&- > out || fail=1
+  compare exp out || fail=1
+
+  returns_ 1 diff --unidirectional-new-file - a <&- > out || fail=1
+  compare exp out || fail=1
+fi
 
 returns_ 1 diff -N b - < a > out || fail=1
 compare exp out || fail=1
@@ -25,13 +35,15 @@ compare exp out || fail=1
 echo '1d0
 < a' > exp || fail=1
 
-returns_ 1 diff -N a - <&- > out || fail=1
-compare exp out || fail=1
+if $stdin_stays_closed; then
+  returns_ 1 diff -N a - <&- > out || fail=1
+  compare exp out || fail=1
 
-# With closed standard input, require an exit status of 2
-# and empty stdout.
-returns_ 2 diff --unidirectional-new-file a - <&- > out || fail=1
-compare /dev/null out || fail=1
+  # With closed standard input, require an exit status of 2
+  # and empty stdout.
+  returns_ 2 diff --unidirectional-new-file a - <&- > out || fail=1
+  compare /dev/null out || fail=1
+fi
 
 returns_ 1 diff -N - b < a > out || fail=1
 compare exp out || fail=1
-- 
2.20.1