EPS import: bound and charge the filter chain over a pending file

This commit is contained in:
Dylan M. Taylor
2026-11-04 10:51:39 +07:00
committed by bflatastic
parent 38bac1114b
commit bb03081a70
2 changed files with 20 additions and 1 deletions
+12 -1
View File
@@ -7,6 +7,8 @@ use super::{
/// Most bytes one filter or image reads.
pub(super) const MAX_DATA: usize = 1 << 28;
/// The longest chain of filters over one pending file (real programs stack two or three).
const MAX_FILTER_CHAIN: usize = 64;
/// Most pixels an image may have.
const MAX_PIXELS: usize = 1 << 26;
@@ -294,7 +296,16 @@ impl Interp<'_> {
let pending = match &*f.borrow() {
Stream::Current => Some((vec![], None)),
Stream::Buf { .. } if running => Some((vec![], Some(f.clone()))),
Stream::Pending { chain, from } => Some((chain.clone(), from.clone())),
Stream::Pending { chain, from } => {
// Cloning the chain copies each filter's delimiter: bound and charge it.
if chain.len() >= MAX_FILTER_CHAIN {
return Err(PsError::Limit("too many filters over one file"));
}
let copied = chain.iter().map(|c| c.sub.1.len() + 1).sum::<usize>();
self.alloc(copied)?;
self.spend((copied / 256) as u64)?;
Some((chain.clone(), from.clone()))
}
Stream::Buf { .. } => None,
};
match pending {
+8
View File
@@ -391,6 +391,14 @@ fn work_on_the_longest_dash_pattern_ends_soon() {
}
}
#[test]
fn a_chain_of_filters_over_the_program_is_bounded() {
let r = read_soon("0 0 10 10 rectfill currentfile 100 { 0 (x) /SubFileDecode filter } repeat pop 20 20 10 10 rectfill");
assert!(r.warnings.iter().any(|w| w.contains("too many filters")), "{:?}", r.warnings);
let r = read_soon("0 0 10 10 rectfill currentfile 3 { 0 (x) /SubFileDecode filter } repeat pop 20 20 10 10 rectfill");
assert!(r.warnings.is_empty(), "{:?}", r.warnings);
}
#[test]
fn procedures_bind_def_loops_and_dictionaries() {
let r = read(