* src/split.c (lines_chunk_split): Make sure CHUNK_NO does not exceed N.
* tests/split/l-chunk.sh: Add a test case without --elide-empty-files
and with it.
* NEWS: Mention the bug fix.

Discovered by Anthropic's OSS Scanner, as bug ANT-2026-ZDDBFXXM.
---
 NEWS                   |  6 ++++++
 src/split.c            |  2 +-
 tests/split/l-chunk.sh | 33 +++++++++++++++++++++++++++++++++
 3 files changed, 40 insertions(+), 1 deletion(-)

diff --git a/NEWS b/NEWS
index d932371d7..e3a23fe54 100644
--- a/NEWS
+++ b/NEWS
@@ -195,6 +195,12 @@ GNU coreutils NEWS                                    -*- 
outline -*-
   alphabet, giving arbitrary and repeated file names, thus losing output.
   [bug introduced with the --hex-suffixes option in coreutils-8.28]
 
+  'split --number=l/N' no longer creates more than N output files,
+  truncating them if they exist, or loops infinitely when used with
+  --elide-empty-files, when the input is smaller than N bytes and ends
+  with a newline.
+  [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 da30b109d..71a097264 100644
--- a/src/split.c
+++ b/src/split.c
@@ -1052,7 +1052,7 @@ lines_chunk_split (intmax_t k, intmax_t n, char *buf, 
idx_t bufsize,
                 return;
               chunk_end += chunk_size + (chunk_no < rem_bytes);
               chunk_no++;
-              if (chunk_end <= n_written)
+              if (chunk_no <= n && chunk_end <= n_written)
                 {
                   if (! k)
                     cwrite (true, NULL, 0);
diff --git a/tests/split/l-chunk.sh b/tests/split/l-chunk.sh
index d79ed9b00..d1b072654 100755
--- a/tests/split/l-chunk.sh
+++ b/tests/split/l-chunk.sh
@@ -126,4 +126,37 @@ printf '5' > exp
 split -n l/7/7 in > out
 compare exp out || fail=1
 
+# In coreutils-9.2 through coreutils-9.12, this would exit with a failure
+# upon exhausting output files, overwriting them if they exist.
+printf 'a\n' >in &&
+printf 'b\n' >out.af &&
+cp out.af exp-out.af &&
+truncate -s 0 exp || framework_failure_
+split -n l/5 in out. >out 2>err || fail=1
+compare /dev/null out || fail=1
+compare /dev/null err || fail=1
+set -- out.*
+files="$*"
+exp_files='out.aa out.ab out.ac out.ad out.ae out.af'
+test "$files" = "$exp_files" || fail=1
+for file in $exp_files; do
+  exp=exp
+  if test $file = out.aa; then
+    exp=in
+  elif test $file = out.af; then
+    exp=exp-out.af
+  fi
+  compare $exp $file || fail=1
+done
+rm -f $exp_files || framework_failure_
+
+# In coreutils-9.2 through coreutils-9.12, this would loop infinitely.
+printf 'a\n' >in || fail=1
+timeout 10 split -e -n l/5 in out. >out 2>err || fail=1
+compare /dev/null out || fail=1
+compare /dev/null err || fail=1
+set -- out.*
+test "$*" = out.aa || fail=1
+compare in out.aa || fail=1
+
 Exit $fail
-- 
2.56.0


Reply via email to