From: Ismail Ramzi <[email protected]> When FROM contained the digits a-f, next_file_name() derived the suffix index with c - '0', giving values up to 54 for a 16 character alphabet. Incrementing the suffix then read past the end of the alphabet string and used those bytes in the output file names, e.g., 'split --hex-suffixes=f' created x0f, x0s, x0u.
* src/split.c (next_file_name): Map start value characters through the suffix alphabet rather than assuming decimal digits. (main): Reject a start value not valid for the final suffix alphabet, as can happen with --hex-suffixes=a --numeric-suffixes. (set_suffix_length): Determine width using base 16 for hex suffixes. * tests/split/numeric.sh: Add test cases. * NEWS: Mention the bug fix. Link: https://github.com/coreutils/coreutils/pull/404 Also discovered by Anthropic's OSS Scanner, as bug ANT-2026-DWADVADP. --- NEWS | 5 +++++ src/split.c | 18 +++++++++++++++--- tests/split/numeric.sh | 23 +++++++++++++++++++++++ 3 files changed, 43 insertions(+), 3 deletions(-) diff --git a/NEWS b/NEWS index 6b4ed1d7b..6fddbaaa3 100644 --- a/NEWS +++ b/NEWS @@ -148,6 +148,11 @@ GNU coreutils NEWS -*- outline -*- environment. [bug introduced in coreutils-9.9] + '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. + [bug introduced with the --hex-suffixes option in coreutils-8.28] + '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 fd3dae24b..ec9160114 100644 --- a/src/split.c +++ b/src/split.c @@ -186,10 +186,11 @@ set_suffix_length (intmax_t n_units, enum Split_type split_type) || split_type == type_rr) { intmax_t n_units_end = n_units - 1; + idx_t alphabet_len = strlen (suffix_alphabet); if (numeric_suffix_start) { intmax_t n_start; - strtol_error e = xstrtoimax (numeric_suffix_start, NULL, 10, + strtol_error e = xstrtoimax (numeric_suffix_start, NULL, alphabet_len, &n_start, ""); if (e == LONGINT_OK && n_start < n_units) { @@ -202,7 +203,6 @@ set_suffix_length (intmax_t n_units, enum Split_type split_type) } } - idx_t alphabet_len = strlen (suffix_alphabet); do suffix_length_needed++; while (n_units_end /= alphabet_len); @@ -480,7 +480,8 @@ new_name: /* Update the suffix index. */ idx_t *sufindex_end = sufindex + suffix_length; while (i-- != 0) - *--sufindex_end = numeric_suffix_start[i] - '0'; + *--sufindex_end = (strchr (suffix_alphabet, numeric_suffix_start[i]) + - suffix_alphabet); } #if ! _POSIX_NO_TRUNC && HAVE_PATHCONF && defined _PC_NAME_MAX @@ -1668,6 +1669,17 @@ main (int argc, char **argv) usage (EXIT_FAILURE); } + /* A start value given with --hex-suffixes may be invalid + for a later --numeric-suffixes. */ + if (numeric_suffix_start + && (strlen (numeric_suffix_start) + != strspn (numeric_suffix_start, suffix_alphabet))) + { + error (0, 0, _("%s: invalid start value for numerical suffix"), + quote (numeric_suffix_start)); + usage (EXIT_FAILURE); + } + /* Check that the suffix length is large enough for the numerical suffix start value. */ if (numeric_suffix_start && strlen (numeric_suffix_start) > suffix_length) diff --git a/tests/split/numeric.sh b/tests/split/numeric.sh index 9ae6cb280..204ab98e5 100755 --- a/tests/split/numeric.sh +++ b/tests/split/numeric.sh @@ -48,4 +48,27 @@ for mode in 'numeric' 'hex'; do returns_ 1 split --$mode-suffixes=one in 2> /dev/null || fail=1 done +# Check hexadecimal start values that are not decimal digits +rm -f x?? || framework_failure_ +split --hex-suffixes=f --lines=2 in || fail=1 +compare exp-0 x0f || fail=1 +compare exp-1 x10 || fail=1 +compare exp-2 x11 || fail=1 + +# A hexadecimal start value is invalid for a later --numeric-suffixes +returns_ 1 split --hex-suffixes=a --numeric-suffixes in 2> /dev/null || fail=1 + +# Only the final alphabet needs to accommodate the retained start value. +split --hex-suffixes=a -d -x --lines=2 in || fail=1 +compare exp-0 x0a || fail=1 +compare exp-1 x0b || fail=1 +compare exp-2 x0c || fail=1 + +# Interpret the start value as hex when determining the suffix length. +# 0x10 + 241 - 1 = 0x100, which requires three digits. +split --hex-suffixes=10 --number=r/241 in || fail=1 +test -f x010 && test -f x100 || fail=1 +returns_ 1 split -a2 --hex-suffixes=10 --number=r/241 in 2>/dev/null \ + || fail=1 + Exit $fail -- 2.56.0
