summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorChris Liddell <chris.liddell@artifex.com>2018-09-05 17:14:59 +0100
committerChris Liddell <chris.liddell@artifex.com>2018-09-06 17:14:28 +0100
commit3e5d316b72e3965b7968bb1d96baa137cd063ac6 (patch)
treeba1fefe52b79de5b25a4b3529bb2526c8f3ec7db
parentc8c01f8c4164bc10281d9e8f87cf96314d93104b (diff)
Bug 699718: Ensure stack space is available before gsrestore call out
During a grestore, if the device is going to change, we call out to Postscript to restore the device configuration, before returning to restore the graphics state internally. We have to ensure sufficient op stack space is available to complete the operation, otherwise the device can end up an undefined state.
-rw-r--r--Resource/Init/gs_setpd.ps20
-rw-r--r--psi/zdevice2.c55
2 files changed, 56 insertions, 19 deletions
diff --git a/Resource/Init/gs_setpd.ps b/Resource/Init/gs_setpd.ps
index b75c4312a..8fa7c51df 100644
--- a/Resource/Init/gs_setpd.ps
+++ b/Resource/Init/gs_setpd.ps
@@ -96,7 +96,7 @@ level2dict begin
96 % we must (carefully) reinstall the old parameters in 96 % we must (carefully) reinstall the old parameters in
97 % the same device. 97 % the same device.
98 .currentpagedevice pop //null currentdevice //null 98 .currentpagedevice pop //null currentdevice //null
99 {.trysetparams} .internalstopped 99 { .trysetparams } .internalstopped
100 { 100 {
101 //null 101 //null
102 } if 102 } if
@@ -104,26 +104,32 @@ level2dict begin
104 { pop pop } 104 { pop pop }
105 { 105 {
106 SETPDDEBUG { (Error in .trysetparams!) = pstack flush } if 106 SETPDDEBUG { (Error in .trysetparams!) = pstack flush } if
107 cleartomark pop pop pop 107 {cleartomark pop pop pop} .internalstopped pop
108 % if resetting the entire device state failed, at least put back the 108 % if resetting the entire device state failed, at least put back the
109 % security related key 109 % security related key
110 currentdevice //null //false mark /.LockSafetyParams .currentpagedevice pop 110 currentdevice //null //false mark /.LockSafetyParams
111 /.LockSafetyParams .knownget not {//false} if .putdeviceparamsonly 111 currentpagedevice /.LockSafetyParams .knownget not
112 {systemdict /SAFER .knownget not {//false} } if
113 .putdeviceparamsonly
112 /.installpagedevice cvx /rangecheck signalerror 114 /.installpagedevice cvx /rangecheck signalerror
113 } 115 }
114 ifelse pop pop 116 ifelse pop pop
115 % A careful reading of the Red Book reveals that an erasepage 117 % A careful reading of the Red Book reveals that an erasepage
116 % should occur, but *not* an initgraphics. 118 % should occur, but *not* an initgraphics.
117 erasepage .beginpage 119 erasepage .beginpage
118 } bind def 120 } bind executeonly def
119 121
120/.uninstallpagedevice 122/.uninstallpagedevice
121 { 2 .endpage { .currentnumcopies //false .outputpage } if 123 {
124 {2 .endpage { .currentnumcopies //false .outputpage } if} .internalstopped pop
122 nulldevice 125 nulldevice
123 } bind def 126 } bind def
124 127
125(%grestorepagedevice) cvn 128(%grestorepagedevice) cvn
126 { .uninstallpagedevice grestore .installpagedevice 129 {
130 .uninstallpagedevice
131 grestore
132 .installpagedevice
127 } bind def 133 } bind def
128 134
129(%grestoreallpagedevice) cvn 135(%grestoreallpagedevice) cvn
diff --git a/psi/zdevice2.c b/psi/zdevice2.c
index 0c7080d57..5447c8c84 100644
--- a/psi/zdevice2.c
+++ b/psi/zdevice2.c
@@ -251,8 +251,8 @@ z2currentgstate(i_ctx_t *i_ctx_p)
251/* ------ Wrappers for operators that reset the graphics state. ------ */ 251/* ------ Wrappers for operators that reset the graphics state. ------ */
252 252
253/* Check whether we need to call out to restore the page device. */ 253/* Check whether we need to call out to restore the page device. */
254static bool 254static int
255restore_page_device(const gs_gstate * pgs_old, const gs_gstate * pgs_new) 255restore_page_device(i_ctx_t *i_ctx_p, const gs_gstate * pgs_old, const gs_gstate * pgs_new)
256{ 256{
257 gx_device *dev_old = gs_currentdevice(pgs_old); 257 gx_device *dev_old = gs_currentdevice(pgs_old);
258 gx_device *dev_new; 258 gx_device *dev_new;
@@ -260,9 +260,10 @@ restore_page_device(const gs_gstate * pgs_old, const gs_gstate * pgs_new)
260 gx_device *dev_t2; 260 gx_device *dev_t2;
261 bool samepagedevice = obj_eq(dev_old->memory, &gs_int_gstate(pgs_old)->pagedevice, 261 bool samepagedevice = obj_eq(dev_old->memory, &gs_int_gstate(pgs_old)->pagedevice,
262 &gs_int_gstate(pgs_new)->pagedevice); 262 &gs_int_gstate(pgs_new)->pagedevice);
263 bool LockSafetyParams = dev_old->LockSafetyParams;
263 264
264 if ((dev_t1 = (*dev_proc(dev_old, get_page_device)) (dev_old)) == 0) 265 if ((dev_t1 = (*dev_proc(dev_old, get_page_device)) (dev_old)) == 0)
265 return false; 266 return 0;
266 /* If we are going to putdeviceparams in a callout, we need to */ 267 /* If we are going to putdeviceparams in a callout, we need to */
267 /* unlock temporarily. The device will be re-locked as needed */ 268 /* unlock temporarily. The device will be re-locked as needed */
268 /* by putdeviceparams from the pgs_old->pagedevice dict state. */ 269 /* by putdeviceparams from the pgs_old->pagedevice dict state. */
@@ -271,23 +272,44 @@ restore_page_device(const gs_gstate * pgs_old, const gs_gstate * pgs_new)
271 dev_new = gs_currentdevice(pgs_new); 272 dev_new = gs_currentdevice(pgs_new);
272 if (dev_old != dev_new) { 273 if (dev_old != dev_new) {
273 if ((dev_t2 = (*dev_proc(dev_new, get_page_device)) (dev_new)) == 0) 274 if ((dev_t2 = (*dev_proc(dev_new, get_page_device)) (dev_new)) == 0)
274 return false; 275 samepagedevice = true;
275 if (dev_t1 != dev_t2) 276 else if (dev_t1 != dev_t2)
276 return true; 277 samepagedevice = false;
278 }
279
280 if (LockSafetyParams && !samepagedevice) {
281 os_ptr op = osp;
282 const int max_ops = 512;
283
284 /* The %grestorepagedevice must complete: the biggest danger
285 is operand stack overflow. As we use get/putdeviceparams
286 that means pushing all the device params onto the stack,
287 pdfwrite having by far the largest number of parameters
288 at (currently) 212 key/value pairs - thus needing (currently)
289 424 entries on the op stack. Allowing for working stack
290 space, and safety margin.....
291 */
292 if (max_ops > op - osbot) {
293 if (max_ops >= ref_stack_count(&o_stack))
294 return_error(gs_error_stackoverflow);
295 }
277 } 296 }
278 /* 297 /*
279 * The current implementation of setpagedevice just sets new 298 * The current implementation of setpagedevice just sets new
280 * parameters in the same device object, so we have to check 299 * parameters in the same device object, so we have to check
281 * whether the page device dictionaries are the same. 300 * whether the page device dictionaries are the same.
282 */ 301 */
283 return !samepagedevice; 302 return samepagedevice ? 0 : 1;
284} 303}
285 304
286/* - grestore - */ 305/* - grestore - */
287static int 306static int
288z2grestore(i_ctx_t *i_ctx_p) 307z2grestore(i_ctx_t *i_ctx_p)
289{ 308{
290 if (!restore_page_device(igs, gs_gstate_saved(igs))) 309 int code = restore_page_device(i_ctx_p, igs, gs_gstate_saved(igs));
310 if (code < 0) return code;
311
312 if (code == 0)
291 return gs_grestore(igs); 313 return gs_grestore(igs);
292 return push_callout(i_ctx_p, "%grestorepagedevice"); 314 return push_callout(i_ctx_p, "%grestorepagedevice");
293} 315}
@@ -297,7 +319,9 @@ static int
297z2grestoreall(i_ctx_t *i_ctx_p) 319z2grestoreall(i_ctx_t *i_ctx_p)
298{ 320{
299 for (;;) { 321 for (;;) {
300 if (!restore_page_device(igs, gs_gstate_saved(igs))) { 322 int code = restore_page_device(i_ctx_p, igs, gs_gstate_saved(igs));
323 if (code < 0) return code;
324 if (code == 0) {
301 bool done = !gs_gstate_saved(gs_gstate_saved(igs)); 325 bool done = !gs_gstate_saved(gs_gstate_saved(igs));
302 326
303 gs_grestore(igs); 327 gs_grestore(igs);
@@ -328,11 +352,15 @@ z2restore(i_ctx_t *i_ctx_p)
328 if (code < 0) return code; 352 if (code < 0) return code;
329 353
330 while (gs_gstate_saved(gs_gstate_saved(igs))) { 354 while (gs_gstate_saved(gs_gstate_saved(igs))) {
331 if (restore_page_device(igs, gs_gstate_saved(igs))) 355 code = restore_page_device(i_ctx_p, igs, gs_gstate_saved(igs));
356 if (code < 0) return code;
357 if (code > 0)
332 return push_callout(i_ctx_p, "%restore1pagedevice"); 358 return push_callout(i_ctx_p, "%restore1pagedevice");
333 gs_grestore(igs); 359 gs_grestore(igs);
334 } 360 }
335 if (restore_page_device(igs, gs_gstate_saved(igs))) 361 code = restore_page_device(i_ctx_p, igs, gs_gstate_saved(igs));
362 if (code < 0) return code;
363 if (code > 0)
336 return push_callout(i_ctx_p, "%restorepagedevice"); 364 return push_callout(i_ctx_p, "%restorepagedevice");
337 365
338 code = dorestore(i_ctx_p, asave); 366 code = dorestore(i_ctx_p, asave);
@@ -355,9 +383,12 @@ static int
355z2setgstate(i_ctx_t *i_ctx_p) 383z2setgstate(i_ctx_t *i_ctx_p)
356{ 384{
357 os_ptr op = osp; 385 os_ptr op = osp;
386 int code;
358 387
359 check_stype(*op, st_igstate_obj); 388 check_stype(*op, st_igstate_obj);
360 if (!restore_page_device(igs, igstate_ptr(op))) 389 code = restore_page_device(i_ctx_p, igs, igstate_ptr(op));
390 if (code < 0) return code;
391 if (code == 0)
361 return zsetgstate(i_ctx_p); 392 return zsetgstate(i_ctx_p);
362 return push_callout(i_ctx_p, "%setgstatepagedevice"); 393 return push_callout(i_ctx_p, "%setgstatepagedevice");
363} 394}