security: fallible bitmap allocation in vendored hayro-jbig2 (#242)

* security: fallible bitmap allocation in vendored hayro-jbig2 (follow-up to #239)

* security: reinitialize allocates before mutating the jbig2 bitmap (CodeRabbit)
This commit is contained in:
Dylan M. Taylor authored and GitHub committed 2026-10-23 11:16:19 -04:00
1 parent 35d18945a9
commit 9def84d7a5
3 files changed
+29 -7

No files matched your search

+2 -1
View File
@@ -50,7 +50,8 @@ Hostile-input bounds (CodeRabbit findings):
- Each object stream's `/N` header pairs are parsed once per document and cached beside its
decoded data, instead of on every compressed-object lookup.
- An embedded JBIG2 page over 2^28 pixels is refused before the output buffer is allocated,
and that buffer is reserved fallibly. A `JBIG2Globals` stream with an image filter is
and that buffer is reserved fallibly (`Bitmap::reinitialize` allocates before touching the
existing bitmap, so a failed reservation leaves it intact). A `JBIG2Globals` stream with an image filter is
ignored, so globals cannot recurse through further globals.
- At most 4096 graphics states, holding at most 64 MiB together, are saved; a `q` past
either limit is counted but not copied, and its `Q` still removes the clips added since.
+1 -1
View File
@@ -62,7 +62,7 @@
"path": "src/bitmap.rs",
"source_sha256": "59ed83d8bac6c39e40270a9856ca3faee628de7d9efae1b01f714e140e2b6b7e",
"source_git_blob": "35ed97561597d30a70fe819cf8cfbd3780538f75",
"sha256": "59ed83d8bac6c39e40270a9856ca3faee628de7d9efae1b01f714e140e2b6b7e"
"sha256": "99dddbef5f7b36c55d0e914d07664326598919eb6d54cdfa93f300076a8ae69d"
},
{
"path": "src/decode/generic.rs",
+26 -5
View File
@@ -7,7 +7,6 @@
use crate::decode::CombinationOperator;
use crate::error::{OverflowError, Result, bail};
use alloc::vec;
use alloc::vec::Vec;
// To guard better against malicious files, we limit bitmap dimensions
@@ -50,6 +49,14 @@ pub(crate) struct Bitmap {
pub(crate) y_location: u32,
}
/// PdfCraft patch: fallible replacement for `vec![word; len]` / `resize`.
fn fill_words(data: &mut Vec<Word>, len: usize, word: Word) -> Result<()> {
data.try_reserve_exact(len)
.map_err(|_| OverflowError::BitmapDimension)?;
data.resize(len, word);
Ok(())
}
impl Bitmap {
/// Create a new bitmap filled with white pixels (false).
///
@@ -76,7 +83,9 @@ impl Bitmap {
let stride = width.div_ceil(WORD_BITS);
let default_word: Word = if default_pixel { !0 } else { 0 };
let data = vec![default_word; stride as usize * height as usize];
// PdfCraft patch: reserve fallibly so hostile dimensions error instead of aborting.
let mut data = Vec::new();
fill_words(&mut data, stride as usize * height as usize, default_word)?;
Ok(Self {
width,
height,
@@ -103,14 +112,15 @@ impl Bitmap {
let stride = width.div_ceil(WORD_BITS);
let default_word: Word = if default_pixel { !0 } else { 0 };
// Allocate first so a failed reservation leaves the bitmap untouched.
let mut data = Vec::new();
fill_words(&mut data, stride as usize * height as usize, default_word)?;
self.width = width;
self.height = height;
self.stride = stride;
self.x_location = 0;
self.y_location = 0;
self.data.clear();
self.data
.resize(stride as usize * height as usize, default_word);
self.data = data;
Ok(())
}
@@ -225,3 +235,14 @@ impl Bitmap {
self.data[(row * self.stride + word_idx) as usize]
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn failed_reservation_is_an_error() {
let mut data = Vec::new();
assert!(fill_words(&mut data, usize::MAX / 2, 0).is_err());
}
}