From bc165c0ef848a44448a5a68a97785a3bb7f58c34 Mon Sep 17 00:00:00 2001 From: mistaste Date: Mon, 15 Jun 2026 01:20:34 +0300 Subject: [PATCH] od: don't abort on a huge --width `od -w=1162652562662412622` aborted the process while allocating the line buffer: $ od -w=1162652562662412622 a memory allocation of 1162652562662412626 bytes failed Aborted (core dumped) `InputDecoder::new` now computes the buffer size with a checked add and reserves it with `try_reserve_exact`, returning an error instead of aborting. An out-of-range width is reported cleanly: $ od -w=1162652562662412622 a od: memory exhausted Fixes #12839 --- src/uu/od/src/input_decoder.rs | 21 +++++++++++++++------ src/uu/od/src/od.rs | 3 ++- tests/by-util/test_od.rs | 20 ++++++++++++++++++++ 3 files changed, 37 insertions(+), 7 deletions(-) diff --git a/src/uu/od/src/input_decoder.rs b/src/uu/od/src/input_decoder.rs index 416badb444b..23b5c5c2c91 100644 --- a/src/uu/od/src/input_decoder.rs +++ b/src/uu/od/src/input_decoder.rs @@ -44,17 +44,26 @@ impl InputDecoder<'_, I> { normal_length: usize, peek_length: usize, byte_order: ByteOrder, - ) -> InputDecoder<'_, I> { - let bytes = vec![0; normal_length + peek_length]; + ) -> io::Result> { + // A huge `--width` would otherwise abort the process while allocating + // the line buffer. Compute the size with a checked add and reserve it + // fallibly so an out-of-range width yields a clean error instead. + let size = normal_length + .checked_add(peek_length) + .ok_or_else(|| io::Error::new(io::ErrorKind::OutOfMemory, "memory exhausted"))?; + let mut data: Vec = Vec::new(); + data.try_reserve_exact(size) + .map_err(|_| io::Error::new(io::ErrorKind::OutOfMemory, "memory exhausted"))?; + data.resize(size, 0); - InputDecoder { + Ok(InputDecoder { input, - data: bytes, + data, reserved_peek_length: peek_length, used_normal_length: 0, used_peek_length: 0, byte_order, - } + }) } } @@ -235,7 +244,7 @@ mod tests { fn smoke_test() { let data = [0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xC0, 0xff, 0xff]; let mut input = PeekReader::new(Cursor::new(&data)); - let mut sut = InputDecoder::new(&mut input, 8, 2, ByteOrder::Little); + let mut sut = InputDecoder::new(&mut input, 8, 2, ByteOrder::Little).unwrap(); // Peek normal length let mut mem = sut.peek_read().unwrap(); diff --git a/src/uu/od/src/od.rs b/src/uu/od/src/od.rs index 0db92ff7bc6..1ac0606b6d9 100644 --- a/src/uu/od/src/od.rs +++ b/src/uu/od/src/od.rs @@ -269,7 +269,8 @@ pub fn uumain(args: impl uucore::Args) -> UResult<()> { od_options.line_bytes, PEEK_BUFFER_SIZE, od_options.byte_order, - ); + ) + .map_err(|e| USimpleError::new(1, e.to_string()))?; let output_info = OutputInfo::new( od_options.line_bytes, diff --git a/tests/by-util/test_od.rs b/tests/by-util/test_od.rs index bc601318606..9db2800ca53 100644 --- a/tests/by-util/test_od.rs +++ b/tests/by-util/test_od.rs @@ -488,6 +488,26 @@ fn test_non_numeric_width_argument() { .stderr_only("od: invalid -w argument 'w'\n"); } +#[test] +fn test_huge_width_argument() { + // A huge width must produce a clean error instead of aborting the process + // while allocating the line buffer. Regression test for issue #12839. + new_ucmd!() + .arg("-w1162652562662412622") + .arg("-An") + .pipe_in("") + .fails_with_code(1) + .stderr_contains("memory exhausted"); + + // A width that does not even fit in `usize` is rejected as too large. + new_ucmd!() + .arg("-w99999999999999999999999") + .arg("-An") + .pipe_in("") + .fails_with_code(1) + .stderr_contains("too large"); +} + #[test] fn test_width_without_value() { let input: [u8; 40] = [0; 40];