mirror of
https://github.com/gentoo-mirror/gentoo.git
synced 2026-08-04 00:08:09 -07:00
Thanks to Collin Funk for the headsup. Bug: https://bugs.gentoo.org/975747 Signed-off-by: Sam James <sam@gentoo.org>
129 lines
4.0 KiB
Diff
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;
|