From: Pádraig Brady
Date: Tue, 16 Sep 2025 15:45:02 +0000 (+0100) Subject: fold: fix write error checks with invalid multi-byte input X-Git-Tag: v9.8~37 X-Git-Url: http://git.ipfire.org/?a=commitdiff_plain;h=c95c7ee76ff4480125a15b8e53945b60e58de400;p=thirdparty%2Fcoreutils.git fold: fix write error checks with invalid multi-byte input * 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. --- diff --git a/NEWS b/NEWS index 6846720f16..972bdfbadf 100644 --- 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. diff --git a/src/fold.c b/src/fold.c index a2ca953d4a..6969a81e80 100644 --- a/src/fold.c +++ b/src/fold.c @@ -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); diff --git a/tests/fold/fold-zero-width.sh b/tests/fold/fold-zero-width.sh index c059555fd8..f69d89f130 100755 --- a/tests/fold/fold-zero-width.sh +++ b/tests/fold/fold-zero-width.sh @@ -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