fix(create): decode an indexed lookup table within a small cap

Image export decoded a stream lookup table under the general 1 GiB
stream limit although a table holds at most 1024 bytes, and every image
sharing the palette decoded it again. The table now decodes within
64 KiB and a larger one is refused for that image.
This commit is contained in:
aiongg
2026-10-22 17:08:54 +00:00
parent 1695106f98
commit 83d8eb0856
2 changed files with 15 additions and 1 deletions
+6 -1
View File
@@ -359,6 +359,9 @@ impl Space {
}
}
/// A lookup table holds at most 256 entries of 4 bytes; decoding stops well past that.
const MAX_LOOKUP: usize = 64 << 10;
fn space(doc: &Document, cs: Option<&Object>) -> Result<Space, String> {
let Some(cs) = cs else { return Err("the image has no colour space".into()) };
let cs = doc.resolve(cs);
@@ -393,7 +396,9 @@ fn space(doc: &Document, cs: Option<&Object>) -> Result<Space, String> {
};
let table = match a.get(3).map(|t| doc.resolve(t)).as_deref() {
Some(Object::String(s)) => s.bytes.clone(),
Some(Object::Stream(t)) => t.decoded().map_err(|e| e.to_string())?,
Some(Object::Stream(t)) => {
t.decoded_within(MAX_LOOKUP).map_err(|_| "an indexed colour space with an oversized or damaged lookup table".to_string())?
}
_ => return Err("an indexed colour space without a lookup table".into()),
};
Ok(Space::Indexed(Box::new(base), hival, table))
+9
View File
@@ -832,6 +832,15 @@ fn indexed_hival_resolves_indirect_values_and_stream_palettes() {
}
}
#[test]
fn indexed_lookup_stream_has_a_size_cap() {
// A lookup table needs 256 × 4 bytes at most; a 1 MiB one is refused, not decoded whole.
let mut palette = vec![255, 0, 0, 0, 0, 255];
palette.resize(1 << 20, 0);
let (doc, image) = indexed_export_fixture(Object::Int(1), palette, true, false, 8, vec![0, 1, 0, 1]);
assert!(crate::extract::image_file(&doc, image).unwrap_err().contains("lookup table"));
}
#[test]
fn indexed_hival_boundary_values_keep_valid_palette_entries() {
let palette: Vec<u8> = (0..=255u8).flat_map(|i| [i, 255 - i, 17]).collect();