From 2c6b3b5392061fee165d308049b46647df0fccb8 Mon Sep 17 00:00:00 2001 From: aiongg <67543387+aiongg@users.noreply.github.com> Date: Fri, 23 Oct 2026 22:32:25 -0400 Subject: [PATCH] hayro-interpret: compute the largest 32-bit Type 0 sample in 64 bits (#257) PdfCraft ledger hayro-interpret (31), its overflow part: a Type 0 function with /BitsPerSample 32 computed 2_u32.pow(32), which overflows (a panic with overflow checks on). The maximum is now ((1u64 << bps) - 1) as f32. The flat sample array that (31) also brings is a performance change and is not part of this commit. Test: function::type0::tests::thirty_two_bit_samples_do_not_overflow. PdfCraft's other reader limits are already covered here: ASCII85 output is bounded by the decode limit (#253), object-stream indexes are parsed once with checked offsets, and direct objects nest at most 64 levels (PdfCraft: 128). --- vendor/README.craft-reader.md | 9 ++++++ vendor/hayro-interpret/SOURCE.json | 2 +- vendor/hayro-interpret/src/function/type0.rs | 33 ++++++++++++++------ 3 files changed, 34 insertions(+), 10 deletions(-) diff --git a/vendor/README.craft-reader.md b/vendor/README.craft-reader.md index 22e1d691..2d164a45 100644 --- a/vendor/README.craft-reader.md +++ b/vendor/README.craft-reader.md @@ -71,6 +71,15 @@ Hostile-input bounds (CodeRabbit findings): - A CID font's `/W2` run that starts past CID 65535 is skipped as in `/W`, instead of overflowing and discarding the whole table. +Reader limits from the [PdfCraft revision a2ec27b4775f53c40cad3456936d9406611e3a8d ledger](https://github.com/storytold/pdfcraft/blob/a2ec27b4775f53c40cad3456936d9406611e3a8d/vendor/README.md), +by its item ids: hayro-interpret (31), its overflow part: the largest 32-bit Type 0 sample is +computed in 64 bits instead of overflowing `2_u32.pow(32)`. The flat sample array that (31) +also brings is a performance change and is not part of this set. hayro-syntax (7), (22) and +(25) are covered by the local bounds above: ASCII85 output stops at the decode limit, +object-stream indexes are parsed once with checked offsets, and direct objects nest at most +64 levels, stricter than PdfCraft's 128. Test: +`function::type0::tests::thirty_two_bit_samples_do_not_overflow`. + 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-interpret/SOURCE.json b/vendor/hayro-interpret/SOURCE.json index af830f76..1284af03 100644 --- a/vendor/hayro-interpret/SOURCE.json +++ b/vendor/hayro-interpret/SOURCE.json @@ -332,7 +332,7 @@ "path": "src/function/type0.rs", "source_sha256": "84cb6ccb45536ca5731394558883350f43b5e66330e07eb8d5a0e070c8e20704", "source_git_blob": "02bbc41b84caddc9b5dfc8f49b2da081eb281199", - "sha256": "84cb6ccb45536ca5731394558883350f43b5e66330e07eb8d5a0e070c8e20704" + "sha256": "89aab3f20d8c3c30629dbccc18ce099a39eff7e4a88b4ef4685fc0415008c233" }, { "path": "src/function/type2.rs", diff --git a/vendor/hayro-interpret/src/function/type0.rs b/vendor/hayro-interpret/src/function/type0.rs index 02bbc41b..a5506aaf 100644 --- a/vendor/hayro-interpret/src/function/type0.rs +++ b/vendor/hayro-interpret/src/function/type0.rs @@ -133,18 +133,12 @@ impl Type0 { let interpolated = interpolator.interpolate(&self.table)?; + // PdfCraft patch (31): `2_u32.pow(32)` overflowed (a panic with overflow checks on). + let max_sample = ((1u64 << self.bits_per_sample) - 1) as f32; let mut out = interpolated .iter() .zip(self.decode.iter()) - .map(|(x, decode)| { - interpolate( - *x, - 0.0, - (2_u32.pow(self.bits_per_sample as u32) - 1) as f32, - decode.0, - decode.1, - ) - }) + .map(|(x, decode)| interpolate(*x, 0.0, max_sample, decode.0, decode.1)) .collect::>(); self.clamper.clamp_output(&mut out); @@ -325,3 +319,24 @@ impl Key { Some(()) } } + +#[cfg(test)] +mod tests { + use crate::function::Function; + use hayro_syntax::object::{FromBytes, Object}; + use smallvec::smallvec; + + /// PdfCraft patch (31): 32-bit samples scale by `2^32 - 1` without overflowing (adapted from + /// PdfCraft's `raster::tests::sampled_functions_interpolate_their_grid_as_before`). + #[test] + fn thirty_two_bit_samples_do_not_overflow() { + let mut data = b"<< /FunctionType 0 /Domain [0 1] /Range [0 1] /Size [2] /BitsPerSample 32 /Length 8 >>\nstream\n".to_vec(); + data.extend_from_slice(&[0, 0, 0, 0, 0xff, 0xff, 0xff, 0xff]); + data.extend_from_slice(b"\nendstream"); + + let object = Object::from_bytes(&data).unwrap(); + let function = Function::new(&object).unwrap(); + assert_eq!(function.eval(smallvec![0.0]).unwrap().as_slice(), &[0.0]); + assert_eq!(function.eval(smallvec![1.0]).unwrap().as_slice(), &[1.0]); + } +}