Blame SOURCES/ghostscript-cve-2018-16802.patch

09061c
From: Chris Liddell <chris.liddell@artifex.com>
09061c
Date: Tue, 4 Sep 2018 22:18:46 +0000 (+0100)
09061c
Subject: Bug 699714: retain .LockSafetyParams through failed .installpagedevice
09061c
09061c
Bug 699714: retain .LockSafetyParams through failed .installpagedevice
09061c
09061c
In the event that the .trysetparams fails during .installpagedevice, catch the
09061c
error, and ensure that at least the .LockSafetyParams is set.
09061c
09061c
https://git.ghostscript.com/?p=ghostpdl.git;a=commit;h=5812b1b78fc4d36fdc293b7859de69241140d590
09061c
09061c
From: Chris Liddell <chris.liddell@artifex.com>
09061c
Date: Wed, 5 Sep 2018 16:14:59 +0000 (+0100)
09061c
Subject: Bug 699718: Ensure stack space is available before gsrestore call out
09061c
09061c
Bug 699718: Ensure stack space is available before gsrestore call out
09061c
09061c
During a grestore, if the device is going to change, we call out to Postscript
09061c
to restore the device configuration, before returning to restore the graphics
09061c
state internally.
09061c
09061c
We have to ensure sufficient op stack space is available to complete the
09061c
operation, otherwise the device can end up an undefined state.
09061c
09061c
https://git.ghostscript.com/?p=ghostpdl.git;a=commit;h=3e5d316b72e3965b7968bb1d96baa137cd063ac6
09061c
09061c
From: Chris Liddell <chris.liddell@artifex.com>
09061c
Date: Fri, 7 Sep 2018 07:07:12 +0000 (+0100)
09061c
Subject: Bug 699718(2): Improve/augment stack size checking
09061c
09061c
Bug 699718(2): Improve/augment stack size checking
09061c
09061c
Improve the rebustness of the previous solution (previously it could trigger an
09061c
error when there *was* stack capacity available).
09061c
09061c
Remove redundant check: we don't need to check if the *current* stack size is
09061c
sufficient, before checking the maximum permitted stack size.
09061c
09061c
Also check the exec stack, as execstackoverflow can also cause the
09061c
Postscript call out to fail.
09061c
09061c
Lastly, in event of failure, put the LockSafetyParams flag back in the existing
09061c
device (this is only necessary because we don't enfore JOBSERVER mode).
09061c
09061c
Note: the Postscript callout (%grestorepagedevice) never pushes any dictionaries
09061c
on the dict stack - if that changes, we should check that stack, too.
09061c
09061c
https://git.ghostscript.com/?p=ghostpdl.git;a=commit;h=643b24dbd002fb9c131313253c307cf3951b3d47
09061c
---
09061c
09061c
diff -up ghostscript-9.07/base/gserrors.h.cve-2018-16802 ghostscript-9.07/base/gserrors.h
09061c
--- ghostscript-9.07/base/gserrors.h.cve-2018-16802	2018-11-27 19:07:38.076517822 +0100
09061c
+++ ghostscript-9.07/base/gserrors.h	2018-11-27 19:09:19.116234748 +0100
09061c
@@ -25,6 +25,7 @@
09061c
 /* We use ints rather than an enum to avoid a lot of casting. */
09061c
 
09061c
 #define gs_error_unknownerror (-1)	/* unknown error */
09061c
+#define gs_error_execstackoverflow (-5)
09061c
 #define gs_error_interrupt (-6)
09061c
 #define gs_error_invalidaccess (-7)
09061c
 #define gs_error_invalidfileaccess (-9)
09061c
@@ -33,6 +34,7 @@
09061c
 #define gs_error_limitcheck (-13)
09061c
 #define gs_error_nocurrentpoint (-14)
09061c
 #define gs_error_rangecheck (-15)
09061c
+#define gs_error_stackoverflow (-16)
09061c
 #define gs_error_stackunderflow (-17)
09061c
 #define gs_error_typecheck (-20)
09061c
 #define gs_error_undefined (-21)
09061c
diff -up ghostscript-9.07/psi/zdevice2.c.cve-2018-16802 ghostscript-9.07/psi/zdevice2.c
09061c
--- ghostscript-9.07/psi/zdevice2.c.cve-2018-16802	2018-11-27 19:04:51.665627148 +0100
09061c
+++ ghostscript-9.07/psi/zdevice2.c	2018-11-27 19:04:51.696626755 +0100
09061c
@@ -250,8 +250,8 @@ z2currentgstate(i_ctx_t *i_ctx_p)
09061c
 /* ------ Wrappers for operators that reset the graphics state. ------ */
09061c
 
09061c
 /* Check whether we need to call out to restore the page device. */
09061c
-static bool
09061c
-restore_page_device(const gs_state * pgs_old, const gs_state * pgs_new)
09061c
+static int
09061c
+restore_page_device(i_ctx_t *i_ctx_p, const gs_state * pgs_old, const gs_state * pgs_new)
09061c
 {
09061c
     gx_device *dev_old = gs_currentdevice(pgs_old);
09061c
     gx_device *dev_new;
09061c
@@ -259,9 +259,10 @@ restore_page_device(const gs_state * pgs
09061c
     gx_device *dev_t2;
09061c
     bool samepagedevice = obj_eq(dev_old->memory, &gs_int_gstate(pgs_old)->pagedevice,
09061c
         &gs_int_gstate(pgs_new)->pagedevice);
09061c
+    bool LockSafetyParams = dev_old->LockSafetyParams;
09061c
 
09061c
     if ((dev_t1 = (*dev_proc(dev_old, get_page_device)) (dev_old)) == 0)
09061c
-        return false;
09061c
+        return 0;
09061c
     /* If we are going to putdeviceparams in a callout, we need to */
09061c
     /* unlock temporarily.  The device will be re-locked as needed */
09061c
     /* by putdeviceparams from the pgs_old->pagedevice dict state. */
09061c
@@ -270,23 +271,51 @@ restore_page_device(const gs_state * pgs
09061c
     dev_new = gs_currentdevice(pgs_new);
09061c
     if (dev_old != dev_new) {
09061c
         if ((dev_t2 = (*dev_proc(dev_new, get_page_device)) (dev_new)) == 0)
09061c
-            return false;
09061c
-        if (dev_t1 != dev_t2)
09061c
-            return true;
09061c
+            samepagedevice = true;
09061c
+        else if (dev_t1 != dev_t2)
09061c
+            samepagedevice = false;
09061c
+    }
09061c
+
09061c
+    if (LockSafetyParams && !samepagedevice) {
09061c
+        const int required_ops = 512;
09061c
+        const int required_es = 32;
09061c
+
09061c
+        /* The %grestorepagedevice must complete: the biggest danger
09061c
+           is operand stack overflow. As we use get/putdeviceparams
09061c
+           that means pushing all the device params onto the stack,
09061c
+           pdfwrite having by far the largest number of parameters
09061c
+           at (currently) 212 key/value pairs - thus needing (currently)
09061c
+           424 entries on the op stack. Allowing for working stack
09061c
+           space, and safety margin.....
09061c
+         */
09061c
+        if (required_ops + ref_stack_count(&o_stack) >= ref_stack_max_count(&o_stack)) {
09061c
+           gs_currentdevice(pgs_old)->LockSafetyParams = LockSafetyParams;
09061c
+           return_error(gs_error_stackoverflow);
09061c
+        }
09061c
+        /* We also want enough exec stack space - 32 is an overestimate of
09061c
+           what we need to complete the Postscript call out.
09061c
+         */
09061c
+        if (required_es + ref_stack_count(&e_stack) >= ref_stack_max_count(&e_stack)) {
09061c
+           gs_currentdevice(pgs_old)->LockSafetyParams = LockSafetyParams;
09061c
+           return_error(gs_error_execstackoverflow);
09061c
+        }
09061c
     }
09061c
     /*
09061c
      * The current implementation of setpagedevice just sets new
09061c
      * parameters in the same device object, so we have to check
09061c
      * whether the page device dictionaries are the same.
09061c
      */
09061c
-    return !samepagedevice;
09061c
+    return samepagedevice ? 0 : 1;
09061c
 }
09061c
 
09061c
 /* - grestore - */
09061c
 static int
09061c
 z2grestore(i_ctx_t *i_ctx_p)
09061c
 {
09061c
-    if (!restore_page_device(igs, gs_state_saved(igs)))
09061c
+    int code = restore_page_device(i_ctx_p, igs, gs_state_saved(igs));
09061c
+    if (code < 0) return code;
09061c
+
09061c
+    if (code == 0)
09061c
         return gs_grestore(igs);
09061c
     return push_callout(i_ctx_p, "%grestorepagedevice");
09061c
 }
09061c
@@ -296,7 +325,9 @@ static int
09061c
 z2grestoreall(i_ctx_t *i_ctx_p)
09061c
 {
09061c
     for (;;) {
09061c
-        if (!restore_page_device(igs, gs_state_saved(igs))) {
09061c
+        int code = restore_page_device(i_ctx_p, igs, gs_state_saved(igs));
09061c
+        if (code < 0) return code;
09061c
+        if (code == 0) {
09061c
             bool done = !gs_state_saved(gs_state_saved(igs));
09061c
 
09061c
             gs_grestore(igs);
09061c
@@ -327,11 +358,15 @@ z2restore(i_ctx_t *i_ctx_p)
09061c
     if (code < 0) return code;
09061c
 
09061c
     while (gs_state_saved(gs_state_saved(igs))) {
09061c
-        if (restore_page_device(igs, gs_state_saved(igs)))
09061c
+        code = restore_page_device(i_ctx_p, igs, gs_state_saved(igs));
09061c
+        if (code < 0) return code;
09061c
+        if (code > 0)
09061c
             return push_callout(i_ctx_p, "%restore1pagedevice");
09061c
         gs_grestore(igs);
09061c
     }
09061c
-    if (restore_page_device(igs, gs_state_saved(igs)))
09061c
+    code = restore_page_device(i_ctx_p, igs, gs_state_saved(igs));
09061c
+    if (code < 0) return code;
09061c
+    if (code > 0)
09061c
         return push_callout(i_ctx_p, "%restorepagedevice");
09061c
 
09061c
     code = dorestore(i_ctx_p, asave);
09061c
@@ -354,9 +389,12 @@ static int
09061c
 z2setgstate(i_ctx_t *i_ctx_p)
09061c
 {
09061c
     os_ptr op = osp;
09061c
+    int code;
09061c
 
09061c
     check_stype(*op, st_igstate_obj);
09061c
-    if (!restore_page_device(igs, igstate_ptr(op)))
09061c
+    code = restore_page_device(i_ctx_p, igs, igstate_ptr(op));
09061c
+    if (code < 0) return code;
09061c
+    if (code == 0)
09061c
         return zsetgstate(i_ctx_p);
09061c
     return push_callout(i_ctx_p, "%setgstatepagedevice");
09061c
 }
09061c
diff -up ghostscript-9.07/Resource/Init/gs_setpd.ps.cve-2018-16802 ghostscript-9.07/Resource/Init/gs_setpd.ps
09061c
--- ghostscript-9.07/Resource/Init/gs_setpd.ps.cve-2018-16802	2013-02-14 08:58:16.000000000 +0100
09061c
+++ ghostscript-9.07/Resource/Init/gs_setpd.ps	2018-11-27 19:04:51.696626755 +0100
09061c
@@ -95,27 +95,41 @@ level2dict begin
09061c
  {	% Since setpagedevice doesn't create new device objects,
09061c
         % we must (carefully) reinstall the old parameters in
09061c
         % the same device.
09061c
-   .currentpagedevice pop //null currentdevice //null .trysetparams
09061c
+   .currentpagedevice pop //null currentdevice //null
09061c
+   { .trysetparams } .internalstopped
09061c
+   {
09061c
+     //null
09061c
+   } if
09061c
    dup type /booleantype eq
09061c
     { pop pop }
09061c
-    {		% This should never happen!
09061c
+    {
09061c
       SETPDDEBUG { (Error in .trysetparams!) = pstack flush } if
09061c
-      cleartomark pop pop pop
09061c
+      {cleartomark pop pop pop} .internalstopped pop
09061c
+      % if resetting the entire device state failed, at least put back the
09061c
+      % security related key
09061c
+      currentdevice //null //false mark /.LockSafetyParams
09061c
+      currentpagedevice /.LockSafetyParams .knownget not
09061c
+      {systemdict /SAFER .knownget not {//false} } if
09061c
+      .putdeviceparamsonly
09061c
       /.installpagedevice cvx /rangecheck signalerror
09061c
     }
09061c
    ifelse pop pop
09061c
         % A careful reading of the Red Book reveals that an erasepage
09061c
         % should occur, but *not* an initgraphics.
09061c
    erasepage .beginpage
09061c
- } bind def
09061c
+ } bind executeonly def
09061c
 
09061c
 /.uninstallpagedevice
09061c
- { 2 .endpage { .currentnumcopies //false .outputpage } if
09061c
+ {
09061c
+   {2 .endpage { .currentnumcopies //false .outputpage } if} .internalstopped pop
09061c
    nulldevice
09061c
  } bind def
09061c
 
09061c
 (%grestorepagedevice) cvn
09061c
- { .uninstallpagedevice grestore .installpagedevice
09061c
+ {
09061c
+ .uninstallpagedevice
09061c
+ grestore
09061c
+ .installpagedevice
09061c
  } bind def
09061c
 
09061c
 (%grestoreallpagedevice) cvn