From aee5671734a34bd178ff9180f905b429cbe3eceb Mon Sep 17 00:00:00 2001 From: dotCooCoo Date: Wed, 4 Nov 2026 07:35:05 -0800 Subject: [PATCH] File formats: Pixar tile pointers into the header or tile table are refused A nonzero tile pointer below the end of the tile pointer table made the reader decode header or table bytes as pixels. The table end is now computed with checked arithmetic, and such a pointer is refused as damaged image data. --- crates/engine/src/cmd/fileio/pxrread.rs | 13 ++++++++++--- crates/engine/src/cmd/fileio/tests_pxrread.rs | 14 ++++++++++++++ 2 files changed, 24 insertions(+), 3 deletions(-) diff --git a/crates/engine/src/cmd/fileio/pxrread.rs b/crates/engine/src/cmd/fileio/pxrread.rs index eb7957ad9..fc3a32f15 100644 --- a/crates/engine/src/cmd/fileio/pxrread.rs +++ b/crates/engine/src/cmd/fileio/pxrread.rs @@ -24,8 +24,8 @@ const TRUNCATED: &str = "the Pixar file is cut short"; /// define. const DAMAGED: &str = "the Pixar file's header is damaged"; -/// The message for encoded tile data with a packet the format doesn't define, or one that runs -/// past the end of its scan line. +/// The message for a tile pointer into the header or the tile pointer table, and for encoded tile +/// data with a packet the format doesn't define, or one that runs past the end of its scan line. const BAD_DATA: &str = "the Pixar file's image data is damaged"; /// The channel bits of the picture format field (header byte 424): R, G, B and A, stored in this @@ -115,7 +115,9 @@ pub(super) fn decode(bytes: &[u8], max_alloc: u64) -> Result Result= bytes.len() { return Err(TRUNCATED.into()); } + // Tile data follows the tile pointer table; a pointer into the header or the table would + // read them as pixels. + if pointer < table_end { + return Err(BAD_DATA.into()); + } let (x0, y0) = (n % across * tile_w, n / across * tile_h); // The part of the tile inside the picture; the pixels of border tiles past its edges are // stored and skipped. diff --git a/crates/engine/src/cmd/fileio/tests_pxrread.rs b/crates/engine/src/cmd/fileio/tests_pxrread.rs index 0562aacbb..f9e89e38f 100644 --- a/crates/engine/src/cmd/fileio/tests_pxrread.rs +++ b/crates/engine/src/cmd/fileio/tests_pxrread.rs @@ -361,3 +361,17 @@ fn damaged_files_are_refused_with_a_message() { assert!(!is_pxr(&bad), "{bad:?}"); } } + +#[test] +fn tile_pointers_into_the_header_or_table_are_refused() { + // Tile data follows the tile pointer table, which ends at byte 520 in a one-tile picture. + let rgb = one(head(2, 1, RGB, 2), &[1, 2, 3, 4, 5, 6]); + for pointer in [4u32, 511, 512, 519] { + let mut inside = rgb.clone(); + inside[512..516].copy_from_slice(&pointer.to_le_bytes()); + let e = raster_image(&inside).err().expect("refused").to_string(); + assert_eq!(e, "the Pixar file's image data is damaged", "a pointer to byte {pointer}"); + } + // A pointer at the table's end reads. + assert_eq!(pixels(&rgb), [[1, 2, 3, 255], [4, 5, 6, 255]]); +}