mirror of
https://github.com/storytold/pdfcraft.git
synced 2026-10-29 20:47:32 -06:00
Fuzzed inputs drove one check-one process to 2-6 GB (the 16 GB CI runner running two of them was killed every night). Three causes, each with a test: - lopdf (bootstrap inspector) sized PNG predictor rows from /Columns before reading data: /Columns 4294967295 allocated two 4 GiB rows. Vendored lopdf 0.45.0 refuses rows longer than the data (and uses saturating row widths); its load-time stream decoding also gets a 256 MiB limit (it had none). - hayro sized a tiling pattern's cell pixmap from /XStep x scale, bounded only by u16: a step far beyond /BBox allocated 2 GB. The vendored patch keeps the cell within 3000 px a side, lowering the resolution instead. - cos decoded cross-reference streams up to the general 1 GiB limit; they now stop at what 8,388,607 objects' rows can hold (~200 MB at most), and the section is reconstructed (logged) instead. On the nightly's seed (41) the worst captured input fell from 6.2 GB to 6 MB, and a 300 s fuzz run had no process above 300 MB (before: many at 2-5 GB). Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
47 lines
1.2 KiB
Rust
47 lines
1.2 KiB
Rust
use super::Object;
|
|
use crate::Result;
|
|
use crate::writer::Writer;
|
|
use std::io::Write;
|
|
|
|
#[derive(Debug, Clone)]
|
|
pub struct Operation {
|
|
pub operator: String,
|
|
pub operands: Vec<Object>,
|
|
}
|
|
|
|
impl Operation {
|
|
pub fn new(operator: &str, operands: Vec<Object>) -> Operation {
|
|
Operation {
|
|
operator: operator.to_string(),
|
|
operands,
|
|
}
|
|
}
|
|
}
|
|
|
|
#[derive(Debug, Clone)]
|
|
pub struct Content<Operations: AsRef<[Operation]> = Vec<Operation>> {
|
|
pub operations: Operations,
|
|
}
|
|
|
|
impl<Operations: AsRef<[Operation]>> Content<Operations> {
|
|
/// Encode content operations.
|
|
pub fn encode(&self) -> Result<Vec<u8>> {
|
|
let mut buffer = Vec::new();
|
|
let mut first_operation = true;
|
|
for operation in self.operations.as_ref() {
|
|
// Add new line after each operation except the last one.
|
|
if first_operation {
|
|
first_operation = false;
|
|
} else {
|
|
buffer.write_all(b"\n")?;
|
|
}
|
|
for operand in &operation.operands {
|
|
Writer::write_object(&mut buffer, operand)?;
|
|
buffer.write_all(b" ")?;
|
|
}
|
|
buffer.write_all(operation.operator.as_bytes())?;
|
|
}
|
|
Ok(buffer)
|
|
}
|
|
}
|