Ideally, this would be two commits. One for the missing NEW_FILE_FLAG
check, and the other for the off-by-one. Given that the fixes are
small, I figured it would be more beneficial to keep each OSS scanner
issue related to one commit. I'm not 100% on it, though.

-- 8< --

* src/split.c (bytes_split): Don't lseek when the offset is already at
the correct position from reading. Don't add 1 to TO_WRITE.
* tests/split/filter.sh: Add two test cases.
* NEWS: Mention the bug fixes.

Discovered by Anthropic's OSS Scanner, as bug ANT-2026-D5DVHK7A.
---
 NEWS                  |  9 +++++++++
 src/split.c           |  9 +++++++--
 tests/split/filter.sh | 20 ++++++++++++++++++++
 3 files changed, 36 insertions(+), 2 deletions(-)

diff --git a/NEWS b/NEWS
index 382ac0c41..7e0e92ed8 100644
--- a/NEWS
+++ b/NEWS
@@ -225,6 +225,15 @@ GNU coreutils NEWS                                    -*- 
outline -*-
   accepting input or the entire input is processed.
   [bug introduced with --filter option in coreutils-8.13]
 
+  'split --bytes N --filter' no longer skips a whole chunk of input when a
+  filter exits without reading all of a chunk ending at an internal buffer
+  boundary.
+  [bug introduced in coreutils-8.28]
+
+  'split -n N' no longer shortens subsequent chunks by one byte after a filter
+  exits before it finishes reading all of its input.
+  [bug introduced in coreutils-9.2]
+
   'stty' again rejects empty speed arguments.
   Previously such arguments were accepted as value 0.
   [bug introduced in coreutils-9.8]
diff --git a/src/split.c b/src/split.c
index ea410dc9f..936f8d5ee 100644
--- a/src/split.c
+++ b/src/split.c
@@ -742,10 +742,15 @@ bytes_split (intmax_t n_bytes, intmax_t rem_bytes,
         }
       else
         {
-          if (! filter_ok
+          /* If the filter for the current chunk has stopped reading,
+             skip the rest of that chunk.  If the failed write is at
+             the end of a chunk, NEW_FILE_FLAG will be true.  In that
+             case the lseek is not needed and TO_WRITE refers to the
+             next chunk.  */
+          if (! filter_ok && ! new_file_flag
               && 0 <= lseek (STDIN_FILENO, to_write, SEEK_CUR))
             {
-              to_write = n_bytes + (opened + 1 < rem_bytes);
+              to_write = n_bytes + (opened < rem_bytes);
               new_file_flag = true;
             }
 
diff --git a/tests/split/filter.sh b/tests/split/filter.sh
index 90ee3a5f2..a38d98720 100755
--- a/tests/split/filter.sh
+++ b/tests/split/filter.sh
@@ -84,6 +84,26 @@ for mode in '' -u; do
   compare exp-xaa xaa || fail=1
   compare exp-xab xab || fail=1
 done
+rm -f x?? || framework_failure_
+
+# Make sure chunks aren't skipped if a filter exits early at the end
+# of an input buffer.
+for c in a b c d e f g; do
+  yes $c | tr -d '\n' | head -c 256k || framework_failure_
+done >in
+split --bytes 256k ---io-blksize=256k --filter='head -c1 >$FILE' in || fail=1
+# coreutils-8.28 to coreutils-9.12 would produce "aceg" here.
+test "$(cat x??)" = abcdefg || fail=1
+rm -f x?? || framework_failure_
+
+# Make sure that --number chunk sizes aren't affected by a filter exiting 
early.
+{ yes a | tr -d '\n' | head -c 300001 &&
+  yes b | tr -d '\n' | head -c 300001 &&
+  yes c | tr -d '\n' | head -c 300000; } >in || framework_failure_
+split --number 3 ---io-blksize=256k --filter='head -c1 >$FILE' in || fail=1
+# coreutils-9.2 to coreutils-9.12 would produce "abb" here.
+test "$(cat x??)" = abc || fail=1
+rm -f x?? || framework_failure_
 
 # Ensure that "endless" input _is_ processed for unbounded number of filters
 for buf in 1000 1000000; do
-- 
2.56.0


Reply via email to