From 5fe6fe62c3323bde623158666f260a8fa0a461eb Mon Sep 17 00:00:00 2001 From: MsfPablo Date: Fri, 7 Aug 2026 11:34:13 +0200 Subject: [PATCH 1/2] find: don't panic on invalid-UTF-8 arguments std::env::args() panics if any argument is not valid UTF-8, so a non-UTF-8 -printf format string (or any other argument) crashed find with exit code 101 instead of a normal error. Collect args via env::args_os() instead and report invalid UTF-8 as a regular error (exit 1) rather than panicking. Fixes #816 --- src/find/main.rs | 12 +++++++++++- tests/test_find.rs | 15 +++++++++++++++ 2 files changed, 26 insertions(+), 1 deletion(-) diff --git a/src/find/main.rs b/src/find/main.rs index 1d8787e8..8a8a80c5 100644 --- a/src/find/main.rs +++ b/src/find/main.rs @@ -10,7 +10,17 @@ fn main() { // the downstream software of the standard output stream closes the pipe and triggers a panic. uucore::panic::mute_sigpipe_panic(); - let args = std::env::args().collect::>(); + let args = std::env::args_os() + .map(|arg| { + arg.into_string().unwrap_or_else(|invalid| { + eprintln!( + "find: invalid UTF-8 was found in one of the arguments: {}", + invalid.to_string_lossy() + ); + std::process::exit(1); + }) + }) + .collect::>(); let strs: Vec<&str> = args.iter().map(std::convert::AsRef::as_ref).collect(); let deps = findutils::find::StandardDependencies::new(); std::process::exit(findutils::find::find_main(&strs, &deps)); diff --git a/tests/test_find.rs b/tests/test_find.rs index ce223d56..26e207a6 100644 --- a/tests/test_find.rs +++ b/tests/test_find.rs @@ -578,6 +578,21 @@ fn find_printf_octal_escape_before_multibyte_char() { .stdout_only("\0€\n"); } +#[cfg(unix)] +#[test] +fn find_printf_invalid_utf8_format_does_not_panic() { + use std::ffi::OsStr; + use std::os::unix::ffi::OsStrExt; + + ucmd() + .args(&["./test_data/simple", "-maxdepth", "0"]) + .arg("-printf") + .arg(OsStr::from_bytes(b"%\xff|\n")) + .fails() + .code_is(1) + .stderr_contains("invalid UTF-8"); +} + #[test] fn find_printf_width_too_large() { ucmd() From 4dabff78398c5ba2fb5173f8401edccdc8246d8d Mon Sep 17 00:00:00 2001 From: MsfPablo Date: Sun, 9 Aug 2026 19:56:24 +0200 Subject: [PATCH 2/2] find: warn-and-continue on invalid-UTF-8 args (GNU parity) Match GNU find, which treats an invalid-UTF-8 argument as a warning and continues with a lossy conversion (exit 0) rather than aborting with exit 1. Replaces the hard std::process::exit(1) in main with a lossy conversion that preserves the existing warning. Lossy-converting the -printf format exposes a pre-existing panic in FormatStringParser::advance_one: it byte-sliced self.string[1..] to drop the just-read character, which panics whenever that character is multibyte (a lossy replacement char from invalid UTF-8, or any non-ASCII char following a %-directive). Slice by c.len_utf8() instead so one character is dropped. Test now expects success (exit 0) + the warning, matching GNU. --- src/find/main.rs | 12 ++++++++---- src/find/matchers/printf.rs | 6 +++++- tests/test_find.rs | 6 ++++-- 3 files changed, 17 insertions(+), 7 deletions(-) diff --git a/src/find/main.rs b/src/find/main.rs index 8a8a80c5..26b4b1de 100644 --- a/src/find/main.rs +++ b/src/find/main.rs @@ -11,14 +11,18 @@ fn main() { uucore::panic::mute_sigpipe_panic(); let args = std::env::args_os() - .map(|arg| { - arg.into_string().unwrap_or_else(|invalid| { + .map(|arg| match arg.into_string() { + Ok(s) => s, + // GNU find treats an invalid-UTF-8 argument as a warning and + // continues with a lossy conversion (exiting 0), rather than + // aborting — so do the same instead of hard-erroring with exit 1. + Err(invalid) => { eprintln!( "find: invalid UTF-8 was found in one of the arguments: {}", invalid.to_string_lossy() ); - std::process::exit(1); - }) + invalid.to_string_lossy().into_owned() + } }) .collect::>(); let strs: Vec<&str> = args.iter().map(std::convert::AsRef::as_ref).collect(); diff --git a/src/find/matchers/printf.rs b/src/find/matchers/printf.rs index 31151db2..5ad13fea 100644 --- a/src/find/matchers/printf.rs +++ b/src/find/matchers/printf.rs @@ -152,7 +152,11 @@ impl FormatStringParser<'_> { fn advance_one(&mut self) -> Result> { let c = self.front()?; - self.string = &self.string[1..]; + // Slice off one *character*, not one byte: byte slicing `[1..]` panics + // when the next character is multibyte (e.g. a lossy-converted + // replacement char from an invalid-UTF-8 argument, or any non-ASCII + // character following a `%` directive). + self.string = &self.string[c.len_utf8()..]; Ok(c) } diff --git a/tests/test_find.rs b/tests/test_find.rs index 26e207a6..4f43062f 100644 --- a/tests/test_find.rs +++ b/tests/test_find.rs @@ -584,12 +584,14 @@ fn find_printf_invalid_utf8_format_does_not_panic() { use std::ffi::OsStr; use std::os::unix::ffi::OsStrExt; + // GNU find treats an invalid-UTF-8 argument as a warning and continues + // (exit 0) with a lossy conversion, so the run succeeds and warns on + // stderr rather than failing with exit 1. ucmd() .args(&["./test_data/simple", "-maxdepth", "0"]) .arg("-printf") .arg(OsStr::from_bytes(b"%\xff|\n")) - .fails() - .code_is(1) + .succeeds() .stderr_contains("invalid UTF-8"); }