coreutils
[Top][All Lists]
Advanced

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

[PATCH] wc: diagnose overflow of total counts


From: Pádraig Brady
Subject: [PATCH] wc: diagnose overflow of total counts
Date: Wed, 29 Mar 2023 16:12:13 +0100

* 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
---
 NEWS                   |  3 +++
 src/wc.c               | 51 ++++++++++++++++++++++++++++++++++++------
 tests/misc/wc-total.sh |  9 ++++++++
 3 files changed, 56 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..6ec9df795 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,11 @@ 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
+    returns_ 1 wc -c big big big big big big big big || fail=1
+    wc --total=never -c big big big big big big big big || fail=1
+  fi
+fi
+
 Exit $fail
-- 
2.26.2




reply via email to

[Prev in Thread] Current Thread [Next in Thread]