From 1b4220391e535be3953ba3dfb46eea62d385082c Mon Sep 17 00:00:00 2001 From: harshasiddartha <147021873+harshasiddartha@users.noreply.github.com> Date: Thu, 3 Sep 2026 23:31:47 +0530 Subject: [PATCH] stat: fix overflow on out-of-range octal escape A \NNN escape whose value exceeds 255 (\400 through \777) was accumulated in a u8, so the multiply overflowed and a build with overflow-checks panicked with exit code 101. Accumulate the escape in a u32 and keep the low byte, so \400 prints 0x00 and \777 prints 0xFF, matching GNU. --- src/uu/stat/src/stat.rs | 9 ++++++--- tests/by-util/test_stat.rs | 11 +++++++++++ 2 files changed, 17 insertions(+), 3 deletions(-) diff --git a/src/uu/stat/src/stat.rs b/src/uu/stat/src/stat.rs index 45817649c73..bf53ad23f10 100644 --- a/src/uu/stat/src/stat.rs +++ b/src/uu/stat/src/stat.rs @@ -943,11 +943,14 @@ impl Stater { '"' => Token::Byte(b'"'), // Double quote '0'..='7' => { // Parse octal escape sequence (up to 3 digits) - let mut value = 0u8; + // Accumulate in a wider type: three octal digits can reach 511, + // and only the low byte is kept, which is what GNU prints for + // an out-of-range escape such as `\400`. + let mut value = 0u32; let mut count = 0; while *i < bound && count < 3 { if let Some(digit) = chars[*i].to_digit(8) { - value = value * 8 + digit as u8; + value = value * 8 + digit; *i += 1; count += 1; } else { @@ -955,7 +958,7 @@ impl Stater { } } *i -= 1; // Adjust index to account for the outer loop increment - Token::Byte(value) + Token::Byte(value as u8) } 'x' => { // Parse hexadecimal escape sequence (\xNN format) diff --git a/tests/by-util/test_stat.rs b/tests/by-util/test_stat.rs index ab51bcab5c7..0fdb5974d0e 100644 --- a/tests/by-util/test_stat.rs +++ b/tests/by-util/test_stat.rs @@ -621,6 +621,17 @@ fn test_printf_octal_2() { .stdout_is_bytes(expected_stdout); } +#[test] +fn test_printf_octal_out_of_range() { + // Octal escapes whose value exceeds 255 wrap around, as they do in GNU stat. + let ts = TestScenario::new(util_name!()); + let expected_stdout = vec![0x00, 0xFF]; // \400 -> 256 & 0xFF, \777 -> 511 & 0xFF + ts.ucmd() + .args(&["--printf=\\400\\777", "."]) + .succeeds() + .stdout_is_bytes(expected_stdout); +} + #[test] fn test_printf_incomplete_hex() { let ts = TestScenario::new(util_name!());