1 /* Copyright (C) 2001-2006 Artifex Software, Inc.
2    All Rights Reserved.
3 
4    This software is provided AS-IS with no warranty, either express or
5    implied.
6 
7    This software is distributed under license and may not be copied, modified
8    or distributed except as expressly authorized under the terms of that
9    license.  Refer to licensing information at http://www.artifex.com/
10    or contact Artifex Software, Inc.,  7 Mt. Lassen Drive - Suite A-134,
11    San Rafael, CA  94903, U.S.A., +1(415)492-9861, for further information.
12 */
13 
14 /* $Id: zvmem.c 9043 2008-08-28 22:48:19Z giles $ */
15 /* "Virtual memory" operators */
16 #include "stat_.h" /* get system header early to avoid name clash on Cygwin */
17 #include "ghost.h"
18 #include "gsstruct.h"
19 #include "oper.h"
20 #include "estack.h"		/* for checking in restore */
21 #include "ialloc.h"
22 #include "idict.h"		/* ditto */
23 #include "igstate.h"
24 #include "isave.h"
25 #include "dstack.h"
26 #include "stream.h"		/* for files.h */
27 #include "files.h"		/* for e-stack processing */
28 #include "store.h"
29 #include "gsmatrix.h"		/* for gsstate.h */
30 #include "gsstate.h"
31 
32 /* Define whether we validate memory before/after save/restore. */
33 /* Note that we only actually do this if DEBUG is set and -Z? is selected. */
34 static const bool I_VALIDATE_BEFORE_SAVE = true;
35 static const bool I_VALIDATE_AFTER_SAVE = true;
36 static const bool I_VALIDATE_BEFORE_RESTORE = true;
37 static const bool I_VALIDATE_AFTER_RESTORE = true;
38 
39 /* 'Save' structure */
40 typedef struct vm_save_s vm_save_t;
41 struct vm_save_s {
42     gs_state *gsave;		/* old graphics state */
43 };
44 
45 gs_private_st_ptrs1(st_vm_save, vm_save_t, "savetype",
46 		    vm_save_enum_ptrs, vm_save_reloc_ptrs, gsave);
47 
48 /* Clean up the stacks and validate storage. */
49 static void
ivalidate_clean_spaces(i_ctx_t * i_ctx_p)50 ivalidate_clean_spaces(i_ctx_t *i_ctx_p)
51 {
52     if (gs_debug_c('?')) {
53 	ref_stack_cleanup(&d_stack);
54 	ref_stack_cleanup(&e_stack);
55 	ref_stack_cleanup(&o_stack);
56 	ivalidate_spaces();
57     }
58 }
59 
60 /* - save <save> */
61 int
zsave(i_ctx_t * i_ctx_p)62 zsave(i_ctx_t *i_ctx_p)
63 {
64     os_ptr op = osp;
65     uint space = icurrent_space;
66     vm_save_t *vmsave;
67     ulong sid;
68     int code;
69     gs_state *prev;
70 
71     if (I_VALIDATE_BEFORE_SAVE)
72 	ivalidate_clean_spaces(i_ctx_p);
73     ialloc_set_space(idmemory, avm_local);
74     vmsave = ialloc_struct(vm_save_t, &st_vm_save, "zsave");
75     ialloc_set_space(idmemory, space);
76     if (vmsave == 0)
77 	return_error(e_VMerror);
78     code = alloc_save_state(idmemory, vmsave, &sid);
79     if (code < 0)
80 	return code;
81     if (sid == 0) {
82 	ifree_object(vmsave, "zsave");
83 	return_error(e_VMerror);
84     }
85     if_debug2('u', "[u]vmsave 0x%lx, id = %lu\n",
86 	      (ulong) vmsave, (ulong) sid);
87     code = gs_gsave_for_save(igs, &prev);
88     if (code < 0)
89 	return code;
90     code = gs_gsave(igs);
91     if (code < 0)
92 	return code;
93     vmsave->gsave = prev;
94     push(1);
95     make_tav(op, t_save, 0, saveid, sid);
96     if (I_VALIDATE_AFTER_SAVE)
97 	ivalidate_clean_spaces(i_ctx_p);
98     return 0;
99 }
100 
101 /* <save> restore - */
102 static int restore_check_operand(os_ptr, alloc_save_t **, gs_dual_memory_t *);
103 static int restore_check_stack(const i_ctx_t *i_ctx_p, const ref_stack_t *, const alloc_save_t *, bool);
104 static void restore_fix_stack(ref_stack_t *, const alloc_save_t *, bool);
105 int
zrestore(i_ctx_t * i_ctx_p)106 zrestore(i_ctx_t *i_ctx_p)
107 {
108     os_ptr op = osp;
109     alloc_save_t *asave;
110     bool last;
111     vm_save_t *vmsave;
112     int code = restore_check_operand(op, &asave, idmemory);
113 
114     if (code < 0)
115 	return code;
116     if_debug2('u', "[u]vmrestore 0x%lx, id = %lu\n",
117 	      (ulong) alloc_save_client_data(asave),
118 	      (ulong) op->value.saveid);
119     if (I_VALIDATE_BEFORE_RESTORE)
120 	ivalidate_clean_spaces(i_ctx_p);
121     /* Check the contents of the stacks. */
122     osp--;
123     {
124 	int code;
125 
126 	if ((code = restore_check_stack(i_ctx_p, &o_stack, asave, false)) < 0 ||
127 	    (code = restore_check_stack(i_ctx_p, &e_stack, asave, true)) < 0 ||
128 	    (code = restore_check_stack(i_ctx_p, &d_stack, asave, false)) < 0
129 	    ) {
130 	    osp++;
131 	    return code;
132 	}
133     }
134     /* Reset l_new in all stack entries if the new save level is zero. */
135     /* Also do some special fixing on the e-stack. */
136     restore_fix_stack(&o_stack, asave, false);
137     restore_fix_stack(&e_stack, asave, true);
138     restore_fix_stack(&d_stack, asave, false);
139     /* Iteratively restore the state of memory, */
140     /* also doing a grestoreall at each step. */
141     do {
142 	vmsave = alloc_save_client_data(alloc_save_current(idmemory));
143 	/* Restore the graphics state. */
144 	gs_grestoreall_for_restore(igs, vmsave->gsave);
145 	/*
146 	 * If alloc_save_space decided to do a second save, the vmsave
147 	 * object was allocated one save level less deep than the
148 	 * current level, so ifree_object won't actually free it;
149 	 * however, it points to a gsave object that definitely
150 	 * *has* been freed.  In order not to trip up the garbage
151 	 * collector, we clear the gsave pointer now.
152 	 */
153 	vmsave->gsave = 0;
154 	/* Now it's safe to restore the state of memory. */
155 	code = alloc_restore_step_in(idmemory, asave);
156 	if (code < 0)
157 	    return code;
158 	last = code;
159     }
160     while (!last);
161     {
162 	uint space = icurrent_space;
163 
164 	ialloc_set_space(idmemory, avm_local);
165 	ifree_object(vmsave, "zrestore");
166 	ialloc_set_space(idmemory, space);
167     }
168     dict_set_top();		/* reload dict stack cache */
169     if (I_VALIDATE_AFTER_RESTORE)
170 	ivalidate_clean_spaces(i_ctx_p);
171     /* If the i_ctx_p LockFilePermissions is true, but the userparams */
172     /* we just restored is false, we need to make sure that we do not */
173     /* cause an 'invalidaccess' in setuserparams. Temporarily set     */
174     /* LockFilePermissions false until the gs_lev2.ps can do a        */
175     /* setuserparams from the restored userparam dictionary.          */
176     i_ctx_p->LockFilePermissions = false;
177     return 0;
178 }
179 /* Check the operand of a restore. */
180 static int
restore_check_operand(os_ptr op,alloc_save_t ** pasave,gs_dual_memory_t * idmem)181 restore_check_operand(os_ptr op, alloc_save_t ** pasave,
182 		      gs_dual_memory_t *idmem)
183 {
184     vm_save_t *vmsave;
185     ulong sid;
186     alloc_save_t *asave;
187 
188     check_type(*op, t_save);
189     vmsave = r_ptr(op, vm_save_t);
190     if (vmsave == 0)		/* invalidated save */
191 	return_error(e_invalidrestore);
192     sid = op->value.saveid;
193     asave = alloc_find_save(idmem, sid);
194     if (asave == 0)
195 	return_error(e_invalidrestore);
196     *pasave = asave;
197     return 0;
198 }
199 /* Check a stack to make sure all its elements are older than a save. */
200 static int
restore_check_stack(const i_ctx_t * i_ctx_p,const ref_stack_t * pstack,const alloc_save_t * asave,bool is_estack)201 restore_check_stack(const i_ctx_t *i_ctx_p, const ref_stack_t * pstack,
202 		    const alloc_save_t * asave, bool is_estack)
203 {
204     ref_stack_enum_t rsenum;
205 
206     ref_stack_enum_begin(&rsenum, pstack);
207     do {
208 	const ref *stkp = rsenum.ptr;
209 	uint size = rsenum.size;
210 
211 	for (; size; stkp++, size--) {
212 	    const void *ptr;
213 
214 	    switch (r_type(stkp)) {
215 		case t_array:
216 		    /*
217 		     * Zero-length arrays are a special case: see the
218 		     * t_*array case (label rr:) in igc.c:gc_trace.
219 		     */
220 		    if (r_size(stkp) == 0) {
221 			/*stkp->value.refs = (void *)0;*/
222 			continue;
223 		    }
224 		    ptr = stkp->value.refs;
225 		    break;
226 		case t_dictionary:
227 		    ptr = stkp->value.pdict;
228 		    break;
229 		case t_file:
230 		    /* Don't check executable or closed literal */
231 		    /* files on the e-stack. */
232 		    {
233 			stream *s;
234 
235 			if (is_estack &&
236 			    (r_has_attr(stkp, a_executable) ||
237 			     file_is_invalid(s, stkp))
238 			    )
239 			    continue;
240 		    }
241 		    ptr = stkp->value.pfile;
242 		    break;
243 		case t_name:
244 		    /* Names are special because of how they are allocated. */
245 		    if (alloc_name_is_since_save((const gs_memory_t *)pstack->memory,
246 						 stkp, asave))
247 			return_error(e_invalidrestore);
248 		    continue;
249 		case t_string:
250 		    /* Don't check empty executable strings */
251 		    /* on the e-stack. */
252 		    if (r_size(stkp) == 0 &&
253 			r_has_attr(stkp, a_executable) && is_estack
254 			)
255 			continue;
256 		    ptr = stkp->value.bytes;
257 		    break;
258 		case t_mixedarray:
259 		case t_shortarray:
260 		    /* See the t_array case above. */
261 		    if (r_size(stkp) == 0) {
262 			/*stkp->value.packed = (void *)0;*/
263 			continue;
264 		    }
265 		    ptr = stkp->value.packed;
266 		    break;
267 		case t_device:
268 		    ptr = stkp->value.pdevice;
269 		    break;
270 		case t_fontID:
271 		case t_struct:
272 		case t_astruct:
273 		    ptr = stkp->value.pstruct;
274 		    break;
275 		case t_save:
276 		    /* See the comment in isave.h regarding the following. */
277 		    if (i_ctx_p->language_level <= 2)
278 			continue;
279 		    ptr = alloc_find_save(&gs_imemory, stkp->value.saveid);
280 		    /*
281 		     * Invalid save objects aren't supposed to be possible
282 		     * in LL3, but just in case....
283 		     */
284 		    if (ptr == 0)
285 			return_error(e_invalidrestore);
286 		    if (ptr == asave)
287 			continue;
288 		    break;
289 		default:
290 		    continue;
291 	    }
292 	    if (alloc_is_since_save(ptr, asave))
293 		return_error(e_invalidrestore);
294 	}
295     } while (ref_stack_enum_next(&rsenum));
296     return 0;		/* OK */
297 }
298 /*
299  * If the new save level is zero, fix up the contents of a stack
300  * by clearing the l_new bit in all the entries (since we can't tolerate
301  * values with l_new set if the save level is zero).
302  * Also, in any case, fix up the e-stack by replacing empty executable
303  * strings and closed executable files that are newer than the save
304  * with canonical ones that aren't.
305  *
306  * Note that this procedure is only called if restore_check_stack succeeded.
307  */
308 static void
restore_fix_stack(ref_stack_t * pstack,const alloc_save_t * asave,bool is_estack)309 restore_fix_stack(ref_stack_t * pstack, const alloc_save_t * asave,
310 		  bool is_estack)
311 {
312     ref_stack_enum_t rsenum;
313 
314     ref_stack_enum_begin(&rsenum, pstack);
315     do {
316 	ref *stkp = rsenum.ptr;
317 	uint size = rsenum.size;
318 
319 	for (; size; stkp++, size--) {
320 	    r_clear_attrs(stkp, l_new);		/* always do it, no harm */
321 	    if (is_estack) {
322 		ref ofile;
323 
324 		ref_assign(&ofile, stkp);
325 		switch (r_type(stkp)) {
326 		    case t_string:
327 			if (r_size(stkp) == 0 &&
328 			    alloc_is_since_save(stkp->value.bytes,
329 						asave)
330 			    ) {
331 			    make_empty_const_string(stkp,
332 						    avm_foreign);
333 			    break;
334 			}
335 			continue;
336 		    case t_file:
337 			if (alloc_is_since_save(stkp->value.pfile,
338 						asave)
339 			    ) {
340 			    make_invalid_file(stkp);
341 			    break;
342 			}
343 			continue;
344 		    default:
345 			continue;
346 		}
347 		r_copy_attrs(stkp, a_all | a_executable,
348 			     &ofile);
349 	    }
350 	}
351     } while (ref_stack_enum_next(&rsenum));
352 }
353 
354 /* - vmstatus <save_level> <vm_used> <vm_maximum> */
355 static int
zvmstatus(i_ctx_t * i_ctx_p)356 zvmstatus(i_ctx_t *i_ctx_p)
357 {
358     os_ptr op = osp;
359     gs_memory_status_t mstat, dstat;
360 
361     gs_memory_status(imemory, &mstat);
362     if (imemory == imemory_global) {
363 	gs_memory_status_t sstat;
364 
365 	gs_memory_status(imemory_system, &sstat);
366 	mstat.allocated += sstat.allocated;
367 	mstat.used += sstat.used;
368     }
369     gs_memory_status(imemory->non_gc_memory, &dstat);
370     push(3);
371     make_int(op - 2, imemory_save_level(iimemory_local));
372     make_int(op - 1, mstat.used);
373     make_int(op, mstat.allocated + dstat.allocated - dstat.used);
374     return 0;
375 }
376 
377 /* ------ Non-standard extensions ------ */
378 
379 /* <save> .forgetsave - */
380 static int
zforgetsave(i_ctx_t * i_ctx_p)381 zforgetsave(i_ctx_t *i_ctx_p)
382 {
383     os_ptr op = osp;
384     alloc_save_t *asave;
385     vm_save_t *vmsave;
386     int code = restore_check_operand(op, &asave, idmemory);
387 
388     if (code < 0)
389 	return 0;
390     vmsave = alloc_save_client_data(asave);
391     /* Reset l_new in all stack entries if the new save level is zero. */
392     restore_fix_stack(&o_stack, asave, false);
393     restore_fix_stack(&e_stack, asave, false);
394     restore_fix_stack(&d_stack, asave, false);
395     /*
396      * Forget the gsaves, by deleting the bottom gstate on
397      * the current stack and the top one on the saved stack and then
398      * concatenating the stacks together.
399      */
400     {
401 	gs_state *pgs = igs;
402 	gs_state *last;
403 
404 	while (gs_state_saved(last = gs_state_saved(pgs)) != 0)
405 	    pgs = last;
406 	gs_state_swap_saved(last, vmsave->gsave);
407 	gs_grestore(last);
408 	gs_grestore(last);
409     }
410     /* Forget the save in the memory manager. */
411     code = alloc_forget_save_in(idmemory, asave);
412     if (code < 0)
413 	return code;
414     {
415 	uint space = icurrent_space;
416 
417 	ialloc_set_space(idmemory, avm_local);
418 	/* See above for why we clear the gsave pointer here. */
419 	vmsave->gsave = 0;
420 	ifree_object(vmsave, "zrestore");
421 	ialloc_set_space(idmemory, space);
422     }
423     pop(1);
424     return 0;
425 }
426 
427 /* ------ Initialization procedure ------ */
428 
429 const op_def zvmem_op_defs[] =
430 {
431     {"1.forgetsave", zforgetsave},
432     {"1restore", zrestore},
433     {"0save", zsave},
434     {"0vmstatus", zvmstatus},
435     op_def_end(0)
436 };
437