diff --git a/src/uu/touch/src/touch.rs b/src/uu/touch/src/touch.rs index 4d1c9772c59..3fe1dcea91c 100644 --- a/src/uu/touch/src/touch.rs +++ b/src/uu/touch/src/touch.rs @@ -741,7 +741,10 @@ fn parse_date(ref_zoned: Zoned, s: &str) -> Result { /// - 68 and before is interpreted as 20xx /// - 69 and after is interpreted as 19xx fn prepend_century(s: &str) -> UResult { - let first_two_digits = s[..2].parse::().map_err(|_| { + // Take the first two chars rather than byte-slicing `s[..2]`: a leading + // multibyte char would otherwise split a UTF-8 boundary and panic. + let first_two: String = s.chars().take(2).collect(); + let first_two_digits = first_two.parse::().map_err(|_| { USimpleError::new( 1, translate!("touch-error-invalid-date-ts-format", "date" => s.quote()), diff --git a/tests/by-util/test_touch.rs b/tests/by-util/test_touch.rs index fbabdb75c65..0f3921b17f9 100644 --- a/tests/by-util/test_touch.rs +++ b/tests/by-util/test_touch.rs @@ -978,6 +978,16 @@ fn test_touch_invalid_date_format() { .stderr_contains("touch: invalid date format '+1000000000000 years'"); } +#[test] +fn test_touch_invalid_timestamp_leading_multibyte_char() { + for ts in ["€123456789", "€23456789012"] { + new_ucmd!() + .args(&["-t", ts, "f"]) + .fails_with_code(1) + .stderr_only(format!("touch: invalid date ts format '{ts}'\n")); + } +} + #[test] #[cfg(not(target_os = "freebsd"))] fn test_touch_symlink_with_no_deref() {