summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorPádraig Brady <P@draigBrady.com>2023-03-29 15:29:52 +0100
committerPádraig Brady <P@draigBrady.com>2023-03-31 11:58:49 +0100
commit7ad749886cddc76860bd8cfc38a408032a5e4c99 (patch)
tree3ec78d113e52c0579a1e80411bd04d7c6a4ff437
parenta9bd274616a8d2e99736b498e657cda4e6954f3f (diff)
downloadcoreutils-7ad749886cddc76860bd8cfc38a408032a5e4c99.tar.gz
wc: diagnose overflow of total counts
* src/wc.c (wc): Use INT_ADD_WRAPV() to detect overflow. (main): Upon overflow, saturate the total, print a diagnostic, and set exit status. * tests/misc/wc-total.sh: Add a test case, which operates on BTRFS and 64 bit systems at least. Reported at https://bugs.debian.org/1027100
-rw-r--r--NEWS3
-rw-r--r--src/wc.c51
-rwxr-xr-xtests/misc/wc-total.sh17
3 files changed, 64 insertions, 7 deletions
diff --git a/NEWS b/NEWS
index bade99043..f53adab6f 100644
--- a/NEWS
+++ b/NEWS
@@ -19,6 +19,9 @@ GNU coreutils NEWS -*- outline -*-
This also applies to cksum, sha*sum, and b2sum.
[bug introduced in coreutils-9.2]
+ wc will now diagnose if any total counts have overflowed.
+ [This bug was present in "the beginning".]
+
* Noteworthy changes in release 9.2 (2023-03-20) [stable]
diff --git a/src/wc.c b/src/wc.c
index 5f3ef6eee..801396a15 100644
--- a/src/wc.c
+++ b/src/wc.c
@@ -78,6 +78,10 @@ static uintmax_t total_lines;
static uintmax_t total_words;
static uintmax_t total_chars;
static uintmax_t total_bytes;
+static uintmax_t total_lines_overflow;
+static uintmax_t total_words_overflow;
+static uintmax_t total_chars_overflow;
+static uintmax_t total_bytes_overflow;
static uintmax_t max_line_length;
/* Which counts to print. */
@@ -703,10 +707,16 @@ wc (int fd, char const *file_x, struct fstatus *fstatus, off_t current_pos)
if (total_mode != total_only)
write_counts (lines, words, chars, bytes, linelength, file_x);
- total_lines += lines;
- total_words += words;
- total_chars += chars;
- total_bytes += bytes;
+
+ if (INT_ADD_WRAPV (total_lines, lines, &total_lines))
+ total_lines_overflow = true;
+ if (INT_ADD_WRAPV (total_words, words, &total_words))
+ total_words_overflow = true;
+ if (INT_ADD_WRAPV (total_chars, chars, &total_chars))
+ total_chars_overflow = true;
+ if (INT_ADD_WRAPV (total_bytes, bytes, &total_bytes))
+ total_bytes_overflow = true;
+
if (linelength > max_line_length)
max_line_length = linelength;
@@ -1022,9 +1032,36 @@ main (int argc, char **argv)
if (total_mode != total_never
&& (total_mode != total_auto || 1 < argv_iter_n_args (ai)))
- write_counts (total_lines, total_words, total_chars, total_bytes,
- max_line_length,
- total_mode != total_only ? _("total") : NULL);
+ {
+ if (total_lines_overflow)
+ {
+ total_lines = UINTMAX_MAX;
+ error (0, EOVERFLOW, _("total lines"));
+ ok = false;
+ }
+ if (total_words_overflow)
+ {
+ total_words = UINTMAX_MAX;
+ error (0, EOVERFLOW, _("total words"));
+ ok = false;
+ }
+ if (total_chars_overflow)
+ {
+ total_chars = UINTMAX_MAX;
+ error (0, EOVERFLOW, _("total characters"));
+ ok = false;
+ }
+ if (total_bytes_overflow)
+ {
+ total_bytes = UINTMAX_MAX;
+ error (0, EOVERFLOW, _("total bytes"));
+ ok = false;
+ }
+
+ write_counts (total_lines, total_words, total_chars, total_bytes,
+ max_line_length,
+ total_mode != total_only ? _("total") : NULL);
+ }
argv_iter_free (ai);
diff --git a/tests/misc/wc-total.sh b/tests/misc/wc-total.sh
index 4848111cd..113b35504 100755
--- a/tests/misc/wc-total.sh
+++ b/tests/misc/wc-total.sh
@@ -18,6 +18,8 @@
. "${srcdir=.}/tests/init.sh"; path_prepend_ ./src
print_ver_ wc
+require_sparse_support_ # for 'truncate --size=$BIG'
+getlimits_
printf '%s\n' '2' > 2b || framework_failure_
printf '%s\n' '2 words' > 2w || framework_failure_
@@ -40,4 +42,19 @@ compare exp out || fail=1
wc --total=always 2b > out || fail=1
test "$(wc -l < out)" = 2 || fail=1
+if truncate -s 2E big; then
+ if test "$UINTMAX_MAX" = '18446744073709551615'; then
+ # Ensure overflow is diagnosed
+ returns_ 1 wc --total=only -c big big big big big big big big \
+ > out || fail=1
+
+ # Ensure total is saturated
+ printf '%s\n' "$UINTMAX_MAX" > exp || framework_failure_
+ compare exp out || fail=1
+
+ # Ensure overflow is ignored if totals not shown
+ wc --total=never -c big big big big big big big big || fail=1
+ fi
+fi
+
Exit $fail