diff --git a/vendor/README.craft-reader.md b/vendor/README.craft-reader.md index 704ce25d..e7ab08cf 100644 --- a/vendor/README.craft-reader.md +++ b/vendor/README.craft-reader.md @@ -36,6 +36,12 @@ Byte transfer-function samples use rounded 8-bit values; failed or non-finite ev leave the sample unchanged. Historical source hashes remain in each `SOURCE.json`, alongside local hashes and the compatibility reference. +The JBIG2 segment parser caps the long-form referred-to-segment count at `u16::MAX` +and rejects a larger claim as an invalid count before any retention-flag or +referred-to-number bytes are consumed, so a crafted header cannot turn +`Vec::with_capacity` into a multi-gigabyte reservation that aborts on allocation +failure. + This remains the patched 0.7 backend. It retains the existing Bradford Lab conversion, floating component precision and codec versions, rather than claiming full 0.8 equivalence. The independent color oracles, backend tests and facade contracts cover diff --git a/vendor/hayro-jbig2/SOURCE.json b/vendor/hayro-jbig2/SOURCE.json index 50ec8937..bb9d6a70 100644 --- a/vendor/hayro-jbig2/SOURCE.json +++ b/vendor/hayro-jbig2/SOURCE.json @@ -176,7 +176,7 @@ "path": "src/segment.rs", "source_sha256": "58d9fd05a15358b4ca945ffa3b8c1872f4ea57f8bbe50e8db3c6fce5bcac2914", "source_git_blob": "35c17c38af62b1fece4e280cda923cdcb23a8661", - "sha256": "58d9fd05a15358b4ca945ffa3b8c1872f4ea57f8bbe50e8db3c6fce5bcac2914" + "sha256": "c64e2845dbdd26e33b5461acde399ba4820f9942e9090a32782f44ff0fd5e61f" }, { "path": "src/simd.rs", diff --git a/vendor/hayro-jbig2/src/segment.rs b/vendor/hayro-jbig2/src/segment.rs index 35c17c38..1c6fcf31 100644 --- a/vendor/hayro-jbig2/src/segment.rs +++ b/vendor/hayro-jbig2/src/segment.rs @@ -8,6 +8,14 @@ use alloc::vec::Vec; use crate::error::{ParseError, Result, SegmentError, bail, err}; use crate::reader::Reader; +/// PdfCraft patch: the most segments one segment may refer to. A crafted long-form +/// header can claim a 29-bit count, which `Vec::with_capacity` below would turn into +/// a multi-gigabyte reservation before a single referred-to number is read — an +/// allocation failure then aborts the process in a way `catch_unwind` cannot contain. +/// Real files refer to a handful of segments, so `u16::MAX` is far beyond any +/// legitimate need. +pub(crate) const MAX_REFERRED_TO_SEGMENTS: u32 = u16::MAX as u32; + /// "The segment type is a number between 0 and 63, inclusive. Not all values /// are allowed." (7.3) #[derive(Debug, Clone, Copy, PartialEq, Eq)] @@ -183,6 +191,12 @@ pub(crate) fn parse_segment_header(reader: &mut Reader<'_>) -> Result MAX_REFERRED_TO_SEGMENTS { + bail!(SegmentError::InvalidReferredCount); + } + // Skip retention flag bytes in long form. // "The first one-byte field following the initial four-byte field is formatted // as follows: Bit 0: Retain bit for this segment. Bit 1-7: Retain bits for @@ -416,4 +430,27 @@ mod tests { assert_eq!(header.data_length, Some(32)); } + + #[test] + fn test_referred_to_count_is_capped() { + use crate::error::DecodeError; + + // A long-form header claiming the 29-bit maximum of 536,870,911 + // referred-to segments would otherwise make `Vec::with_capacity` + // reserve ~2 GiB before a single referred-to number is read. + // The first byte is 0xFF: long form, count bits 0x1F; the count is + // 0x1FFFFFFF. + let data = [ + 0x00, 0x01, 0x00, 0x00, // Segment number = 65536 + 0x86, // Flags: type 6, page assoc 1 byte, deferred non-retain + 0xFF, 0xFF, 0xFF, 0xFF, // Long form, count = 536,870,911 + ]; + + let mut reader = Reader::new(&data); + let error = parse_segment_header(&mut reader).unwrap_err(); + assert!(matches!( + error, + DecodeError::Segment(SegmentError::InvalidReferredCount) + )); + } }