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

b21b1b
From 26d93b9a3f2a47ece821defe12e9059b4a7a3bf2 Mon Sep 17 00:00:00 2001
b21b1b
From: Chris Liddell <chris.liddell@artifex.com>
b21b1b
Date: Fri, 24 Aug 2018 09:26:04 +0100
b21b1b
Subject: [PATCH] Improve restore robustness
b21b1b
b21b1b
Prompted by looking at Bug 699654:
b21b1b
b21b1b
There are two variants of the restore operator in Ghostscript: one is Level 1
b21b1b
(restoring VM), the other is Level 2+ (adding page device restoring to the
b21b1b
Level operator).
b21b1b
b21b1b
This was implemented by the Level 2+ version restoring the device in the
b21b1b
graphics state, then calling the Level 1 implementation to handle actually
b21b1b
restoring the VM state.
b21b1b
b21b1b
The problem was that the operand checking, and sanity of the save object was
b21b1b
only done by the Level 1 variant, thus meaning an invalid save object could
b21b1b
leave a (Level 2+) restore partially complete - with the page device part
b21b1b
restored, but not VM, and the page device not configured.
b21b1b
b21b1b
To solve that, this commit splits the operand and sanity checking, and the
b21b1b
core of the restore operation into separate functions, so the relevant
b21b1b
operators can validate the operand *before* taking any further action. That
b21b1b
reduces the chances of an invalid restore leaving the interpreter in an
b21b1b
unknown state.
b21b1b
b21b1b
If an error occurs during the actual VM restore it is essentially fatal, and the
b21b1b
interpreter cannot continue, but as an extra surety for security, in the event
b21b1b
of such an error, we'll explicitly preserve the LockSafetyParams of the device,
b21b1b
rather than rely on the post-restore device configuration (which won't happen
b21b1b
in the event of an error).
b21b1b
---
b21b1b
 psi/int.mak    |  4 ++--
b21b1b
 psi/isave.h    |  6 ++++++
b21b1b
 psi/zdevice2.c | 32 ++++++++++++++++++++++++++++++--
b21b1b
 psi/zvmem.c    | 56 +++++++++++++++++++++++++++++++++++++++++++++++---------
b21b1b
 4 files changed, 85 insertions(+), 13 deletions(-)
b21b1b
b21b1b
diff --git a/psi/int.mak b/psi/int.mak
b21b1b
index fbf191d..4d0f296 100644
b21b1b
--- a/psi/int.mak
b21b1b
+++ b/psi/int.mak
b21b1b
@@ -1047,8 +1047,8 @@ $(PSD)pagedev.dev : $(INT_MAK) $(ECHOGS_XE) $(pagedev_)
b21b1b
 
b21b1b
 $(PSOBJ)zdevice2.$(OBJ) : $(PSSRC)zdevice2.c $(OP) $(math__h) $(memory__h)\
b21b1b
  $(dstack_h) $(estack_h)\
b21b1b
- $(idict_h) $(idparam_h) $(igstate_h) $(iname_h) $(iutil_h) $(store_h)\
b21b1b
- $(gxdevice_h) $(gsstate_h)
b21b1b
+ $(idict_h) $(idparam_h) $(igstate_h) $(iname_h) $(isave) $(iutil_h) \
b21b1b
+ $(store_h) $(gxdevice_h) $(gsstate_h)
b21b1b
 	$(PSCC) $(PSO_)zdevice2.$(OBJ) $(C_) $(PSSRC)zdevice2.c
b21b1b
 
b21b1b
 $(PSOBJ)zmedia2.$(OBJ) : $(PSSRC)zmedia2.c $(OP) $(math__h) $(memory__h)\
b21b1b
diff --git a/psi/isave.h b/psi/isave.h
b21b1b
index f8a6f32..0914de1 100644
b21b1b
--- a/psi/isave.h
b21b1b
+++ b/psi/isave.h
b21b1b
@@ -122,4 +122,10 @@ int  font_restore(const alloc_save_t * save);
b21b1b
    express purpose of getting the library context. */
b21b1b
 gs_memory_t *gs_save_any_memory(const alloc_save_t *save);
b21b1b
 
b21b1b
+int
b21b1b
+restore_check_save(i_ctx_t *i_ctx_p, alloc_save_t **asave);
b21b1b
+
b21b1b
+int
b21b1b
+dorestore(i_ctx_t *i_ctx_p, alloc_save_t *asave);
b21b1b
+
b21b1b
 #endif /* isave_INCLUDED */
