194 lines
7.3 KiB
Diff
194 lines
7.3 KiB
Diff
|
Ghostscript 9.24 was released with an incomplete fix for CVE-2018-16509:
|
||
|
https://nvd.nist.gov/vuln/detail/CVE-2018-16509
|
||
|
https://bugs.chromium.org/p/project-zero/issues/detail?id=1640#c19
|
||
|
https://bugs.ghostscript.com/show_bug.cgi?id=699718
|
||
|
|
||
|
The reproducers no longer work after applying these commits:
|
||
|
|
||
|
https://git.ghostscript.com/?p=ghostpdl.git;a=commitdiff;h=5812b1b78fc4d36fdc293b7859de69241140d590
|
||
|
https://git.ghostscript.com/?p=ghostpdl.git;a=commitdiff;h=e914f1da46e33decc534486598dc3eadf69e6efb
|
||
|
https://git.ghostscript.com/?p=ghostpdl.git;a=commitdiff;h=3e5d316b72e3965b7968bb1d96baa137cd063ac6
|
||
|
https://git.ghostscript.com/?p=ghostpdl.git;a=commitdiff;h=643b24dbd002fb9c131313253c307cf3951b3d47
|
||
|
|
||
|
This patch is a "squashed" version of those.
|
||
|
|
||
|
diff --git a/Resource/Init/gs_setpd.ps b/Resource/Init/gs_setpd.ps
|
||
|
index bba3c8c0e..8fa7c51df 100644
|
||
|
--- a/Resource/Init/gs_setpd.ps
|
||
|
+++ b/Resource/Init/gs_setpd.ps
|
||
|
@@ -95,27 +95,41 @@ level2dict begin
|
||
|
{ % Since setpagedevice doesn't create new device objects,
|
||
|
% we must (carefully) reinstall the old parameters in
|
||
|
% the same device.
|
||
|
- .currentpagedevice pop //null currentdevice //null .trysetparams
|
||
|
+ .currentpagedevice pop //null currentdevice //null
|
||
|
+ { .trysetparams } .internalstopped
|
||
|
+ {
|
||
|
+ //null
|
||
|
+ } if
|
||
|
dup type /booleantype eq
|
||
|
{ pop pop }
|
||
|
- { % This should never happen!
|
||
|
+ {
|
||
|
SETPDDEBUG { (Error in .trysetparams!) = pstack flush } if
|
||
|
- cleartomark pop pop pop
|
||
|
+ {cleartomark pop pop pop} .internalstopped pop
|
||
|
+ % if resetting the entire device state failed, at least put back the
|
||
|
+ % security related key
|
||
|
+ currentdevice //null //false mark /.LockSafetyParams
|
||
|
+ currentpagedevice /.LockSafetyParams .knownget not
|
||
|
+ {systemdict /SAFER .knownget not {//false} } if
|
||
|
+ .putdeviceparamsonly
|
||
|
/.installpagedevice cvx /rangecheck signalerror
|
||
|
}
|
||
|
ifelse pop pop
|
||
|
% A careful reading of the Red Book reveals that an erasepage
|
||
|
% should occur, but *not* an initgraphics.
|
||
|
erasepage .beginpage
|
||
|
- } bind def
|
||
|
+ } bind executeonly def
|
||
|
|
||
|
/.uninstallpagedevice
|
||
|
- { 2 .endpage { .currentnumcopies //false .outputpage } if
|
||
|
+ {
|
||
|
+ {2 .endpage { .currentnumcopies //false .outputpage } if} .internalstopped pop
|
||
|
nulldevice
|
||
|
} bind def
|
||
|
|
||
|
(%grestorepagedevice) cvn
|
||
|
- { .uninstallpagedevice grestore .installpagedevice
|
||
|
+ {
|
||
|
+ .uninstallpagedevice
|
||
|
+ grestore
|
||
|
+ .installpagedevice
|
||
|
} bind def
|
||
|
|
||
|
(%grestoreallpagedevice) cvn
|
||
|
diff --git a/psi/zdevice2.c b/psi/zdevice2.c
|
||
|
index 0c7080d57..159a0c0d9 100644
|
||
|
--- a/psi/zdevice2.c
|
||
|
+++ b/psi/zdevice2.c
|
||
|
@@ -251,8 +251,8 @@ z2currentgstate(i_ctx_t *i_ctx_p)
|
||
|
/* ------ Wrappers for operators that reset the graphics state. ------ */
|
||
|
|
||
|
/* Check whether we need to call out to restore the page device. */
|
||
|
-static bool
|
||
|
-restore_page_device(const gs_gstate * pgs_old, const gs_gstate * pgs_new)
|
||
|
+static int
|
||
|
+restore_page_device(i_ctx_t *i_ctx_p, const gs_gstate * pgs_old, const gs_gstate * pgs_new)
|
||
|
{
|
||
|
gx_device *dev_old = gs_currentdevice(pgs_old);
|
||
|
gx_device *dev_new;
|
||
|
@@ -260,9 +260,10 @@ restore_page_device(const gs_gstate * pgs_old, const gs_gstate * pgs_new)
|
||
|
gx_device *dev_t2;
|
||
|
bool samepagedevice = obj_eq(dev_old->memory, &gs_int_gstate(pgs_old)->pagedevice,
|
||
|
&gs_int_gstate(pgs_new)->pagedevice);
|
||
|
+ bool LockSafetyParams = dev_old->LockSafetyParams;
|
||
|
|
||
|
if ((dev_t1 = (*dev_proc(dev_old, get_page_device)) (dev_old)) == 0)
|
||
|
- return false;
|
||
|
+ return 0;
|
||
|
/* If we are going to putdeviceparams in a callout, we need to */
|
||
|
/* unlock temporarily. The device will be re-locked as needed */
|
||
|
/* by putdeviceparams from the pgs_old->pagedevice dict state. */
|
||
|
@@ -271,23 +272,51 @@ restore_page_device(const gs_gstate * pgs_old, const gs_gstate * pgs_new)
|
||
|
dev_new = gs_currentdevice(pgs_new);
|
||
|
if (dev_old != dev_new) {
|
||
|
if ((dev_t2 = (*dev_proc(dev_new, get_page_device)) (dev_new)) == 0)
|
||
|
- return false;
|
||
|
- if (dev_t1 != dev_t2)
|
||
|
- return true;
|
||
|
+ samepagedevice = true;
|
||
|
+ else if (dev_t1 != dev_t2)
|
||
|
+ samepagedevice = false;
|
||
|
+ }
|
||
|
+
|
||
|
+ if (LockSafetyParams && !samepagedevice) {
|
||
|
+ const int required_ops = 512;
|
||
|
+ const int required_es = 32;
|
||
|
+
|
||
|
+ /* The %grestorepagedevice must complete: the biggest danger
|
||
|
+ is operand stack overflow. As we use get/putdeviceparams
|
||
|
+ that means pushing all the device params onto the stack,
|
||
|
+ pdfwrite having by far the largest number of parameters
|
||
|
+ at (currently) 212 key/value pairs - thus needing (currently)
|
||
|
+ 424 entries on the op stack. Allowing for working stack
|
||
|
+ space, and safety margin.....
|
||
|
+ */
|
||
|
+ if (required_ops + ref_stack_count(&o_stack) >= ref_stack_max_count(&o_stack)) {
|
||
|
+ gs_currentdevice(pgs_old)->LockSafetyParams = LockSafetyParams;
|
||
|
+ return_error(gs_error_stackoverflow);
|
||
|
+ }
|
||
|
+ /* We also want enough exec stack space - 32 is an overestimate of
|
||
|
+ what we need to complete the Postscript call out.
|
||
|
+ */
|
||
|
+ if (required_es + ref_stack_count(&e_stack) >= ref_stack_max_count(&e_stack)) {
|
||
|
+ gs_currentdevice(pgs_old)->LockSafetyParams = LockSafetyParams;
|
||
|
+ return_error(gs_error_execstackoverflow);
|
||
|
+ }
|
||
|
}
|
||
|
/*
|
||
|
* The current implementation of setpagedevice just sets new
|
||
|
* parameters in the same device object, so we have to check
|
||
|
* whether the page device dictionaries are the same.
|
||
|
*/
|
||
|
- return !samepagedevice;
|
||
|
+ return samepagedevice ? 0 : 1;
|
||
|
}
|
||
|
|
||
|
/* - grestore - */
|
||
|
static int
|
||
|
z2grestore(i_ctx_t *i_ctx_p)
|
||
|
{
|
||
|
- if (!restore_page_device(igs, gs_gstate_saved(igs)))
|
||
|
+ int code = restore_page_device(i_ctx_p, igs, gs_gstate_saved(igs));
|
||
|
+ if (code < 0) return code;
|
||
|
+
|
||
|
+ if (code == 0)
|
||
|
return gs_grestore(igs);
|
||
|
return push_callout(i_ctx_p, "%grestorepagedevice");
|
||
|
}
|
||
|
@@ -297,7 +326,9 @@ static int
|
||
|
z2grestoreall(i_ctx_t *i_ctx_p)
|
||
|
{
|
||
|
for (;;) {
|
||
|
- if (!restore_page_device(igs, gs_gstate_saved(igs))) {
|
||
|
+ int code = restore_page_device(i_ctx_p, igs, gs_gstate_saved(igs));
|
||
|
+ if (code < 0) return code;
|
||
|
+ if (code == 0) {
|
||
|
bool done = !gs_gstate_saved(gs_gstate_saved(igs));
|
||
|
|
||
|
gs_grestore(igs);
|
||
|
@@ -328,11 +359,15 @@ z2restore(i_ctx_t *i_ctx_p)
|
||
|
if (code < 0) return code;
|
||
|
|
||
|
while (gs_gstate_saved(gs_gstate_saved(igs))) {
|
||
|
- if (restore_page_device(igs, gs_gstate_saved(igs)))
|
||
|
+ code = restore_page_device(i_ctx_p, igs, gs_gstate_saved(igs));
|
||
|
+ if (code < 0) return code;
|
||
|
+ if (code > 0)
|
||
|
return push_callout(i_ctx_p, "%restore1pagedevice");
|
||
|
gs_grestore(igs);
|
||
|
}
|
||
|
- if (restore_page_device(igs, gs_gstate_saved(igs)))
|
||
|
+ code = restore_page_device(i_ctx_p, igs, gs_gstate_saved(igs));
|
||
|
+ if (code < 0) return code;
|
||
|
+ if (code > 0)
|
||
|
return push_callout(i_ctx_p, "%restorepagedevice");
|
||
|
|
||
|
code = dorestore(i_ctx_p, asave);
|
||
|
@@ -355,9 +390,12 @@ static int
|
||
|
z2setgstate(i_ctx_t *i_ctx_p)
|
||
|
{
|
||
|
os_ptr op = osp;
|
||
|
+ int code;
|
||
|
|
||
|
check_stype(*op, st_igstate_obj);
|
||
|
- if (!restore_page_device(igs, igstate_ptr(op)))
|
||
|
+ code = restore_page_device(i_ctx_p, igs, igstate_ptr(op));
|
||
|
+ if (code < 0) return code;
|
||
|
+ if (code == 0)
|
||
|
return zsetgstate(i_ctx_p);
|
||
|
return push_callout(i_ctx_p, "%setgstatepagedevice");
|
||
|
}
|