]> git.ipfire.org Git - thirdparty/coreutils.git/commitdiff
unexpand: fix heap overflow
authorPádraig Brady <P@draigBrady.com>
Tue, 28 Apr 2026 19:33:10 +0000 (20:33 +0100)
committerPádraig Brady <P@draigBrady.com>
Wed, 29 Apr 2026 12:16:43 +0000 (13:16 +0100)
* src/unexpand.c (unexpand): Use xinmalloc() to gracefully
handle overflow.  Also use the runtime locale specific MB_CUR_MAX
rather than the worst case MB_LEN_MAX.
* tests/unexpand/mb.sh: Add a test case that fails in a default
glibc build with either MB_CUR_MAX or MB_LEN_MAX.
* NEWS: Mention the bug fix.
Reported by Michał Majchrowicz.

NEWS
src/unexpand.c
tests/unexpand/mb.sh

diff --git a/NEWS b/NEWS
index d7dc61e4648b7c7070aea145b9a51556e5f5ca5b..8e041272e2631e676b124ff4c11fa1646b5199e8 100644 (file)
--- a/NEWS
+++ b/NEWS
@@ -12,6 +12,9 @@ GNU coreutils NEWS                                    -*- outline -*-
   standard output is fully buffered, e.g., when redirected to a file.
   [bug introduced in coreutils-9.10]
 
+  'unexpand -t' no longer overflows a heap buffer, for tab values > SIZE_MAX/16.
+  [bug introduced in coreutils-9.11]
+
   'uniq -w' no longer overruns the read buffer in multibyte locales.
   [bug introduced in coreutils-9.5]
 
index 3cbff1b1292a681c9272c46e972afd3475dff5cb..c859c17a3e5314b916ca2c8761cf141717a856a3 100644 (file)
@@ -131,7 +131,7 @@ unexpand (void)
   /* The worst case is a non-blank character, then one blank, then a
      tab stop, then MAX_COLUMN_WIDTH - 1 blanks, then a non-blank; so
      allocate MAX_COLUMN_WIDTH bytes to store the blanks.  */
-  pending_blank = ximalloc (max_column_width * sizeof (char) * MB_LEN_MAX);
+  pending_blank = xinmalloc (max_column_width, MB_CUR_MAX);
 
   while (true)
     {
index 76a2679035956495c0f89b29d6aa32ed92ce7996..076a1c1ae52e0f27034d9c0984beab62ba5240a2 100755 (executable)
@@ -17,6 +17,7 @@
 
 . "${srcdir=.}/tests/init.sh"; path_prepend_ ./src
 print_ver_ unexpand printf
+getlimits_
 
 test "$LOCALE_FR_UTF8" != none || skip_ "French UTF-8 locale not available"
 export LC_ALL="$LOCALE_FR_UTF8"
@@ -161,4 +162,11 @@ EOF
 unexpand -a ./in ./in > out || fail=1
 compare exp out > /dev/null 2>&1 || fail=1
 
+# Ensure overflow is handed gracefully
+# coreutils v9.11 induced a buffer overflow with mb_mul=4 (or 16).
+for mb_mul in 4 6; do
+  printf '   \n' | unexpand -t $(expr $SIZE_MAX / $mb_mul + 1) 2>err; ret=$?
+  test "$ret" = 1 || test "$ret" = 0 || { cat err; fail=1; }
+done
+
 Exit $fail