b21b1b
diff --git a/psi/zdevice2.c b/psi/zdevice2.c
b21b1b
index f391da1..2ed2a3b 100644
b21b1b
--- a/psi/zdevice2.c
b21b1b
+++ b/psi/zdevice2.c
b21b1b
@@ -26,6 +26,7 @@
b21b1b
 #include "igstate.h"
b21b1b
 #include "iname.h"
b21b1b
 #include "iutil.h"
b21b1b
+#include "isave.h"
b21b1b
 #include "store.h"
b21b1b
 #include "gxdevice.h"
b21b1b
 #include "gsstate.h"
b21b1b
@@ -306,11 +307,25 @@ z2grestoreall(i_ctx_t *i_ctx_p)
b21b1b
     }
b21b1b
     return 0;
b21b1b
 }
b21b1b
-
b21b1b
+/* This is the Level 2+ variant of restore - which adds restoring
b21b1b
+   of the page device to the Level 1 variant in zvmem.c.
b21b1b
+   Previous this restored the device state before calling zrestore.c
b21b1b
+   which validated operands etc, meaning a restore could error out
b21b1b
+   partially complete.
b21b1b
+   The operand checking, and actual VM restore are now in two functions
b21b1b
+   so they can called separately thus, here, we can do as much
b21b1b
+   checking as possible, before embarking on actual changes
b21b1b
+ */
b21b1b
 /* <save> restore - */
b21b1b
 static int
b21b1b
 z2restore(i_ctx_t *i_ctx_p)
b21b1b
 {
b21b1b
+    alloc_save_t *asave;
b21b1b
+    bool saveLockSafety = gs_currentdevice_inline(igs)->LockSafetyParams;
b21b1b
+    int code = restore_check_save(i_ctx_p, &asave);
b21b1b
+
b21b1b
+    if (code < 0) return code;
b21b1b
+
b21b1b
     while (gs_state_saved(gs_state_saved(igs))) {
b21b1b
         if (restore_page_device(igs, gs_state_saved(igs)))
b21b1b
             return push_callout(i_ctx_p, "%restore1pagedevice");
b21b1b
@@ -318,7 +333,20 @@ z2restore(i_ctx_t *i_ctx_p)
b21b1b
     }
b21b1b
     if (restore_page_device(igs, gs_state_saved(igs)))
b21b1b
         return push_callout(i_ctx_p, "%restorepagedevice");
b21b1b
-    return zrestore(i_ctx_p);
b21b1b
+
b21b1b
+    code = dorestore(i_ctx_p, asave);
b21b1b
+
b21b1b
+    if (code < 0) {
b21b1b
+        /* An error here is basically fatal, but....
b21b1b
+           restore_page_device() has to set LockSafetyParams false so it can
b21b1b
+           configure the restored device correctly - in normal operation, that
b21b1b
+           gets reset by that configuration. If we hit an error, though, that
b21b1b
+           may not happen -  at least ensure we keep the setting through the
b21b1b
+           error.
b21b1b
+         */
b21b1b
+        gs_currentdevice_inline(igs)->LockSafetyParams = saveLockSafety;
b21b1b
+    }
b21b1b
+    return code;
b21b1b
 }
b21b1b
 
b21b1b
 /* <gstate> setgstate - */
b21b1b
diff --git a/psi/zvmem.c b/psi/zvmem.c
b21b1b
index 4ad8f9a..d8de39d 100644
b21b1b
--- a/psi/zvmem.c
b21b1b
+++ b/psi/zvmem.c
b21b1b
@@ -104,19 +104,18 @@ zsave(i_ctx_t *i_ctx_p)
b21b1b
 static int restore_check_operand(os_ptr, alloc_save_t **, gs_dual_memory_t *);
b21b1b
 static int restore_check_stack(const i_ctx_t *i_ctx_p, const ref_stack_t *, const alloc_save_t *, bool);
b21b1b
 static void restore_fix_stack(i_ctx_t *i_ctx_p, ref_stack_t *, const alloc_save_t *, bool);
b21b1b
+
b21b1b
+/* Do as many up front checks of the save object as we reasonably can */
b21b1b
 int
