Subject: grep: (standard input): invalid argument when run from make
From: Benno Fünfstück <[email-masked]>
To: [email-masked]
Date: Tue, 27 Dec 2016 18:14:27 +0000
List-Id: <debbugs-submit.debbugs.gnu.org>

The following Makefile illustrates the issue:

$ cat >Makefile <<EOF
all:
echo "abc" | grep "b"
.PHONY: all
$ make all # works as expected
$ make all > /dev/null
grep: (standard input): Invalid argument
make: *** [Makefile:2: all] Error 2

The bug was introduced by commit 4fa6f48b573267e758650e114ec158d97916411e
(introducing the usage of splice), which was first released in grep version
2.27.

My environment:

$ make --version
GNU Make 4.2.1
Built for x86_64-pc-linux-gnu
Copyright (C) 1988-2016 Free Software Foundation, Inc.
License GPLv3+: GNU GPL version 3 or later <http://gnu.org/licenses/gpl.html
>
This is free software: you are free to change and redistribute it.
There is NO WARRANTY, to the extent permitted by law.

$ grep --version
grep (GNU grep) 2.27
Copyright (C) 2016 Free Software Foundation, Inc.
License GPLv3+: GNU GPL version 3 or later <http://gnu.org/licenses/gpl.html
>.
This is free software: you are free to change and redistribute it.
There is NO WARRANTY, to the extent permitted by law.

Written by Mike Haertel and others, see <
http://git.sv.gnu.org/cgit/grep.git/tree/AUTHORS>.

Patch attached.

--
Benno Fünfstück
diff --git a/src/grep.c b/src/grep.c
index f28f3c2..3e8e662 100644
--- a/src/grep.c
+++ b/src/grep.c
@@ -1729,10 +1729,19 @@ drain_input (int fd, struct stat const *st)
 #ifdef SPLICE_F_MOVE
       /* Should be faster, since it need not copy data to user space.  */
       while ((nbytes = splice (fd, NULL, STDOUT_FILENO, NULL,
-                               INITIAL_BUFSIZE, SPLICE_F_MOVE)))
-        if (nbytes < 0)
-          return false;
-      return true;
+                               INITIAL_BUFSIZE, SPLICE_F_MOVE)) > 0)
+        continue;
+
+      if(nbytes == 0) return true;
+
+      /* STDOUT might have been opened with O_APPEND (gnumake sets this flag for example).
+       * In this case, splice fails with EINVAL. 
+       *
+       * In that case, the safe_read still works so instead of returning with an error here,
+       * we just fall through to the safe_read variant.
+       */
+      if(nbytes < 0 && errno != EINVAL) return false;
+
 #endif
     }
   while ((nbytes = safe_read (fd, buffer, bufalloc)))