unexpand: fix heap overflow when a wide blank overshoots a tab stop

Resolves: RHEL-152110
This commit is contained in:
Lukáš Zaoral 2026-06-10 13:12:41 +02:00
parent 257b9cdf18
commit 98f865bcae
No known key found for this signature in database
GPG Key ID: 39157506DD67752D
2 changed files with 52 additions and 38 deletions

View File

@ -21,7 +21,7 @@ Subject: [PATCH] coreutils-i18n.patch
src/local.mk | 4 +-
src/pr.c | 443 ++++++++++++++++++--
src/sort.c | 792 +++++++++++++++++++++++++++++++++---
src/unexpand.c | 102 ++++-
src/unexpand.c | 104 ++++-
tests/Coreutils.pm | 3 +
tests/expand/mb.sh | 183 +++++++++
tests/i18n/sort.sh | 29 ++
@ -33,8 +33,8 @@ Subject: [PATCH] coreutils-i18n.patch
tests/pr/pr-tests.pl | 49 +++
tests/sort/sort-merge.pl | 42 ++
tests/sort/sort.pl | 40 +-
tests/unexpand/mb.sh | 179 ++++++++
30 files changed, 3606 insertions(+), 196 deletions(-)
tests/unexpand/mb.sh | 188 +++++++++
30 files changed, 3616 insertions(+), 197 deletions(-)
create mode 100644 lib/mbchar.c
create mode 100644 lib/mbchar.h
create mode 100644 lib/mbfile.c
@ -47,7 +47,7 @@ Subject: [PATCH] coreutils-i18n.patch
create mode 100644 tests/unexpand/mb.sh
diff --git a/bootstrap.conf b/bootstrap.conf
index 126e1e80..b4ccebf2 100644
index 126e1e8..b4ccebf 100644
--- a/bootstrap.conf
+++ b/bootstrap.conf
@@ -163,6 +163,8 @@ gnulib_modules="
@ -60,7 +60,7 @@ index 126e1e80..b4ccebf2 100644
mbrtoc32
mbrtowc
diff --git a/configure.ac b/configure.ac
index 9cb6ee14..1131ce35 100644
index 9cb6ee1..1131ce3 100644
--- a/configure.ac
+++ b/configure.ac
@@ -504,6 +504,12 @@ fi
@ -77,7 +77,7 @@ index 9cb6ee14..1131ce35 100644
if test $gl_cv_sys_tiocgwinsz_needs_termios_h = no && \
diff --git a/lib/linebuffer.h b/lib/linebuffer.h
index ae0d55dd..5bf53503 100644
index ae0d55d..5bf5350 100644
--- a/lib/linebuffer.h
+++ b/lib/linebuffer.h
@@ -22,6 +22,11 @@
@ -104,7 +104,7 @@ index ae0d55dd..5bf53503 100644
/* Initialize linebuffer LINEBUFFER for use. */
diff --git a/lib/mbchar.c b/lib/mbchar.c
new file mode 100644
index 00000000..d94b7c33
index 0000000..d94b7c3
--- /dev/null
+++ b/lib/mbchar.c
@@ -0,0 +1,23 @@
@ -133,7 +133,7 @@ index 00000000..d94b7c33
+#include "mbchar.h"
diff --git a/lib/mbchar.h b/lib/mbchar.h
new file mode 100644
index 00000000..c06ef11b
index 0000000..c06ef11
--- /dev/null
+++ b/lib/mbchar.h
@@ -0,0 +1,373 @@
@ -512,7 +512,7 @@ index 00000000..c06ef11b
+#endif /* _MBCHAR_H */
diff --git a/lib/mbfile.c b/lib/mbfile.c
new file mode 100644
index 00000000..8d2957b5
index 0000000..8d2957b
--- /dev/null
+++ b/lib/mbfile.c
@@ -0,0 +1,20 @@
@ -538,7 +538,7 @@ index 00000000..8d2957b5
+#include "mbfile.h"
diff --git a/lib/mbfile.h b/lib/mbfile.h
new file mode 100644
index 00000000..1ea1358b
index 0000000..1ea1358
--- /dev/null
+++ b/lib/mbfile.h
@@ -0,0 +1,261 @@
@ -805,7 +805,7 @@ index 00000000..1ea1358b
+#endif /* _MBFILE_H */
diff --git a/m4/mbchar.m4 b/m4/mbchar.m4
new file mode 100644
index 00000000..471e8c45
index 0000000..471e8c4
--- /dev/null
+++ b/m4/mbchar.m4
@@ -0,0 +1,13 @@
@ -824,7 +824,7 @@ index 00000000..471e8c45
+])
diff --git a/m4/mbfile.m4 b/m4/mbfile.m4
new file mode 100644
index 00000000..83068a99
index 0000000..83068a9
--- /dev/null
+++ b/m4/mbfile.m4
@@ -0,0 +1,14 @@
@ -843,7 +843,7 @@ index 00000000..83068a99
+ :
+])
diff --git a/src/cut.c b/src/cut.c
index 061e09c3..6d104259 100644
index 061e09c..6d10425 100644
--- a/src/cut.c
+++ b/src/cut.c
@@ -27,6 +27,11 @@
@ -1503,7 +1503,7 @@ index 061e09c3..6d104259 100644
if (have_read_stdin && fclose (stdin) == EOF)
diff --git a/src/expand-common.c b/src/expand-common.c
index c95998dc..d4386fec 100644
index c95998d..d4386fe 100644
--- a/src/expand-common.c
+++ b/src/expand-common.c
@@ -19,6 +19,7 @@
@ -1635,7 +1635,7 @@ index c95998dc..d4386fec 100644
to the list of tab stops. */
extern void
diff --git a/src/expand-common.h b/src/expand-common.h
index 1a57108e..60256522 100644
index 1a57108..6025652 100644
--- a/src/expand-common.h
+++ b/src/expand-common.h
@@ -25,6 +25,18 @@ extern size_t max_column_width;
@ -1658,7 +1658,7 @@ index 1a57108e..60256522 100644
extern void
add_tab_stop (uintmax_t tabval);
diff --git a/src/expand.c b/src/expand.c
index a6176a97..60b1b8ee 100644
index a6176a9..60b1b8e 100644
--- a/src/expand.c
+++ b/src/expand.c
@@ -38,6 +38,9 @@
@ -1814,7 +1814,7 @@ index a6176a97..60b1b8ee 100644
}
diff --git a/src/fold.c b/src/fold.c
index 941ad11c..2b119815 100644
index 941ad11..2b11981 100644
--- a/src/fold.c
+++ b/src/fold.c
@@ -23,10 +23,32 @@
@ -2218,7 +2218,7 @@ index 941ad11c..2b119815 100644
case 's': /* Break at word boundaries. */
diff --git a/src/local.mk b/src/local.mk
index 96ee941c..8fdb8fc3 100644
index 96ee941..8fdb8fc 100644
--- a/src/local.mk
+++ b/src/local.mk
@@ -450,8 +450,8 @@ src_base32_CPPFLAGS = -DBASE_TYPE=32 $(AM_CPPFLAGS)
@ -2233,7 +2233,7 @@ index 96ee941c..8fdb8fc3 100644
src_wc_SOURCES = src/wc.c
if USE_AVX2_WC_LINECOUNT
diff --git a/src/pr.c b/src/pr.c
index 09c6fa8a..7552b629 100644
index 09c6fa8..7552b62 100644
--- a/src/pr.c
+++ b/src/pr.c
@@ -312,6 +312,24 @@
@ -3004,7 +3004,7 @@ index 09c6fa8a..7552b629 100644
looking for more options and printing the next batch of files.
diff --git a/src/sort.c b/src/sort.c
index 09634197..dea94575 100644
index 0963419..dea9457 100644
--- a/src/sort.c
+++ b/src/sort.c
@@ -29,6 +29,14 @@
@ -4082,7 +4082,7 @@ index 09634197..dea94575 100644
break;
diff --git a/src/unexpand.c b/src/unexpand.c
index aca67dd7..dacded6d 100644
index aca67dd..ae897d5 100644
--- a/src/unexpand.c
+++ b/src/unexpand.c
@@ -39,6 +39,9 @@
@ -4194,7 +4194,7 @@ index aca67dd7..dacded6d 100644
if (blank)
{
@@ -178,16 +236,16 @@ unexpand (void)
@@ -178,30 +236,31 @@ unexpand (void)
if (next_tab_column < column)
error (EXIT_FAILURE, 0, _("input line is too long"));
@ -4212,9 +4212,10 @@ index aca67dd7..dacded6d 100644
- column++;
+ column += mb_width (c);
if (! (prev_blank && column == next_tab_column))
- if (! (prev_blank && column == next_tab_column))
+ if (! (prev_blank && column >= next_tab_column))
{
@@ -195,13 +253,14 @@ unexpand (void)
/* It is not yet known whether the pending blanks
will be replaced by tabs. */
if (column == next_tab_column)
one_blank_before_tab_stop = true;
@ -4287,7 +4288,7 @@ index aca67dd7..dacded6d 100644
}
diff --git a/tests/Coreutils.pm b/tests/Coreutils.pm
index 18e7bea8..24a141b1 100644
index 18e7bea..24a141b 100644
--- a/tests/Coreutils.pm
+++ b/tests/Coreutils.pm
@@ -269,6 +269,9 @@ sub run_tests ($$$$$)
@ -4302,7 +4303,7 @@ index 18e7bea8..24a141b1 100644
warn "$program_name: $test_name: test name is too long (> $max)\n";
diff --git a/tests/expand/mb.sh b/tests/expand/mb.sh
new file mode 100644
index 00000000..6d6497a3
index 0000000..6d6497a
--- /dev/null
+++ b/tests/expand/mb.sh
@@ -0,0 +1,183 @@
@ -4491,7 +4492,7 @@ index 00000000..6d6497a3
+Exit $fail
diff --git a/tests/i18n/sort.sh b/tests/i18n/sort.sh
new file mode 100644
index 00000000..26c95de9
index 0000000..26c95de
--- /dev/null
+++ b/tests/i18n/sort.sh
@@ -0,0 +1,29 @@
@ -4525,7 +4526,7 @@ index 00000000..26c95de9
+
+Exit $fail
diff --git a/tests/local.mk b/tests/local.mk
index d4546a0f..3bd74532 100644
index d4546a0..3bd7453 100644
--- a/tests/local.mk
+++ b/tests/local.mk
@@ -388,6 +388,8 @@ all_tests = \
@ -4554,7 +4555,7 @@ index d4546a0f..3bd74532 100644
# See tests/factor/create-test.sh.
diff --git a/tests/misc/expand.pl b/tests/misc/expand.pl
index 11f3fc4b..d609a2cb 100755
index 11f3fc4..d609a2c 100755
--- a/tests/misc/expand.pl
+++ b/tests/misc/expand.pl
@@ -27,6 +27,15 @@ my $prog = 'expand';
@ -4621,7 +4622,7 @@ index 11f3fc4b..d609a2cb 100755
my $verbose = $ENV{VERBOSE};
diff --git a/tests/misc/fold.pl b/tests/misc/fold.pl
index 00b43624..7d51bea5 100755
index 00b4362..7d51bea 100755
--- a/tests/misc/fold.pl
+++ b/tests/misc/fold.pl
@@ -20,9 +20,18 @@ use strict;
@ -4695,7 +4696,7 @@ index 00b43624..7d51bea5 100755
exit $fail;
diff --git a/tests/misc/sort-mb-tests.sh b/tests/misc/sort-mb-tests.sh
new file mode 100644
index 00000000..11836baa
index 0000000..11836ba
--- /dev/null
+++ b/tests/misc/sort-mb-tests.sh
@@ -0,0 +1,45 @@
@ -4745,7 +4746,7 @@ index 00000000..11836baa
+
+Exit $fail
diff --git a/tests/misc/unexpand.pl b/tests/misc/unexpand.pl
index 76bcbd4d..59eb8199 100755
index 76bcbd4..59eb819 100755
--- a/tests/misc/unexpand.pl
+++ b/tests/misc/unexpand.pl
@@ -27,6 +27,14 @@ my $limits = getlimits ();
@ -4802,7 +4803,7 @@ index 76bcbd4d..59eb8199 100755
my $verbose = $ENV{VERBOSE};
diff --git a/tests/pr/pr-tests.pl b/tests/pr/pr-tests.pl
index 6b34e0b6..34b4aeb0 100755
index 6b34e0b..34b4aeb 100755
--- a/tests/pr/pr-tests.pl
+++ b/tests/pr/pr-tests.pl
@@ -24,6 +24,15 @@ use strict;
@ -4871,7 +4872,7 @@ index 6b34e0b6..34b4aeb0 100755
my $verbose = $ENV{VERBOSE};
diff --git a/tests/sort/sort-merge.pl b/tests/sort/sort-merge.pl
index 89eed0c6..b855d738 100755
index 89eed0c..b855d73 100755
--- a/tests/sort/sort-merge.pl
+++ b/tests/sort/sort-merge.pl
@@ -26,6 +26,15 @@ my $prog = 'sort';
@ -4931,7 +4932,7 @@ index 89eed0c6..b855d738 100755
my $verbose = $ENV{VERBOSE};
diff --git a/tests/sort/sort.pl b/tests/sort/sort.pl
index d49f65f6..ebba9255 100755
index d49f65f..ebba925 100755
--- a/tests/sort/sort.pl
+++ b/tests/sort/sort.pl
@@ -24,10 +24,15 @@ my $prog = 'sort';
@ -5000,10 +5001,10 @@ index d49f65f6..ebba9255 100755
my $verbose = $ENV{VERBOSE};
diff --git a/tests/unexpand/mb.sh b/tests/unexpand/mb.sh
new file mode 100644
index 00000000..89cac17b
index 0000000..5229b7c
--- /dev/null
+++ b/tests/unexpand/mb.sh
@@ -0,0 +1,179 @@
@@ -0,0 +1,188 @@
+#!/bin/sh
+
+# Copyright (C) 2012-2015 Free Software Foundation, Inc.
@ -5182,6 +5183,16 @@ index 00000000..89cac17b
+ test "$ret" = 1 || test "$ret" = 0 || { cat err; fail=1; }
+done
+
+# A blank whose display width exceeds the tab distance must not overrun
+# the pending-blank buffer. With -t1 every column is a tab stop, so a
+# width-2 ideographic space steps over the stop without landing on it;
+# the run of blanks then grew pending_blank without bound.
+ideo_space=$(env printf '\u3000')
+{ yes "$ideo_space" | head -n 40000 | tr -d '\n'; echo; } |
+ unexpand -t1 >out 2>err; ret=$?
+test "$ret" = 0 || { cat err; fail=1; }
+
+Exit $fail
--
2.54.0

View File

@ -1,7 +1,7 @@
Summary: A set of basic GNU tools commonly used in shell scripts
Name: coreutils
Version: 9.5
Release: 11%{?dist}
Release: 12%{?dist}
# some used parts of gnulib are under various variants of LGPL
License: GPL-3.0-or-later AND GFDL-1.3-no-invariants-or-later AND LGPL-2.1-or-later AND LGPL-3.0-or-later
Url: https://www.gnu.org/software/coreutils/
@ -309,6 +309,9 @@ rm -f $RPM_BUILD_ROOT%{_infodir}/dir
%license COPYING
%changelog
* Wed Jun 10 2026 Lukáš Zaoral <lzaoral@redhat.com> - 9.5-12
- unexpand: fix heap overflow when a wide blank overshoots a tab stop (RHEL-152110)
* Tue Jun 09 2026 Lukáš Zaoral <lzaoral@redhat.com> - 9.5-11
- Fix cp --preserve=mode for ACLs (RHEL-132191)