Merge pull request #3822 from Silvertree2010/fix/3593-audit-eps-import-gstate-currentg

Charge gstate and currentgstate path copies to the memory budget
This commit is contained in:
BFlat
2026-10-24 02:00:40 +07:00
committed by GitHub
2 changed files with 14 additions and 3 deletions
+2
View File
@@ -1330,11 +1330,13 @@ impl Interp<'_> {
}
GStateNew => {
self.spend_elements(self.g.path.elements().len())?;
self.alloc(std::mem::size_of_val(self.g.path.elements()))?;
self.push(Obj::GState(Rc::new(self.g.clone())))?;
}
CurrentGState => {
self.pop()?;
self.spend_elements(self.g.path.elements().len())?;
self.alloc(std::mem::size_of_val(self.g.path.elements()))?;
self.push(Obj::GState(Rc::new(self.g.clone())))?;
}
SetGState => match self.pop()? {
+12 -3
View File
@@ -899,7 +899,7 @@ fn paths_made_in_a_loop_run_out_of_the_budget() {
/// A loop of operators that read or copy the whole current path or clip path runs out of the
/// operation budget (PLRM 3rd ed., chapter 8): `pathbbox`, `clip`, `clippath`, `shfill` (which
/// reads the clip path for the area it paints), `pathforall` (which copies the path before it runs
/// a procedure), `gstate`, `currentgstate` and `setgstate`.
/// a procedure) and `setgstate`. `gstate` and `currentgstate` run out of memory first.
#[test]
fn paths_read_or_copied_in_a_loop_run_out_of_the_budget() {
// 10,001 elements reaching 10,000,000 points to the right: what `shfill` paints in a clip of
@@ -913,8 +913,6 @@ fn paths_read_or_copied_in_a_loop_run_out_of_the_budget() {
"clip newpath { clippath } loop",
"clip newpath { sh shfill } loop",
"{ { pop pop exit } { } { } { } pathforall } loop",
"{ gstate pop } loop",
"/g gstate def { g currentgstate pop } loop",
"/g gstate def { g setgstate } loop",
] {
runs_out_of_budget(&format!("{sh} {path} {body}"));
@@ -1162,3 +1160,14 @@ fn an_oversized_image_plane_is_refused() {
.unwrap_err();
assert!(e.contains("too large"), "{e}");
}
/// Graphics states kept with `gstate` or `currentgstate` each hold a copy of the current path, and
/// the copies count against the memory budget as `gsave` does.
#[test]
fn stored_graphics_states_use_up_the_memory_budget() {
let path = "newpath 0 0 moveto 0 1 1000000 { pop 1 0 rlineto } for";
for body in ["30 { gstate } repeat", "30 { 0 currentgstate } repeat"] {
let r = read_soon(&format!("0 0 10 10 rectfill {path} {body}"));
assert!(r.warnings.iter().any(|w| w.contains("uses too much memory")), "{body}: {:?}", r.warnings);
}
}