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