b21b1b
-zrestore(i_ctx_t *i_ctx_p)
b21b1b
+restore_check_save(i_ctx_t *i_ctx_p, alloc_save_t **asave)
b21b1b
 {
b21b1b
     os_ptr op = osp;
b21b1b
-    alloc_save_t *asave;
b21b1b
-    bool last;
b21b1b
-    vm_save_t *vmsave;
b21b1b
-    int code = restore_check_operand(op, &asave, idmemory);
b21b1b
+    int code = restore_check_operand(op, asave, idmemory);
b21b1b
 
b21b1b
     if (code < 0)
b21b1b
         return code;
b21b1b
     if_debug2m('u', imemory, "[u]vmrestore 0x%lx, id = %lu\n",
b21b1b
-               (ulong) alloc_save_client_data(asave),
b21b1b
+               (ulong) alloc_save_client_data(*asave),
b21b1b
                (ulong) op->value.saveid);
b21b1b
     if (I_VALIDATE_BEFORE_RESTORE)
b21b1b
         ivalidate_clean_spaces(i_ctx_p);
b21b1b
@@ -125,14 +124,37 @@ zrestore(i_ctx_t *i_ctx_p)
b21b1b
     {
b21b1b
         int code;
b21b1b
 
b21b1b
-        if ((code = restore_check_stack(i_ctx_p, &o_stack, asave, false)) < 0 ||
b21b1b
-            (code = restore_check_stack(i_ctx_p, &e_stack, asave, true)) < 0 ||
b21b1b
-            (code = restore_check_stack(i_ctx_p, &d_stack, asave, false)) < 0
b21b1b
+        if ((code = restore_check_stack(i_ctx_p, &o_stack, *asave, false)) < 0 ||
b21b1b
+            (code = restore_check_stack(i_ctx_p, &e_stack, *asave, true)) < 0 ||
b21b1b
+            (code = restore_check_stack(i_ctx_p, &d_stack, *asave, false)) < 0
b21b1b
             ) {
b21b1b
             osp++;
b21b1b
             return code;
b21b1b
         }
b21b1b
     }
b21b1b
+    osp++;
b21b1b
+    return 0;
b21b1b
+}
b21b1b
+
b21b1b
+/* the semantics of restore differ slightly between Level 1 and
b21b1b
+   Level 2 and later - the latter includes restoring the device
b21b1b
+   state (whilst Level 1 didn't have "page devices" as such).
b21b1b
+   Hence we have two restore operators - one here (Level 1)
b21b1b
+   and one in zdevice2.c (Level 2+). For that reason, the
b21b1b
+   operand checking and guts of the restore operation are
b21b1b
+   separated so both implementations can use them to best
b21b1b
+   effect.
b21b1b
+ */
b21b1b
+int
b21b1b
+dorestore(i_ctx_t *i_ctx_p, alloc_save_t *asave)
b21b1b
+{
b21b1b
+    os_ptr op = osp;
b21b1b
+    bool last;
b21b1b
+    vm_save_t *vmsave;
b21b1b
+    int code;
b21b1b
+
b21b1b
+    osp--;
b21b1b
+
b21b1b
     /* Reset l_new in all stack entries if the new save level is zero. */
b21b1b
     /* Also do some special fixing on the e-stack. */
b21b1b
     restore_fix_stack(i_ctx_p, &o_stack, asave, false);
b21b1b
@@ -175,9 +197,24 @@ zrestore(i_ctx_t *i_ctx_p)
b21b1b
     /* cause an 'invalidaccess' in setuserparams. Temporarily set     */
b21b1b
     /* LockFilePermissions false until the gs_lev2.ps can do a        */
b21b1b
     /* setuserparams from the restored userparam dictionary.          */
b21b1b
+    /* NOTE: This is safe to do here, since the restore has           */
b21b1b
+    /* successfully completed - this should never come before any     */
b21b1b
+    /* operation that can trigger an error                            */
b21b1b
     i_ctx_p->LockFilePermissions = false;
b21b1b
     return 0;
b21b1b
 }
b21b1b
+
b21b1b
+int
b21b1b
+zrestore(i_ctx_t *i_ctx_p)
b21b1b
+{
b21b1b
+    alloc_save_t *asave;
b21b1b
+    int code = restore_check_save(i_ctx_p, &asave);
b21b1b
+    if (code < 0)
b21b1b
+        return code;
b21b1b
+
b21b1b
+    return dorestore(i_ctx_p, asave);
b21b1b
+}
b21b1b
+
b21b1b
 /* Check the operand of a restore. */
b21b1b
 static int
b21b1b
 restore_check_operand(os_ptr op, alloc_save_t ** pasave,
b21b1b
@@ -198,6 +235,7 @@ restore_check_operand(os_ptr op, alloc_save_t ** pasave,
b21b1b
     *pasave = asave;
b21b1b
     return 0;
b21b1b
 }
b21b1b
+
b21b1b
 /* Check a stack to make sure all its elements are older than a save. */
b21b1b
 static int
b21b1b
 restore_check_stack(const i_ctx_t *i_ctx_p, const ref_stack_t * pstack,
b21b1b
-- 
b21b1b
2.14.4
b21b1b