Merge pull request #1565 from midasmatterhorn/fix/r4-smask-bpc-shift

pdf: a soft mask at an unsupported bit depth is ignored, not fatal
This commit is contained in:
BFlat
2026-10-16 19:05:12 +07:00
committed by GitHub
2 changed files with 27 additions and 1 deletions
+5
View File
@@ -184,6 +184,11 @@ fn mask_image(file: &File, o: &Obj, soft: bool) -> Option<(Vec<u8>, usize, usize
return Some((data.into_iter().step_by(jpeg_n.unwrap_or(1)).take(w * h).collect(), w, h));
}
let bpc = if soft { file.get_num(d, "BitsPerComponent").unwrap_or(8.0) as usize } else { 1 };
// Like `decode()`: only 1/2/4/8/16-bit depths are supported. Anything else (notably
// `bpc >= 32`, where `1u32 << bpc` below would overflow) means "no usable mask".
if !matches!(bpc, 1 | 2 | 4 | 8 | 16) {
return None;
}
let max = ((1u32 << bpc) - 1).max(1) as f64;
let dec = file.get(d, "Decode").map(|x| file.nums(x)).filter(|v| v.len() >= 2);
let v = unpack(&data, w, h, 1, bpc);
+22 -1
View File
@@ -274,11 +274,32 @@ fn inline_image_with_absurd_dimensions_falls_back_to_ei_scan() {
assert!(!doc.skipped.is_empty());
// A length just under `usize::MAX` passes the row math; past byte 4096 of the content the
// caller's `start + len` would then overflow.
let content = format!("{}q 10 0 0 10 0 0 cm BI /W 2305843009213693440 /H 8 /CS /G /BPC 8 ID EI Q", " ".repeat(5000));
let content = format!("{}q 10 0 0 10 0 0 cm BI /W 2305843009213693440 /H 8 /CS /G /BPC 8 ID \0 EI Q", " ".repeat(5000));
let doc = parse(&page_pdf(&content, "", vec![])).unwrap();
let _ = render(&doc);
}
#[test]
fn smask_with_absurd_bpc_is_ignored_not_fatal() {
// `/BitsPerComponent 32` in a soft mask: `1u32 << bpc` must neither panic (debug)
// nor mistranslate samples into alpha (release); the mask is treated as absent.
let bytes = page_pdf(
"q 2 0 0 2 0 0 cm /Im Do Q",
"/XObject << /Im 10 0 R >>",
vec![
(
10,
"<< /Type /XObject /Subtype /Image /Width 2 /Height 2 /ColorSpace /DeviceGray /BitsPerComponent 8 /SMask 11 0 R >>".into(),
Some(vec![0x80; 4]),
),
(11, "<< /Type /XObject /Subtype /Image /Width 2 /Height 2 /ColorSpace /DeviceGray /BitsPerComponent 32 >>".into(), Some(vec![0xFF; 16])),
],
);
let doc = parse(&bytes).unwrap();
let img = render(&doc);
assert!(coverage(&img, 0, 0, 200, 100) > 0.0, "image still draws, opaque");
}
#[test]
fn truncated_and_corrupt_eps_never_panic() {
let eps = b"%!PS-Adobe-3.0 EPSF-3.0\n%%BoundingBox: 0 0 100 100\n\