Files
gentoo/sys-apps/coreutils/files/coreutils-9.11-tee.patch
Sam James 98a9bbcf50 sys-apps/coreutils: fix tee bug
Thanks to Collin Funk for the headsup.

Bug: https://bugs.gentoo.org/975747
Signed-off-by: Sam James <sam@gentoo.org>
2026-06-06 15:54:02 +01:00

129 lines
4.0 KiB
Diff

https://cgit.git.savannah.gnu.org/cgit/coreutils.git/commit/?id=0d6fcb99d691d920961938e61c43478566ef626e
From 0d6fcb99d691d920961938e61c43478566ef626e Mon Sep 17 00:00:00 2001
From: Collin Funk <collin.funk1@gmail.com>
Date: Mon, 18 May 2026 20:40:28 -0700
Subject: tee: fix infinite loop when write returns EAGAIN and short write
errors
* NEWS: Mention the bug fixes.
* THANKS.in: Add Bernhard M. Wiedemann for reporting the bugs.
* src/iopoll.c (close_wait): Remove function.
(write_wait): Don't call wait_for_nonblocking_write if write is
successful. Handle errors more robustly.
* src/iopoll.h (close_wait): Remove declaration.
* src/tee.c (tee_files): Use close instead of close_wait.
* tests/tee/short-write.sh: New test for the bug.
* tests/tee/write-eagain.sh: Likewise.
* tests/local.mk (all_tests): Add the new tests.
Fixes https://bugs.gnu.org/81060
---
src/iopoll.c | 59 +++++++++++++++++++++++++++++------------------
src/iopoll.h | 1 -
src/tee.c | 2 +-
8 files changed, 111 insertions(+), 25 deletions(-)
create mode 100755 tests/tee/short-write.sh
create mode 100755 tests/tee/write-eagain.sh
diff --git a/src/iopoll.c b/src/iopoll.c
index de20bc8d9..0bd2d6bf6 100644
--- a/src/iopoll.c
+++ b/src/iopoll.c
@@ -194,17 +194,6 @@ wait_for_nonblocking_write (int fd)
return true;
}
-/* wrapper for close() that also waits for FD if non blocking. */
-
-extern bool
-close_wait (int fd)
-{
- while (wait_for_nonblocking_write (fd))
- ;
- return close (fd) == 0;
-}
-
-
/* wrapper for write() that also waits for FD if non blocking. */
extern bool
@@ -212,19 +201,43 @@ write_wait (int fd, void const *buffer, size_t size)
{
unsigned char const *buf = buffer;
- while (true)
+ do
{
- ssize_t written = write (fd, buf, size);
- if (written < 0)
- written = 0;
-
- size -= written;
- if (size <= 0) /* everything written */
- return true;
-
- if (! wait_for_nonblocking_write (fd))
- return false;
+ const ssize_t written = write (fd, buf, size);
+ /* POSIX says that calling write with SIZE of zero may detect and
+ return errors. If no error occurs, or write makes no attempt
+ to detect errors, then write returns zero with no other
+ results. write_fail will return successfully in this case. */
+ if (written == 0)
+ {
+ if (size == 0)
+ return true;
+ else
+ {
+ /* If SIZE is greater than zero and write returns zero,
+ treat it as an error. Some buggy drivers behave this
+ way. See src/dd.c and Gnulib's lib/full-write.c for
+ more details. */
+ errno = ENOSPC;
+ return false;
+ }
+ }
- buf += written;
+ if (written < 0)
+ {
+ /* Return an error if write detected one with a SIZE of zero.
+ Otherwise, if SIZE is greater than zero, fail if it does
+ not become writable. */
+ if (size == 0 || ! wait_for_nonblocking_write (fd))
+ return false;
+ }
+ else
+ {
+ buf += written;
+ size -= written;
+ }
}
+ while (0 < size);
+
+ return true;
}
diff --git a/src/iopoll.h b/src/iopoll.h
index 1711fdab9..a1561b9ff 100644
--- a/src/iopoll.h
+++ b/src/iopoll.h
@@ -5,5 +5,4 @@ int iopoll (int fdin, int fdout, bool block);
bool iopoll_input_ok (int fdin);
bool iopoll_output_ok (int fdout);
-bool close_wait (int fd);
bool write_wait (int fd, void const *buffer, size_t size);
diff --git a/src/tee.c b/src/tee.c
index 32a18e340..fba6ae089 100644
--- a/src/tee.c
+++ b/src/tee.c
@@ -329,7 +329,7 @@ tee_files (int nfiles, char **files, bool pipe_check)
/* Close the files, but not standard output. */
for (int i = 1; i <= nfiles; i++)
- if (0 <= descriptors[i] && ! close_wait (descriptors[i]))
+ if (0 <= descriptors[i] && close (descriptors[i]) < 0)
{
error (0, errno, "%s", quotef (files[i]));
ok = false;