On 10/07/2026 15:18, h wrote:
Hello coreutils maintainers,

I sent this report four days ago, but it does not appear in the bug-coreutils 
archive, so I am resending it after subscribing to the mailing list.

The details are provided below:

I noticed a possible robustness issue in GNU pr.

In src/pr.c, the -e tab width is parsed as an int, and very large positive 
values are accepted. Later, when expanding tab characters, char_to_clump() 
computes the tab expansion width and updates the current input position using 
int arithmetic:

width = TAB_WIDTH(chars_per_input_tab, input_position);
...
input_position += width;

With a very large tab width and enough tab characters in the input, this 
position update can exceed INT_MAX. In a normal build this may not produce a 
visible failure, but with UBSan enabled it can report signed integer overflow.

For example, with a UBSan build:

printf '\t\t\t\t\t\t\t\t\t\n' > /tmp/tabs.txt
./src/pr -t -e268435456 /tmp/tabs.txt > /dev/null

This also implies very large memory allocation and output processing, since 
clump_buff is allocated based on chars_per_input_tab.

I am not sure whether this should be considered a bug or just an extreme input 
case.


I prepared a small patch for this issue.

The patch guards the input_position += width update in char_to_clump() with 
ckd_add(),
so that an integer overflow is reported instead of relying on undefined signed 
overflow behavior.

Please let me know if this approach looks reasonable.

Note: The patch author address is my other email address.

Best regards,

Guanqiang Han

Interestingly your repro triggers heap corruption
in the i18n patched version. I.e., on Fedora 44 I see:

  $ valgrind pr -t -e268435456 tabs.txt > /dev/null

  Invalid write of size 8
    at 0x4864064: memset (vg_replace_strmem.c:1399)
    by 0x4003CA9: char_to_clump_multi (pr.c:3010)
Note ckd_add doesn't need a temp variable,
and can operate directly on a single variable.

This should also have a test and NEWS,
which I've done in the proposed patch attached,
which I'll push soon.

Marking this as done.

thanks,
Padraig
From 3b747e050574b0fe56689758bf570d527da81f04 Mon Sep 17 00:00:00 2001
From: Guanqiang Han <[email protected]>
Date: Tue, 7 Jul 2026 22:58:43 +0800
Subject: [PATCH] pr: guard input position update against overflow

* src/pr.c (char_to_clump): Use ckd_add() and report integer overflow.
* tests/pr/options.sh: Add a test case.
* NEWS: Mention the bug fix.
Fixes https://bugs.gnu.org/81393
---
 NEWS                |  4 ++++
 src/pr.c            |  5 ++++-
 tests/pr/options.sh | 10 ++++++++++
 3 files changed, 18 insertions(+), 1 deletion(-)

diff --git a/NEWS b/NEWS
index 0e3d9bd5f..191d2ec63 100644
--- a/NEWS
+++ b/NEWS
@@ -26,6 +26,10 @@ GNU coreutils NEWS                                    -*- outline -*-
   standard output is fully buffered, e.g., when redirected to a file.
   [bug introduced in coreutils-9.10]
 
+  'pr' now exits gracefully upon exceeding internal accounting limits,
+  like when processing large tab stops.
+  [This bug was present in "the beginning".]
+
   'shred' no longer blocks when opening a FIFO that has no readers.
   [This bug was present in "the beginning".]
 
diff --git a/src/pr.c b/src/pr.c
index 09a1b94a8..c3e085bcd 100644
--- a/src/pr.c
+++ b/src/pr.c
@@ -2732,7 +2732,10 @@ char_to_clump (char c)
   else if (width < 0 && input_position <= -width)
     input_position = 0;
   else
-    input_position += width;
+    {
+      if (ckd_add (&input_position, input_position, width))
+        integer_overflow ();
+    }
 
   return chars;
 }
diff --git a/tests/pr/options.sh b/tests/pr/options.sh
index feaf49f19..5d4d02cd1 100755
--- a/tests/pr/options.sh
+++ b/tests/pr/options.sh
@@ -57,4 +57,14 @@ printf '%s\n' "pr: '-e' extra characters or $INV in the argument: '-1'" \
  >exp || framework_failure_
 compare exp err || fail=1
 
+# Ensure we exit gracefully upon internal overflow limits
+# Tag as expensive as it uses little mem, but about 10s on a 2020 class machine.
+if test "$RUN_VERY_EXPENSIVE_TESTS" = yes ||
+   test "$RUN_EXPENSIVE_TESTS" = yes; then
+  head -c1M /dev/zero | tr '\0' '\t' |
+  returns_ 1 pr -t -e$(($INT_MAX/(1024*1024) + 1)) 2>err >/dev/null || fail=1
+  printf '%s\n' "pr: integer overflow" > exp || framework_failure_
+  compare exp err || fail=1
+fi
+
 Exit $fail
-- 
2.55.0

Reply via email to