* src/sort.c (sortlines): Don't share compressed temporary files when
the same input is given multiple times.
* tests/sort/sort-compress.sh: Print 'seq --version'. Add a test case
that would previously hang.
* NEWS: Mention the bug fix.
Discovered by Anthropic's OSS Scanner, as bug ANT-2026-MYSN7JAW.
---
NEWS | 4 ++++
src/sort.c | 21 ++++++++++++++++-----
tests/sort/sort-compress.sh | 21 +++++++++++++++++++--
3 files changed, 39 insertions(+), 7 deletions(-)
diff --git a/NEWS b/NEWS
index 5e9360c23..a5ea61a62 100644
--- a/NEWS
+++ b/NEWS
@@ -186,6 +186,10 @@ GNU coreutils NEWS -*-
outline -*-
environment.
[bug introduced in coreutils-9.9]
+ 'sort -m --compress-program=PROG -o FILE' no longer hangs when given
+ the same input file more than once.
+ [bug introduced in coreutils-8.8]
+
'split --hex-suffixes=FROM' now generates consecutive suffixes when FROM
contains any of the digits a to f. Previously it read beyond the suffix
alphabet, giving arbitrary and repeated file names, thus losing output.
diff --git a/src/sort.c b/src/sort.c
index 962588c34..74f712b94 100644
--- a/src/sort.c
+++ b/src/sort.c
@@ -4103,9 +4103,10 @@ sortlines (struct line *restrict lines, size_t nthreads,
/* Scan through FILES[NTEMPS .. NFILES-1] looking for files that are
the same as OUTFILE. If found, replace each with the same
temporary copy that can be merged into OUTFILE without destroying
- OUTFILE before it is completely read. This temporary copy does not
- count as a merge temp, so don't worry about incrementing NTEMPS in
- the caller; final cleanup will remove it, not zaptemp.
+ OUTFILE before it is completely read, or with its own file if
+ temporary files are compressed. This temporary copy does not count
+ as a merge temp, so don't worry about incrementing NTEMPS in the
+ caller; final cleanup will remove it, not zaptemp.
This test ensures that an otherwise-erroneous use like
"sort -m -o FILE ... FILE ..." copies FILE before writing to it.
@@ -4146,11 +4147,21 @@ avoid_trashing_input (struct sortfile *files, size_t
ntemps,
if (same)
{
- if (! tempcopy)
+ if (! tempcopy || tempcopy->state != UNCOMPRESSED)
{
FILE *tftp;
+ struct sortfile src = files[i];
+
+ /* Don't share compressed temporary files when the same input
+ file is given multiple times since each tempnode only
+ tracks a single PID. */
+ if (tempcopy)
+ {
+ src.name = tempcopy->name;
+ src.temp = tempcopy;
+ }
tempcopy = create_temp (&tftp);
- mergefiles (&files[i], 0, 1, tftp, tempcopy->name);
+ mergefiles (&src, 0, 1, tftp, tempcopy->name);
}
files[i].name = tempcopy->name;
diff --git a/tests/sort/sort-compress.sh b/tests/sort/sort-compress.sh
index d82a0cd67..48c30de72 100755
--- a/tests/sort/sort-compress.sh
+++ b/tests/sort/sort-compress.sh
@@ -17,7 +17,7 @@
# along with this program. If not, see <https://www.gnu.org/licenses/>.
. "${srcdir=.}/tests/init.sh"; path_prepend_ ./src
-print_ver_ sort
+print_ver_ sort seq
require_trap_signame_
seq -w 2000 > exp || framework_failure_
@@ -100,7 +100,24 @@ rm -f ok
PATH=.:$PATH sort --compress-program=dzip -S 1k in > out || fail=1
compare exp out || fail=1
test -f ok || fail=1
-rm -f dzip ok
+
+# Make sure that 'sort' doesn't hang when the output file is given as
+# the input multiple times.
+# FIXME: This assumes the input file is larger than the default pipe size.
+seq -w 100000 >exp1 &&
+sed p exp1 >exp2 &&
+cp exp1 inp || framework_failure_
+timeout 10 sort -m --compress-program=./dzip -o inp inp inp >out 2>err ||
fail=1
+compare exp2 inp || fail=1
+compare /dev/null out || fail=1
+compare /dev/null errr || fail=1
+cp exp1 inp || framework_failure_
+timeout 10 sort -m --compress-program=./dzip -o inp - - <inp >out 2>err \
+ || fail=1
+compare exp2 inp || fail=1
+compare /dev/null out || fail=1
+compare /dev/null err || fail=1
+rm -f dzip ok || framework_failure_
# Check the behavior of 'sort' when the program specified by --compress-program
# does not exist.
--
2.56.0