* 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


Reply via email to