diff --git a/coreutils-CVE-2025-5278.patch b/coreutils-CVE-2025-5278.patch
new file mode 100644
index 0000000..f001bbb
--- /dev/null
+++ b/coreutils-CVE-2025-5278.patch
@@ -0,0 +1,107 @@
+From ec1b9be71e9c033e48c40cac58d784a94d6d3179 Mon Sep 17 00:00:00 2001
+From: =?UTF-8?q?P=C3=A1draig=20Brady?=
+Date: Tue, 20 May 2025 16:03:44 +0100
+Subject: [PATCH] sort: fix buffer under-read (CWE-127)
+
+* src/sort.c (begfield): Check pointer adjustment
+to avoid Out-of-range pointer offset (CWE-823).
+(limfield): Likewise.
+* tests/sort/sort-field-limit.sh: Add a new test,
+which triggers with ASAN or Valgrind.
+* tests/local.mk: Reference the new test.
+Fixes https://bugs.gnu.org/78507
+
+(cherry picked from commit 8c9602e3a145e9596dc1a63c6ed67865814b6633)
+---
+ src/sort.c | 12 ++++++++++--
+ tests/local.mk | 1 +
+ tests/sort/sort-field-limit.sh | 35 ++++++++++++++++++++++++++++++++++
+ 3 files changed, 46 insertions(+), 2 deletions(-)
+ create mode 100755 tests/sort/sort-field-limit.sh
+
+diff --git a/src/sort.c b/src/sort.c
+index 329ed45dc..a78dbf713 100644
+--- a/src/sort.c
++++ b/src/sort.c
+@@ -1643,7 +1643,11 @@ begfield (struct line const *line, struct keyfield const *key)
+ ++ptr;
+
+ /* Advance PTR by SCHAR (if possible), but no further than LIM. */
+- ptr = MIN (lim, ptr + schar);
++ size_t remaining_bytes = lim - ptr;
++ if (schar < remaining_bytes)
++ ptr += schar;
++ else
++ ptr = lim;
+
+ return ptr;
+ }
+@@ -1744,7 +1748,11 @@ limfield (struct line const *line, struct keyfield const *key)
+ ++ptr;
+
+ /* Advance PTR by ECHAR (if possible), but no further than LIM. */
+- ptr = MIN (lim, ptr + echar);
++ size_t remaining_bytes = lim - ptr;
++ if (echar < remaining_bytes)
++ ptr += echar;
++ else
++ ptr = lim;
+ }
+
+ return ptr;
+diff --git a/tests/local.mk b/tests/local.mk
+index a41790799..69cf80f23 100644
+--- a/tests/local.mk
++++ b/tests/local.mk
+@@ -369,6 +369,7 @@ all_tests = \
+ tests/misc/sort-debug-keys.sh \
+ tests/misc/sort-debug-warn.sh \
+ tests/misc/sort-discrim.sh \
++ tests/sort/sort-field-limit.sh \
+ tests/misc/sort-files0-from.pl \
+ tests/misc/sort-float.sh \
+ tests/misc/sort-h-thousands-sep.sh \
+diff --git a/tests/sort/sort-field-limit.sh b/tests/sort/sort-field-limit.sh
+new file mode 100755
+index 000000000..52d8e1d17
+--- /dev/null
++++ b/tests/sort/sort-field-limit.sh
+@@ -0,0 +1,35 @@
++#!/bin/sh
++# From 7.2-9.7, this would trigger an out of bounds mem read
++
++# Copyright (C) 2025 Free Software Foundation, Inc.
++
++# This program is free software: you can redistribute it and/or modify
++# it under the terms of the GNU General Public License as published by
++# the Free Software Foundation, either version 3 of the License, or
++# (at your option) any later version.
++
++# This program is distributed in the hope that it will be useful,
++# but WITHOUT ANY WARRANTY; without even the implied warranty of
++# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
++# GNU General Public License for more details.
++
++# You should have received a copy of the GNU General Public License
++# along with this program. If not, see .
++
++. "${srcdir=.}/tests/init.sh"; path_prepend_ ./src
++print_ver_ sort
++getlimits_
++
++# This issue triggers with valgrind or ASAN
++valgrind --error-exitcode=1 sort --version 2>/dev/null &&
++ VALGRIND='valgrind --error-exitcode=1'
++
++{ printf '%s\n' aa bb; } > in || framework_failure_
++
++_POSIX2_VERSION=200809 $VALGRIND sort +0.${SIZE_MAX}R in > out || fail=1
++compare in out || fail=1
++
++_POSIX2_VERSION=200809 $VALGRIND sort +1 -1.${SIZE_MAX}R in > out || fail=1
++compare in out || fail=1
++
++Exit $fail
+--
+2.54.0
+
diff --git a/coreutils-i18n.patch b/coreutils-i18n.patch
index c323cff..771ac9e 100644
--- a/coreutils-i18n.patch
+++ b/coreutils-i18n.patch
@@ -6,7 +6,7 @@ Subject: [PATCH] coreutils-i18n.patch
TODO: merge upstream
---
lib/linebuffer.h | 8 +
- src/fold.c | 308 +++++++++++++--
+ src/fold.c | 309 +++++++++++++--
src/join.c | 359 ++++++++++++++---
src/pr.c | 443 +++++++++++++++++++--
src/sort.c | 764 ++++++++++++++++++++++++++++++++++--
@@ -22,12 +22,12 @@ TODO: merge upstream
tests/misc/unexpand.pl | 39 ++
tests/misc/uniq.pl | 55 +++
tests/pr/pr-tests.pl | 49 +++
- 17 files changed, 2290 insertions(+), 154 deletions(-)
+ 17 files changed, 2291 insertions(+), 154 deletions(-)
create mode 100755 tests/i18n/sort.sh
create mode 100755 tests/misc/sort-mb-tests.sh
diff --git a/lib/linebuffer.h b/lib/linebuffer.h
-index 64181af..9b8fe5a 100644
+index b19fea70f..1f0a1a25d 100644
--- a/lib/linebuffer.h
+++ b/lib/linebuffer.h
@@ -21,6 +21,11 @@
@@ -53,7 +53,7 @@ index 64181af..9b8fe5a 100644
/* Initialize linebuffer LINEBUFFER for use. */
diff --git a/src/fold.c b/src/fold.c
-index 8cd0d6b..d23edd5 100644
+index 2221f7207..a601f897b 100644
--- a/src/fold.c
+++ b/src/fold.c
@@ -22,12 +22,34 @@
@@ -209,10 +209,10 @@ index 8cd0d6b..d23edd5 100644
- saved_errno = errno;
+ *saved_errno = errno;
-
- if (offset_out)
- fwrite (line_out, sizeof (char), (size_t) offset_out, stdout);
-
++
++ if (offset_out)
++ fwrite (line_out, sizeof (char), (size_t) offset_out, stdout);
++
+}
+
+#if HAVE_MBRTOWC
@@ -385,10 +385,10 @@ index 8cd0d6b..d23edd5 100644
+ }
+
+ *saved_errno = errno;
-+
-+ if (offset_out)
-+ fwrite (line_out, sizeof (char), (size_t) offset_out, stdout);
-+
+
+ if (offset_out)
+ fwrite (line_out, sizeof (char), (size_t) offset_out, stdout);
+
+}
+#endif
+
@@ -427,7 +427,7 @@ index 8cd0d6b..d23edd5 100644
if (ferror (istream))
{
error (0, saved_errno, "%s", quotef (filename));
-@@ -252,7 +499,8 @@ main (int argc, char **argv)
+@@ -252,7 +500,8 @@ main (int argc, char **argv)
atexit (close_stdout);
@@ -437,7 +437,7 @@ index 8cd0d6b..d23edd5 100644
while ((optc = getopt_long (argc, argv, shortopts, longopts, NULL)) != -1)
{
-@@ -261,7 +509,15 @@ main (int argc, char **argv)
+@@ -261,7 +510,15 @@ main (int argc, char **argv)
switch (optc)
{
case 'b': /* Count bytes rather than columns. */
@@ -455,7 +455,7 @@ index 8cd0d6b..d23edd5 100644
case 's': /* Break at word boundaries. */
diff --git a/src/join.c b/src/join.c
-index 98b461c..9990f38 100644
+index 1accfd48d..e27243f87 100644
--- a/src/join.c
+++ b/src/join.c
@@ -22,19 +22,33 @@
@@ -948,7 +948,7 @@ index 98b461c..9990f38 100644
break;
diff --git a/src/pr.c b/src/pr.c
-index 26f221f..633f50e 100644
+index 160608276..6374a7fb4 100644
--- a/src/pr.c
+++ b/src/pr.c
@@ -311,6 +311,24 @@
@@ -1714,7 +1714,7 @@ index 26f221f..633f50e 100644
looking for more options and printing the next batch of files.
diff --git a/src/sort.c b/src/sort.c
-index 6d2eec5..f189a0d 100644
+index a78dbf713..57152ce3d 100644
--- a/src/sort.c
+++ b/src/sort.c
@@ -29,6 +29,14 @@
@@ -1971,7 +1971,7 @@ index 6d2eec5..f189a0d 100644
++ptr;
if (ptr < lim)
++ptr;
-@@ -1648,11 +1797,70 @@ begfield (struct line const *line, struct keyfield const *key)
+@@ -1652,11 +1801,70 @@ begfield (struct line const *line, struct keyfield const *key)
return ptr;
}
@@ -2043,7 +2043,7 @@ index 6d2eec5..f189a0d 100644
{
char *ptr = line->text, *lim = ptr + line->length - 1;
size_t eword = key->eword, echar = key->echar;
-@@ -1667,10 +1875,10 @@ limfield (struct line const *line, struct keyfield const *key)
+@@ -1671,10 +1879,10 @@ limfield (struct line const *line, struct keyfield const *key)
'beginning' is the first character following the delimiting TAB.
Otherwise, leave PTR pointing at the first 'blank' character after
the preceding field. */
@@ -2056,7 +2056,7 @@ index 6d2eec5..f189a0d 100644
++ptr;
if (ptr < lim && (eword || echar))
++ptr;
-@@ -1716,10 +1924,10 @@ limfield (struct line const *line, struct keyfield const *key)
+@@ -1720,10 +1928,10 @@ limfield (struct line const *line, struct keyfield const *key)
*/
/* Make LIM point to the end of (one byte past) the current field. */
@@ -2069,7 +2069,7 @@ index 6d2eec5..f189a0d 100644
if (newlim)
lim = newlim;
}
-@@ -1750,6 +1958,130 @@ limfield (struct line const *line, struct keyfield const *key)
+@@ -1758,6 +1966,130 @@ limfield (struct line const *line, struct keyfield const *key)
return ptr;
}
@@ -2200,7 +2200,7 @@ index 6d2eec5..f189a0d 100644
/* Fill BUF reading from FP, moving buf->left bytes from the end
of buf->buf to the beginning first. If EOF is reached and the
file wasn't terminated by a newline, supply one. Set up BUF's line
-@@ -1836,8 +2168,22 @@ fillbuf (struct buffer *buf, FILE *fp, char const *file)
+@@ -1844,8 +2176,22 @@ fillbuf (struct buffer *buf, FILE *fp, char const *file)
else
{
if (key->skipsblanks)
@@ -2225,7 +2225,7 @@ index 6d2eec5..f189a0d 100644
line->keybeg = line_start;
}
}
-@@ -1987,7 +2333,7 @@ human_numcompare (char const *a, char const *b)
+@@ -1995,7 +2341,7 @@ human_numcompare (char const *a, char const *b)
hideously fast. */
static int
@@ -2234,7 +2234,7 @@ index 6d2eec5..f189a0d 100644
{
while (blanks[to_uchar (*a)])
a++;
-@@ -1997,6 +2343,25 @@ numcompare (char const *a, char const *b)
+@@ -2005,6 +2351,25 @@ numcompare (char const *a, char const *b)
return strnumcmp (a, b, decimal_point, thousands_sep);
}
@@ -2260,7 +2260,7 @@ index 6d2eec5..f189a0d 100644
/* Work around a problem whereby the long double value returned by glibc's
strtold ("NaN", ...) contains uninitialized bits: clear all bytes of
A and B before calling strtold. FIXME: remove this function if
-@@ -2047,7 +2412,7 @@ general_numcompare (char const *sa, char const *sb)
+@@ -2055,7 +2420,7 @@ general_numcompare (char const *sa, char const *sb)
Return 0 if the name in S is not recognized. */
static int
@@ -2269,7 +2269,7 @@ index 6d2eec5..f189a0d 100644
{
size_t lo = 0;
size_t hi = MONTHS_PER_YEAR;
-@@ -2323,15 +2688,14 @@ debug_key (struct line const *line, struct keyfield const *key)
+@@ -2331,15 +2696,14 @@ debug_key (struct line const *line, struct keyfield const *key)
char saved = *lim;
*lim = '\0';
@@ -2287,7 +2287,7 @@ index 6d2eec5..f189a0d 100644
else if (key->general_numeric)
ignore_value (strtold (beg, &tighter_lim));
else if (key->numeric || key->human_numeric)
-@@ -2465,7 +2829,7 @@ key_warnings (struct keyfield const *gkey, bool gkey_only)
+@@ -2473,7 +2837,7 @@ key_warnings (struct keyfield const *gkey, bool gkey_only)
/* Warn about significant leading blanks. */
bool implicit_skip = key_numeric (key) || key->month;
bool line_offset = key->eword == 0 && key->echar != 0; /* -k1.x,1.y */
@@ -2296,7 +2296,7 @@ index 6d2eec5..f189a0d 100644
&& ((!key->skipsblanks && !implicit_skip)
|| (!key->skipsblanks && key->schar)
|| (!key->skipeblanks && key->echar)))
-@@ -2523,11 +2887,87 @@ key_warnings (struct keyfield const *gkey, bool gkey_only)
+@@ -2531,11 +2895,87 @@ key_warnings (struct keyfield const *gkey, bool gkey_only)
error (0, 0, _("option '-r' only applies to last-resort comparison"));
}
@@ -2385,7 +2385,7 @@ index 6d2eec5..f189a0d 100644
{
struct keyfield *key = keylist;
-@@ -2612,7 +3052,7 @@ keycompare (struct line const *a, struct line const *b)
+@@ -2620,7 +3060,7 @@ keycompare (struct line const *a, struct line const *b)
else if (key->human_numeric)
diff = human_numcompare (ta, tb);
else if (key->month)
@@ -2394,7 +2394,7 @@ index 6d2eec5..f189a0d 100644
else if (key->random)
diff = compare_random (ta, tlena, tb, tlenb);
else if (key->version)
-@@ -2728,6 +3168,211 @@ keycompare (struct line const *a, struct line const *b)
+@@ -2736,6 +3176,211 @@ keycompare (struct line const *a, struct line const *b)
return key->reverse ? -diff : diff;
}
@@ -2606,7 +2606,7 @@ index 6d2eec5..f189a0d 100644
/* Compare two lines A and B, returning negative, zero, or positive
depending on whether A compares less than, equal to, or greater than B. */
-@@ -2755,7 +3400,7 @@ compare (struct line const *a, struct line const *b)
+@@ -2763,7 +3408,7 @@ compare (struct line const *a, struct line const *b)
diff = - NONZERO (blen);
else if (blen == 0)
diff = 1;
@@ -2615,7 +2615,7 @@ index 6d2eec5..f189a0d 100644
{
/* xmemcoll0 is a performance enhancement as
it will not unconditionally write '\0' after the
-@@ -4145,6 +4790,7 @@ set_ordering (char const *s, struct keyfield *key, enum blanktype blanktype)
+@@ -4153,6 +4798,7 @@ set_ordering (char const *s, struct keyfield *key, enum blanktype blanktype)
break;
case 'f':
key->translate = fold_toupper;
@@ -2623,7 +2623,7 @@ index 6d2eec5..f189a0d 100644
break;
case 'g':
key->general_numeric = true;
-@@ -4224,7 +4870,7 @@ main (int argc, char **argv)
+@@ -4232,7 +4878,7 @@ main (int argc, char **argv)
initialize_exit_failure (SORT_FAILURE);
hard_LC_COLLATE = hard_locale (LC_COLLATE);
@@ -2632,7 +2632,7 @@ index 6d2eec5..f189a0d 100644
hard_LC_TIME = hard_locale (LC_TIME);
#endif
-@@ -4245,6 +4891,29 @@ main (int argc, char **argv)
+@@ -4253,6 +4899,29 @@ main (int argc, char **argv)
thousands_sep = -1;
}
@@ -2662,7 +2662,7 @@ index 6d2eec5..f189a0d 100644
have_read_stdin = false;
inittables ();
-@@ -4519,13 +5188,34 @@ main (int argc, char **argv)
+@@ -4527,13 +5196,34 @@ main (int argc, char **argv)
case 't':
{
@@ -2701,7 +2701,7 @@ index 6d2eec5..f189a0d 100644
else
{
/* Provoke with 'sort -txx'. Complain about
-@@ -4536,9 +5226,11 @@ main (int argc, char **argv)
+@@ -4544,9 +5234,11 @@ main (int argc, char **argv)
quote (optarg));
}
}
@@ -2715,7 +2715,7 @@ index 6d2eec5..f189a0d 100644
}
break;
-@@ -4767,12 +5459,10 @@ main (int argc, char **argv)
+@@ -4775,12 +5467,10 @@ main (int argc, char **argv)
sort (files, nfiles, outfile, nthreads);
}
@@ -2729,7 +2729,7 @@ index 6d2eec5..f189a0d 100644
if (have_read_stdin && fclose (stdin) == EOF)
sort_die (_("close failed"), "-");
diff --git a/src/uniq.c b/src/uniq.c
-index 87a0c93..9f755d9 100644
+index e0247579b..534231f6d 100644
--- a/src/uniq.c
+++ b/src/uniq.c
@@ -21,6 +21,17 @@
@@ -2895,7 +2895,7 @@ index 87a0c93..9f755d9 100644
check_chars = SIZE_MAX;
diff --git a/tests/i18n/sort.sh b/tests/i18n/sort.sh
new file mode 100755
-index 0000000..26c95de
+index 000000000..26c95de9a
--- /dev/null
+++ b/tests/i18n/sort.sh
@@ -0,0 +1,29 @@
@@ -2929,11 +2929,11 @@ index 0000000..26c95de
+
+Exit $fail
diff --git a/tests/local.mk b/tests/local.mk
-index 568944e..192f776 100644
+index 08c403d10..9e366ebed 100644
--- a/tests/local.mk
+++ b/tests/local.mk
-@@ -369,6 +369,8 @@ all_tests = \
- tests/misc/sort-discrim.sh \
+@@ -372,6 +372,8 @@ all_tests = \
+ tests/sort/sort-field-limit.sh \
tests/misc/sort-files0-from.pl \
tests/misc/sort-float.sh \
+ tests/misc/sort-mb-tests.sh \
@@ -2942,7 +2942,7 @@ index 568944e..192f776 100644
tests/misc/sort-merge.pl \
tests/misc/sort-merge-fdlimit.sh \
diff --git a/tests/misc/expand.pl b/tests/misc/expand.pl
-index 8a9cad1..9293e39 100755
+index 8e5beaf61..040e321b2 100755
--- a/tests/misc/expand.pl
+++ b/tests/misc/expand.pl
@@ -27,6 +27,15 @@ my $prog = 'expand';
@@ -3009,7 +3009,7 @@ index 8a9cad1..9293e39 100755
my $verbose = $ENV{VERBOSE};
diff --git a/tests/misc/fold.pl b/tests/misc/fold.pl
-index 7b192b4..76f073f 100755
+index 3a72629a3..af85ee54f 100755
--- a/tests/misc/fold.pl
+++ b/tests/misc/fold.pl
@@ -20,9 +20,18 @@ use strict;
@@ -3082,7 +3082,7 @@ index 7b192b4..76f073f 100755
my $fail = run_tests ($program_name, $prog, \@Tests, $save_temps, $verbose);
exit $fail;
diff --git a/tests/misc/join.pl b/tests/misc/join.pl
-index 4d399d8..07f2823 100755
+index 8ab93ad75..bdd3bc731 100755
--- a/tests/misc/join.pl
+++ b/tests/misc/join.pl
@@ -25,6 +25,15 @@ my $limits = getlimits ();
@@ -3153,7 +3153,7 @@ index 4d399d8..07f2823 100755
diff --git a/tests/misc/sort-mb-tests.sh b/tests/misc/sort-mb-tests.sh
new file mode 100755
-index 0000000..11836ba
+index 000000000..11836baa5
--- /dev/null
+++ b/tests/misc/sort-mb-tests.sh
@@ -0,0 +1,45 @@
@@ -3203,7 +3203,7 @@ index 0000000..11836ba
+
+Exit $fail
diff --git a/tests/misc/sort-merge.pl b/tests/misc/sort-merge.pl
-index 23f6ed2..402a987 100755
+index 3a485803b..4540d2f84 100755
--- a/tests/misc/sort-merge.pl
+++ b/tests/misc/sort-merge.pl
@@ -26,6 +26,15 @@ my $prog = 'sort';
@@ -3263,7 +3263,7 @@ index 23f6ed2..402a987 100755
my $verbose = $ENV{VERBOSE};
diff --git a/tests/misc/sort.pl b/tests/misc/sort.pl
-index c3e7f8e..6ecd3ff 100755
+index 081618d08..af217b49d 100755
--- a/tests/misc/sort.pl
+++ b/tests/misc/sort.pl
@@ -24,10 +24,15 @@ my $prog = 'sort';
@@ -3331,7 +3331,7 @@ index c3e7f8e..6ecd3ff 100755
my $save_temps = $ENV{DEBUG};
my $verbose = $ENV{VERBOSE};
diff --git a/tests/misc/unexpand.pl b/tests/misc/unexpand.pl
-index 6ba6d40..de86723 100755
+index c8f5ff918..518e82002 100755
--- a/tests/misc/unexpand.pl
+++ b/tests/misc/unexpand.pl
@@ -27,6 +27,14 @@ my $limits = getlimits ();
@@ -3388,7 +3388,7 @@ index 6ba6d40..de86723 100755
my $verbose = $ENV{VERBOSE};
diff --git a/tests/misc/uniq.pl b/tests/misc/uniq.pl
-index f028036..8eaf59a 100755
+index 6f332c8a1..ce49925eb 100755
--- a/tests/misc/uniq.pl
+++ b/tests/misc/uniq.pl
@@ -23,9 +23,17 @@ my $limits = getlimits ();
@@ -3464,7 +3464,7 @@ index f028036..8eaf59a 100755
@Tests = triple_test \@Tests;
diff --git a/tests/pr/pr-tests.pl b/tests/pr/pr-tests.pl
-index ec3980a..136657d 100755
+index 272036997..8427f2b4a 100755
--- a/tests/pr/pr-tests.pl
+++ b/tests/pr/pr-tests.pl
@@ -24,6 +24,15 @@ use strict;
@@ -3533,5 +3533,5 @@ index ec3980a..136657d 100755
my $verbose = $ENV{VERBOSE};
--
-2.7.4
+2.54.0
diff --git a/coreutils.spec b/coreutils.spec
index 289c530..f980f18 100644
--- a/coreutils.spec
+++ b/coreutils.spec
@@ -84,6 +84,10 @@ Patch23: coreutils-nproc-affinity-2.patch
# fix sort fdlimit test failures on s390x with /dev/z90crypt (RHEL-60290)
Patch24: coreutils-8.32-s390x-fdlimit.patch
+# CVE-2025-5278 - Heap Buffer Under-Read in sort via Key Specification
+# upstream commit: https://cgit.git.savannah.gnu.org/cgit/coreutils.git/commit/?id=8c9602e3a145e9596dc1a63c6ed67865814b6633
+Patch25: coreutils-CVE-2025-5278.patch
+
# disable the test-lock gnulib test prone to deadlock
Patch100: coreutils-8.26-test-lock.patch