From de9ab5370e93ef1e274bb175aa46585b6d99a9ae Mon Sep 17 00:00:00 2001 From: KenM76 Date: Tue, 27 Oct 2026 05:03:47 -0400 Subject: [PATCH] fix(xfa): leave a 16-byte BMP header to the decoder; test core headers and a huge colour count Review follow-up: the repair reads the 40-byte header's compression and colour count, which a 16-byte OS/2 header does not have. New cases pin a 12-byte core header (3-byte palette entries) and a biClrUsed of u32::MAX. Co-Authored-By: Claude Opus 5.5 Claude-Session: https://claude.ai/code/session_01SqmHYx8FiWM5PcsoiErgp9 --- crates/xfa/src/image.rs | 3 +++ crates/xfa/src/tests.rs | 17 +++++++++++++++++ 2 files changed, 20 insertions(+) diff --git a/crates/xfa/src/image.rs b/crates/xfa/src/image.rs index 8f1953bedd..a284b444a8 100644 --- a/crates/xfa/src/image.rs +++ b/crates/xfa/src/image.rs @@ -74,6 +74,9 @@ fn bmp_pixel_offset_repair(data: &[u8]) -> Option { let (bpp, masks, used, entry) = if info_size == 12 { // BITMAPCOREHEADER: 16-bit fields, no colour count, 3-byte table entries. (le16(24)?, 0, 0, 3) + } else if info_size < 40 { + // OS/2's 16-byte header ends before the colour count; leave it to the decoder. + return None; } else { // BI_BITFIELDS and BI_ALPHABITFIELDS put their masks after a 40-byte header; the larger // headers hold them inside. diff --git a/crates/xfa/src/tests.rs b/crates/xfa/src/tests.rs index f8c0c6dd78..8926c12b2a 100644 --- a/crates/xfa/src/tests.rs +++ b/crates/xfa/src/tests.rs @@ -1825,6 +1825,23 @@ fn a_bmp_whose_pixel_offset_points_into_its_headers_is_read_from_after_them() { fields[10..14].copy_from_slice(&0u32.to_le_bytes()); fields.splice(54..54, [0u8; 12]); assert_eq!(bmp_with_pixel_offset(&fields).get(10..14), Some(&66u32.to_le_bytes()[..])); + // A colour count no file could hold: left alone. + let mut huge = tiny_bmp(true); + huge[46..50].copy_from_slice(&u32::MAX.to_le_bytes()); + huge[10..14].copy_from_slice(&0u32.to_le_bytes()); + assert_eq!(bmp_with_pixel_offset(&huge).as_ref(), &huge[..]); + // A 12-byte core header: 16-bit fields and 3-byte palette entries, `1 << bpp` of them. + let mut core = b"BM".to_vec(); + core.extend_from_slice(&[0; 8]); + core.extend_from_slice(&0u32.to_le_bytes()); + core.extend_from_slice(&12u32.to_le_bytes()); + for v in [2u16, 1, 1, 1] { + core.extend_from_slice(&v.to_le_bytes()); + } + core.extend_from_slice(&[0, 0, 255, 255, 0, 0, 0b0100_0000, 0, 0, 0]); + assert_eq!(bmp_with_pixel_offset(&core).get(10..14), Some(&32u32.to_le_bytes()[..])); + let d = decode(&bmp_with_pixel_offset(&core)).unwrap(); + assert_eq!(d.samples, vec![255, 0, 0, 0, 0, 255], "core header"); } /// Designer forms often carry BMP and TIFF pictures, declared as `image/bmp` and `image/tif`