summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorChris Liddell <chris.liddell@artifex.com>2018-08-24 09:26:04 +0100
committerChris Liddell <chris.liddell@artifex.com>2018-08-24 14:36:48 +0100
commit5516c614dc33662a2afdc377159f70218e67bde5 (patch)
treebdf9d1406e63706c0edeacccd22ed4d76d22d693
parentb0a3854751363657998d4c9bd33c290bf9d07c67 (diff)
Improve restore robustness
Prompted by looking at Bug 699654: There are two variants of the restore operator in Ghostscript: one is Level 1 (restoring VM), the other is Level 2+ (adding page device restoring to the Level operator). This was implemented by the Level 2+ version restoring the device in the graphics state, then calling the Level 1 implementation to handle actually restoring the VM state. The problem was that the operand checking, and sanity of the save object was only done by the Level 1 variant, thus meaning an invalid save object could leave a (Level 2+) restore partially complete - with the page device part restored, but not VM, and the page device not configured. To solve that, this commit splits the operand and sanity checking, and the core of the restore operation into separate functions, so the relevant operators can validate the operand *before* taking any further action. That reduces the chances of an invalid restore leaving the interpreter in an unknown state. If an error occurs during the actual VM restore it is essentially fatal, and the interpreter cannot continue, but as an extra surety for security, in the event of such an error, we'll explicitly preserve the LockSafetyParams of the device, rather than rely on the post-restore device configuration (which won't happen in the event of an error).
-rw-r--r--psi/int.mak4
-rw-r--r--psi/isave.h6
-rw-r--r--psi/zdevice2.c33
-rw-r--r--psi/zvmem.c56
4 files changed, 84 insertions, 15 deletions
diff --git a/psi/int.mak b/psi/int.mak
index 19688202a..16db0cff0 100644
--- a/psi/int.mak
+++ b/psi/int.mak
@@ -1086,8 +1086,8 @@ $(PSD)pagedev.dev : $(ECHOGS_XE) $(pagedev_)\
1086 1086
1087$(PSOBJ)zdevice2.$(OBJ) : $(PSSRC)zdevice2.c $(OP) $(math__h) $(memory__h)\ 1087$(PSOBJ)zdevice2.$(OBJ) : $(PSSRC)zdevice2.c $(OP) $(math__h) $(memory__h)\
1088 $(dstack_h) $(estack_h)\ 1088 $(dstack_h) $(estack_h)\
1089 $(idict_h) $(idparam_h) $(igstate_h) $(iname_h) $(iutil_h) $(store_h)\ 1089 $(idict_h) $(idparam_h) $(igstate_h) $(iname_h) $(isave) $(iutil_h) \
1090 $(gxdevice_h) $(gsstate_h) $(INT_MAK) $(MAKEDIRS) 1090 $(store_h) $(gxdevice_h) $(gsstate_h) $(INT_MAK) $(MAKEDIRS)
1091 $(PSCC) $(PSO_)zdevice2.$(OBJ) $(C_) $(PSSRC)zdevice2.c 1091 $(PSCC) $(PSO_)zdevice2.$(OBJ) $(C_) $(PSSRC)zdevice2.c
1092 1092
1093$(PSOBJ)zmedia2.$(OBJ) : $(PSSRC)zmedia2.c $(OP) $(math__h) $(memory__h)\ 1093$(PSOBJ)zmedia2.$(OBJ) : $(PSSRC)zmedia2.c $(OP) $(math__h) $(memory__h)\
diff --git a/psi/isave.h b/psi/isave.h
index 30216398f..7eaaced46 100644
--- a/psi/isave.h
+++ b/psi/isave.h
@@ -128,4 +128,10 @@ int font_restore(const alloc_save_t * save);
128 express purpose of getting the library context. */ 128 express purpose of getting the library context. */
129gs_memory_t *gs_save_any_memory(const alloc_save_t *save); 129gs_memory_t *gs_save_any_memory(const alloc_save_t *save);
130 130
131int
132restore_check_save(i_ctx_t *i_ctx_p, alloc_save_t **asave);
133
134int
135dorestore(i_ctx_t *i_ctx_p, alloc_save_t *asave);
136
131#endif /* isave_INCLUDED */ 137#endif /* isave_INCLUDED */
diff --git a/psi/zdevice2.c b/psi/zdevice2.c
index 9fbb4e339..0c7080d57 100644
--- a/psi/zdevice2.c
+++ b/psi/zdevice2.c
@@ -26,6 +26,7 @@
26#include "igstate.h" 26#include "igstate.h"
27#include "iname.h" 27#include "iname.h"
28#include "iutil.h" 28#include "iutil.h"
29#include "isave.h"
29#include "store.h" 30#include "store.h"
30#include "gxdevice.h" 31#include "gxdevice.h"
31#include "gsstate.h" 32#include "gsstate.h"
@@ -307,13 +308,24 @@ z2grestoreall(i_ctx_t *i_ctx_p)
307 } 308 }
308 return 0; 309 return 0;
309} 310}
310 311/* This is the Level 2+ variant of restore - which adds restoring
312 of the page device to the Level 1 variant in zvmem.c.
313 Previous this restored the device state before calling zrestore.c
314 which validated operands etc, meaning a restore could error out
315 partially complete.
316 The operand checking, and actual VM restore are now in two functions
317 so they can called separately thus, here, we can do as much
318 checking as possible, before embarking on actual changes
319 */
311/* <save> restore - */ 320/* <save> restore - */
312static int 321static int
313z2restore(i_ctx_t *i_ctx_p) 322z2restore(i_ctx_t *i_ctx_p)
314{ 323{
315 os_ptr op = osp; 324 alloc_save_t *asave;
316 check_type(*op, t_save); 325 bool saveLockSafety = gs_currentdevice_inline(igs)->LockSafetyParams;
326 int code = restore_check_save(i_ctx_p, &asave);
327
328 if (code < 0) return code;
317 329
318 while (gs_gstate_saved(gs_gstate_saved(igs))) { 330 while (gs_gstate_saved(gs_gstate_saved(igs))) {
319 if (restore_page_device(igs, gs_gstate_saved(igs))) 331 if (restore_page_device(igs, gs_gstate_saved(igs)))
@@ -322,7 +334,20 @@ z2restore(i_ctx_t *i_ctx_p)
322 } 334 }
323 if (restore_page_device(igs, gs_gstate_saved(igs))) 335 if (restore_page_device(igs, gs_gstate_saved(igs)))
324 return push_callout(i_ctx_p, "%restorepagedevice"); 336 return push_callout(i_ctx_p, "%restorepagedevice");
325 return zrestore(i_ctx_p); 337
338 code = dorestore(i_ctx_p, asave);
339
340 if (code < 0) {
341 /* An error here is basically fatal, but....
342 restore_page_device() has to set LockSafetyParams false so it can
343 configure the restored device correctly - in normal operation, that
344 gets reset by that configuration. If we hit an error, though, that
345 may not happen - at least ensure we keep the setting through the
346 error.
347 */
348 gs_currentdevice_inline(igs)->LockSafetyParams = saveLockSafety;
349 }
350 return code;
326} 351}
327 352
328/* <gstate> setgstate - */ 353/* <gstate> setgstate - */
diff --git a/psi/zvmem.c b/psi/zvmem.c
index 44cd7a8e0..87a0a4ff1 100644
--- a/psi/zvmem.c
+++ b/psi/zvmem.c
@@ -99,19 +99,18 @@ zsave(i_ctx_t *i_ctx_p)
99static int restore_check_operand(os_ptr, alloc_save_t **, gs_dual_memory_t *); 99static int restore_check_operand(os_ptr, alloc_save_t **, gs_dual_memory_t *);
100static int restore_check_stack(const i_ctx_t *i_ctx_p, const ref_stack_t *, const alloc_save_t *, bool); 100static int restore_check_stack(const i_ctx_t *i_ctx_p, const ref_stack_t *, const alloc_save_t *, bool);
101static void restore_fix_stack(i_ctx_t *i_ctx_p, ref_stack_t *, const alloc_save_t *, bool); 101static void restore_fix_stack(i_ctx_t *i_ctx_p, ref_stack_t *, const alloc_save_t *, bool);
102
103/* Do as many up front checks of the save object as we reasonably can */
102int 104int
103zrestore(i_ctx_t *i_ctx_p) 105restore_check_save(i_ctx_t *i_ctx_p, alloc_save_t **asave)
104{ 106{
105 os_ptr op = osp; 107 os_ptr op = osp;
106 alloc_save_t *asave; 108 int code = restore_check_operand(op, asave, idmemory);
107 bool last;
108 vm_save_t *vmsave;
109 int code = restore_check_operand(op, &asave, idmemory);
110 109
111 if (code < 0) 110 if (code < 0)
112 return code; 111 return code;
113 if_debug2m('u', imemory, "[u]vmrestore 0x%lx, id = %lu\n", 112 if_debug2m('u', imemory, "[u]vmrestore 0x%lx, id = %lu\n",
114 (ulong) alloc_save_client_data(asave), 113 (ulong) alloc_save_client_data(*asave),
115 (ulong) op->value.saveid); 114 (ulong) op->value.saveid);
116 if (I_VALIDATE_BEFORE_RESTORE) 115 if (I_VALIDATE_BEFORE_RESTORE)
117 ivalidate_clean_spaces(i_ctx_p); 116 ivalidate_clean_spaces(i_ctx_p);
@@ -120,14 +119,37 @@ zrestore(i_ctx_t *i_ctx_p)
120 { 119 {
121 int code; 120 int code;
122 121
123 if ((code = restore_check_stack(i_ctx_p, &o_stack, asave, false)) < 0 || 122 if ((code = restore_check_stack(i_ctx_p, &o_stack, *asave, false)) < 0 ||
124 (code = restore_check_stack(i_ctx_p, &e_stack, asave, true)) < 0 || 123 (code = restore_check_stack(i_ctx_p, &e_stack, *asave, true)) < 0 ||
125 (code = restore_check_stack(i_ctx_p, &d_stack, asave, false)) < 0 124 (code = restore_check_stack(i_ctx_p, &d_stack, *asave, false)) < 0
126 ) { 125 ) {
127 osp++; 126 osp++;
128 return code; 127 return code;
129 } 128 }
130 } 129 }
130 osp++;
131 return 0;
132}
133
134/* the semantics of restore differ slightly between Level 1 and
135 Level 2 and later - the latter includes restoring the device
136 state (whilst Level 1 didn't have "page devices" as such).
137 Hence we have two restore operators - one here (Level 1)
138 and one in zdevice2.c (Level 2+). For that reason, the
139 operand checking and guts of the restore operation are
140 separated so both implementations can use them to best
141 effect.
142 */
143int
144dorestore(i_ctx_t *i_ctx_p, alloc_save_t *asave)
145{
146 os_ptr op = osp;
147 bool last;
148 vm_save_t *vmsave;
149 int code;
150
151 osp--;
152
131 /* Reset l_new in all stack entries if the new save level is zero. */ 153 /* Reset l_new in all stack entries if the new save level is zero. */
132 /* Also do some special fixing on the e-stack. */ 154 /* Also do some special fixing on the e-stack. */
133 restore_fix_stack(i_ctx_p, &o_stack, asave, false); 155 restore_fix_stack(i_ctx_p, &o_stack, asave, false);
@@ -170,9 +192,24 @@ zrestore(i_ctx_t *i_ctx_p)
170 /* cause an 'invalidaccess' in setuserparams. Temporarily set */ 192 /* cause an 'invalidaccess' in setuserparams. Temporarily set */
171 /* LockFilePermissions false until the gs_lev2.ps can do a */ 193 /* LockFilePermissions false until the gs_lev2.ps can do a */
172 /* setuserparams from the restored userparam dictionary. */ 194 /* setuserparams from the restored userparam dictionary. */
195 /* NOTE: This is safe to do here, since the restore has */
196 /* successfully completed - this should never come before any */
197 /* operation that can trigger an error */
173 i_ctx_p->LockFilePermissions = false; 198 i_ctx_p->LockFilePermissions = false;
174 return 0; 199 return 0;
175} 200}
201
202int
203zrestore(i_ctx_t *i_ctx_p)
204{
205 alloc_save_t *asave;
206 int code = restore_check_save(i_ctx_p, &asave);
207 if (code < 0)
208 return code;
209
210 return dorestore(i_ctx_p, asave);
211}
212
176/* Check the operand of a restore. */ 213/* Check the operand of a restore. */
177static int 214static int
178restore_check_operand(os_ptr op, alloc_save_t ** pasave, 215restore_check_operand(os_ptr op, alloc_save_t ** pasave,
@@ -193,6 +230,7 @@ restore_check_operand(os_ptr op, alloc_save_t ** pasave,
193 *pasave = asave; 230 *pasave = asave;
194 return 0; 231 return 0;
195} 232}
233
196/* Check a stack to make sure all its elements are older than a save. */ 234/* Check a stack to make sure all its elements are older than a save. */
197static int 235static int
198restore_check_stack(const i_ctx_t *i_ctx_p, const ref_stack_t * pstack, 236restore_check_stack(const i_ctx_t *i_ctx_p, const ref_stack_t * pstack,