diff options
author | Chris Liddell <chris.liddell@artifex.com> | 2018-09-05 17:14:59 +0100 |
---|---|---|
committer | Chris Liddell <chris.liddell@artifex.com> | 2018-09-06 17:14:28 +0100 |
commit | 3e5d316b72e3965b7968bb1d96baa137cd063ac6 (patch) | |
tree | ba1fefe52b79de5b25a4b3529bb2526c8f3ec7db | |
parent | c8c01f8c4164bc10281d9e8f87cf96314d93104b (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.ps | 20 | ||||
-rw-r--r-- | psi/zdevice2.c | 55 |
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. */ |
254 | static bool | 254 | static int |
255 | restore_page_device(const gs_gstate * pgs_old, const gs_gstate * pgs_new) | 255 | restore_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 - */ |
287 | static int | 306 | static int |
288 | z2grestore(i_ctx_t *i_ctx_p) | 307 | z2grestore(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 | |||
297 | z2grestoreall(i_ctx_t *i_ctx_p) | 319 | z2grestoreall(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 | |||
355 | z2setgstate(i_ctx_t *i_ctx_p) | 383 | z2setgstate(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 | } |