]> git.ipfire.org Git - thirdparty/coreutils.git/commitdiff
fold: fix write error checks with invalid multi-byte input
authorPádraig Brady <P@draigBrady.com>
Tue, 16 Sep 2025 15:45:02 +0000 (16:45 +0100)
committerPádraig Brady <P@draigBrady.com>
Tue, 16 Sep 2025 17:41:51 +0000 (18:41 +0100)
* src/fold.c (write_out): A new helper to check all writes.
(fold-file): Use write_out() for all writes.
* tests/fold/fold-zero-width.sh: Adjust to writing more
data in various patterns, rather than two buffers of NULs.
This is a more robust memory bound check, and the '\303' case
tests this particular logic change.
* NEWS: fold now exits immediately, not just promptly.

NEWS
src/fold.c
tests/fold/fold-zero-width.sh

diff --git a/NEWS b/NEWS
index 6846720f16f4fec9c8517bcd7eea9a0e5f18268c..972bdfbadf9f59a27c34c71a013c51463db8c6e3 100644 (file)
--- a/NEWS
+++ b/NEWS
@@ -142,7 +142,7 @@ GNU coreutils NEWS                                    -*- outline -*-
   'factor' is now much faster at identifying large prime numbers,
   and significantly faster on composite numbers greater than 2^128.
 
-  fold now exits promptly upon receiving a write error,
+  fold now exits immediately upon receiving a write error,
   which is significant when reading large / unbounded inputs.
 
   'seq' is more accurate with large integer start values.
index a2ca953d4a309764b41918654596e330a74ff2e2..6969a81e80f1fccd6adc919589ba85e83c38d1f2 100644 (file)
@@ -133,6 +133,14 @@ adjust_column (size_t column, mcel_t g)
   return column;
 }
 
+static void
+write_out (char const *line, size_t line_len, bool newline)
+{
+  if (fwrite (line, sizeof (char), line_len, stdout) != line_len
+      || (newline && putchar ('\n') < 0))
+    write_error ();
+}
+
 /* Fold file FILENAME, or standard input if FILENAME is "-",
    to stdout, with maximum line length WIDTH.
    Return true if successful.  */
@@ -192,8 +200,7 @@ fold_file (char const *filename, size_t width)
             }
           if (g.ch == '\n')
             {
-              fwrite (line_out, sizeof (char), offset_out, stdout);
-              putchar ('\n');
+              write_out (line_out, offset_out, /*newline=*/ true);
               column = offset_out = 0;
               continue;
             }
@@ -226,8 +233,7 @@ fold_file (char const *filename, size_t width)
                     {
                       logical_end += space_length;
                       /* Found a blank.  Don't output the part after it. */
-                      fwrite (line_out, sizeof (char), logical_end, stdout);
-                      putchar ('\n');
+                      write_out (line_out, logical_end, /*newline=*/ true);
                       /* Move the remainder to the beginning of the next line.
                          The areas being copied here might overlap. */
                       memmove (line_out, line_out + logical_end,
@@ -253,8 +259,7 @@ fold_file (char const *filename, size_t width)
                   continue;
                 }
 
-              fwrite (line_out, sizeof (char), offset_out, stdout);
-              putchar ('\n');
+              write_out (line_out, offset_out, /*newline=*/ true);
               column = offset_out = 0;
               goto rescan;
             }
@@ -263,7 +268,7 @@ fold_file (char const *filename, size_t width)
              zero.  */
           if (sizeof line_out <= offset_out + g.len)
             {
-              fwrite (line_out, sizeof (char), offset_out, stdout);
+              write_out (line_out, offset_out, /*newline=*/ false);
               offset_out = 0;
             }
 
@@ -273,9 +278,6 @@ fold_file (char const *filename, size_t width)
       if (feof (istream))
         break;
 
-      if (ferror (stdout))
-        write_error ();
-
       /* We read a full buffer of complete characters.  */
       offset_in = 0;
 
@@ -287,7 +289,7 @@ fold_file (char const *filename, size_t width)
     saved_errno = 0;
 
   if (offset_out)
-    fwrite (line_out, sizeof (char), offset_out, stdout);
+    write_out (line_out, offset_out, /*newline=*/ false);
 
   if (STREQ (filename, "-"))
     clearerr (istream);
index c059555fd882bf2e3ff6ebfe0f8bf5d7198158cb..f69d89f13042cc26c4e43272247ee040c12b146d 100755 (executable)
@@ -47,10 +47,15 @@ fold --characters inp > out || fail=1
 test $(wc -l < out) -eq $(($IO_BUFSIZE_TIMES2 / 80)) || fail=1
 
 # Ensure bounded memory operation.
+test -w /dev/full && test -c /dev/full &&
 vm=$(get_min_ulimit_v_ fold /dev/null) && {
-  head -c $IO_BUFSIZE_TIMES2 /dev/zero | tr -d '\n' \
-    | (ulimit -v $(($vm+8000)) && fold 2>err) | head || fail=1
-  compare /dev/null err || fail=1
+  # \303 results in EILSEQ on input
+  for c in '\n' '\0' '\303'; do
+    tr '\0' "$c" < /dev/zero | timeout 10 $SHELL -c \
+     "(ulimit -v $(($vm+8000)) && fold 2>err >/dev/full)"
+    { test $? = 124 || ! grep 'space' err >/dev/null; } &&
+     { fail=1; cat err; echo "fold didn't diagnose ENOSPC" >&2; }
+  done
 }
 
 Exit $fail