diff options
author | Thomas Deutschmann <whissi@gentoo.org> | 2019-10-15 12:24:12 +0200 |
---|---|---|
committer | Thomas Deutschmann <whissi@gentoo.org> | 2020-08-13 11:26:55 +0200 |
commit | e088156d5b620e5e639580dacf85c6dc13823c74 (patch) | |
tree | 57f5c025e203279944da512166c20bc0521d8ccd /psi/zvmem.c | |
download | ghostscript-gpl-patches-e088156d5b620e5e639580dacf85c6dc13823c74.tar.gz ghostscript-gpl-patches-e088156d5b620e5e639580dacf85c6dc13823c74.tar.bz2 ghostscript-gpl-patches-e088156d5b620e5e639580dacf85c6dc13823c74.zip |
Import Ghostscript 9.50ghostscript-9.50
Signed-off-by: Thomas Deutschmann <whissi@gentoo.org>
Diffstat (limited to 'psi/zvmem.c')
-rw-r--r-- | psi/zvmem.c | 470 |
1 files changed, 470 insertions, 0 deletions
diff --git a/psi/zvmem.c b/psi/zvmem.c new file mode 100644 index 00000000..b6e366fa --- /dev/null +++ b/psi/zvmem.c @@ -0,0 +1,470 @@ +/* Copyright (C) 2001-2019 Artifex Software, Inc. + All Rights Reserved. + + This software is provided AS-IS with no warranty, either express or + implied. + + This software is distributed under license and may not be copied, + modified or distributed except as expressly authorized under the terms + of the license contained in the file LICENSE in this distribution. + + Refer to licensing information at http://www.artifex.com or contact + Artifex Software, Inc., 1305 Grant Avenue - Suite 200, Novato, + CA 94945, U.S.A., +1(415)492-9861, for further information. +*/ + + +/* "Virtual memory" operators */ +#include "stat_.h" /* get system header early to avoid name clash on Cygwin */ +#include "ghost.h" +#include "gsstruct.h" +#include "oper.h" +#include "estack.h" /* for checking in restore */ +#include "ialloc.h" +#include "idict.h" /* ditto */ +#include "igstate.h" +#include "isave.h" +#include "dstack.h" +#include "stream.h" /* for files.h */ +#include "files.h" /* for e-stack processing */ +#include "store.h" +#include "gsmatrix.h" /* for gsstate.h */ +#include "gsstate.h" + +/* Define whether we validate memory before/after save/restore. */ +/* Note that we only actually do this if DEBUG is set and -Z? is selected. */ +static const bool I_VALIDATE_BEFORE_SAVE = true; +static const bool I_VALIDATE_AFTER_SAVE = true; +static const bool I_VALIDATE_BEFORE_RESTORE = true; +static const bool I_VALIDATE_AFTER_RESTORE = true; + +gs_private_st_ptrs1(st_vm_save, vm_save_t, "savetype", + vm_save_enum_ptrs, vm_save_reloc_ptrs, gsave); + +/* Clean up the stacks and validate storage. */ +void +ivalidate_clean_spaces(i_ctx_t *i_ctx_p) +{ + if (gs_debug_c('?')) { + ref_stack_cleanup(&d_stack); + ref_stack_cleanup(&e_stack); + ref_stack_cleanup(&o_stack); + ivalidate_spaces(); + } +} + +/* - save <save> */ +int +zsave(i_ctx_t *i_ctx_p) +{ + os_ptr op = osp; + uint space = icurrent_space; + vm_save_t *vmsave; + ulong sid; + int code; + gs_gstate *prev; + + if (I_VALIDATE_BEFORE_SAVE) + ivalidate_clean_spaces(i_ctx_p); + ialloc_set_space(idmemory, avm_local); + vmsave = ialloc_struct(vm_save_t, &st_vm_save, "zsave"); + ialloc_set_space(idmemory, space); + if (vmsave == 0) + return_error(gs_error_VMerror); + vmsave->gsave = NULL; /* Ensure constructed enough to destroy safely */ + code = alloc_save_state(idmemory, vmsave, &sid); + if (code < 0) + return code; + if (sid == 0) { + ifree_object(vmsave, "zsave"); + return_error(gs_error_VMerror); + } + if_debug2m('u', imemory, "[u]vmsave 0x%lx, id = %lu\n", + (ulong) vmsave, (ulong) sid); + code = gs_gsave_for_save(igs, &prev); + if (code < 0) + return code; + code = gs_gsave(igs); + if (code < 0) + return code; + vmsave->gsave = prev; + push(1); + make_tav(op, t_save, 0, saveid, sid); + if (I_VALIDATE_AFTER_SAVE) + ivalidate_clean_spaces(i_ctx_p); + return 0; +} + +/* <save> restore - */ +static int restore_check_operand(os_ptr, alloc_save_t **, gs_dual_memory_t *); +static int restore_check_stack(const i_ctx_t *i_ctx_p, const ref_stack_t *, const alloc_save_t *, bool); +static void restore_fix_stack(i_ctx_t *i_ctx_p, ref_stack_t *, const alloc_save_t *, bool); + +/* Do as many up front checks of the save object as we reasonably can */ +int +restore_check_save(i_ctx_t *i_ctx_p, alloc_save_t **asave) +{ + os_ptr op = osp; + int code = restore_check_operand(op, asave, idmemory); + + if (code < 0) + return code; + if_debug2m('u', imemory, "[u]vmrestore 0x%lx, id = %lu\n", + (ulong) alloc_save_client_data(*asave), + (ulong) op->value.saveid); + if (I_VALIDATE_BEFORE_RESTORE) + ivalidate_clean_spaces(i_ctx_p); + /* Check the contents of the stacks. */ + osp--; + { + int code; + + if ((code = restore_check_stack(i_ctx_p, &o_stack, *asave, false)) < 0 || + (code = restore_check_stack(i_ctx_p, &e_stack, *asave, true)) < 0 || + (code = restore_check_stack(i_ctx_p, &d_stack, *asave, false)) < 0 + ) { + osp++; + return code; + } + } + osp++; + return 0; +} + +/* the semantics of restore differ slightly between Level 1 and + Level 2 and later - the latter includes restoring the device + state (whilst Level 1 didn't have "page devices" as such). + Hence we have two restore operators - one here (Level 1) + and one in zdevice2.c (Level 2+). For that reason, the + operand checking and guts of the restore operation are + separated so both implementations can use them to best + effect. + */ +int +dorestore(i_ctx_t *i_ctx_p, alloc_save_t *asave) +{ + bool last; + vm_save_t *vmsave; + int code; + + osp--; + + /* Reset l_new in all stack entries if the new save level is zero. */ + /* Also do some special fixing on the e-stack. */ + restore_fix_stack(i_ctx_p, &o_stack, asave, false); + restore_fix_stack(i_ctx_p, &e_stack, asave, true); + restore_fix_stack(i_ctx_p, &d_stack, asave, false); + /* Iteratively restore the state of memory, */ + /* also doing a grestoreall at each step. */ + do { + vmsave = alloc_save_client_data(alloc_save_current(idmemory)); + /* Restore the graphics state. */ + gs_grestoreall_for_restore(igs, vmsave->gsave); + /* + * If alloc_save_space decided to do a second save, the vmsave + * object was allocated one save level less deep than the + * current level, so ifree_object won't actually free it; + * however, it points to a gsave object that definitely + * *has* been freed. In order not to trip up the garbage + * collector, we clear the gsave pointer now. + */ + vmsave->gsave = 0; + /* Now it's safe to restore the state of memory. */ + code = alloc_restore_step_in(idmemory, asave); + if (code < 0) + return code; + last = code; + } + while (!last); + { + uint space = icurrent_space; + + ialloc_set_space(idmemory, avm_local); + ifree_object(vmsave, "zrestore"); + ialloc_set_space(idmemory, space); + } + dict_set_top(); /* reload dict stack cache */ + if (I_VALIDATE_AFTER_RESTORE) + ivalidate_clean_spaces(i_ctx_p); + /* If the i_ctx_p LockFilePermissions is true, but the userparams */ + /* we just restored is false, we need to make sure that we do not */ + /* cause an 'invalidaccess' in setuserparams. Temporarily set */ + /* LockFilePermissions false until the gs_lev2.ps can do a */ + /* setuserparams from the restored userparam dictionary. */ + /* NOTE: This is safe to do here, since the restore has */ + /* successfully completed - this should never come before any */ + /* operation that can trigger an error */ + i_ctx_p->LockFilePermissions = false; + return 0; +} + +int +zrestore(i_ctx_t *i_ctx_p) +{ + alloc_save_t *asave; + int code = restore_check_save(i_ctx_p, &asave); + if (code < 0) + return code; + + return dorestore(i_ctx_p, asave); +} + +/* Check the operand of a restore. */ +static int +restore_check_operand(os_ptr op, alloc_save_t ** pasave, + gs_dual_memory_t *idmem) +{ + vm_save_t *vmsave; + ulong sid; + alloc_save_t *asave; + + check_type(*op, t_save); + vmsave = r_ptr(op, vm_save_t); + if (vmsave == 0) /* invalidated save */ + return_error(gs_error_invalidrestore); + sid = op->value.saveid; + asave = alloc_find_save(idmem, sid); + if (asave == 0) + return_error(gs_error_invalidrestore); + *pasave = asave; + return 0; +} + +/* Check a stack to make sure all its elements are older than a save. */ +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) +{ + ref_stack_enum_t rsenum; + + ref_stack_enum_begin(&rsenum, pstack); + do { + const ref *stkp = rsenum.ptr; + uint size = rsenum.size; + + for (; size; stkp++, size--) { + const void *ptr; + + switch (r_type(stkp)) { + case t_array: + /* + * Zero-length arrays are a special case: see the + * t_*array case (label rr:) in igc.c:gc_trace. + */ + if (r_size(stkp) == 0) { + /*stkp->value.refs = (void *)0;*/ + continue; + } + ptr = stkp->value.refs; + break; + case t_dictionary: + ptr = stkp->value.pdict; + break; + case t_file: + /* Don't check executable or closed literal */ + /* files on the e-stack. */ + { + stream *s; + + if (is_estack && + (r_has_attr(stkp, a_executable) || + file_is_invalid(s, stkp)) + ) + continue; + } + ptr = stkp->value.pfile; + break; + case t_name: + /* Names are special because of how they are allocated. */ + if (alloc_name_is_since_save((const gs_memory_t *)pstack->memory, + stkp, asave)) + return_error(gs_error_invalidrestore); + continue; + case t_string: + /* Don't check empty executable strings */ + /* on the e-stack. */ + if (r_size(stkp) == 0 && + r_has_attr(stkp, a_executable) && is_estack + ) + continue; + ptr = stkp->value.bytes; + break; + case t_mixedarray: + case t_shortarray: + /* See the t_array case above. */ + if (r_size(stkp) == 0) { + /*stkp->value.packed = (void *)0;*/ + continue; + } + ptr = stkp->value.packed; + break; + case t_device: + ptr = stkp->value.pdevice; + break; + case t_fontID: + case t_struct: + case t_astruct: + ptr = stkp->value.pstruct; + break; + case t_save: + /* See the comment in isave.h regarding the following. */ + if (i_ctx_p->language_level <= 2) + continue; + ptr = alloc_find_save(&gs_imemory, stkp->value.saveid); + /* + * Invalid save objects aren't supposed to be possible + * in LL3, but just in case.... + */ + if (ptr == 0) + return_error(gs_error_invalidrestore); + if (ptr == asave) + continue; + break; + default: + continue; + } + if (alloc_is_since_save(ptr, asave)) + return_error(gs_error_invalidrestore); + } + } while (ref_stack_enum_next(&rsenum)); + return 0; /* OK */ +} +/* + * If the new save level is zero, fix up the contents of a stack + * by clearing the l_new bit in all the entries (since we can't tolerate + * values with l_new set if the save level is zero). + * Also, in any case, fix up the e-stack by replacing empty executable + * strings and closed executable files that are newer than the save + * with canonical ones that aren't. + * + * Note that this procedure is only called if restore_check_stack succeeded. + */ +static void +restore_fix_stack(i_ctx_t *i_ctx_p, ref_stack_t * pstack, + const alloc_save_t * asave, bool is_estack) +{ + ref_stack_enum_t rsenum; + + ref_stack_enum_begin(&rsenum, pstack); + do { + ref *stkp = rsenum.ptr; + uint size = rsenum.size; + + for (; size; stkp++, size--) { + r_clear_attrs(stkp, l_new); /* always do it, no harm */ + if (is_estack) { + ref ofile; + + ref_assign(&ofile, stkp); + switch (r_type(stkp)) { + case t_string: + if (r_size(stkp) == 0 && + alloc_is_since_save(stkp->value.bytes, + asave) + ) { + make_empty_const_string(stkp, + avm_foreign); + break; + } + continue; + case t_file: + if (alloc_is_since_save(stkp->value.pfile, + asave) + ) { + make_invalid_file(i_ctx_p, stkp); + break; + } + continue; + default: + continue; + } + r_copy_attrs(stkp, a_all | a_executable, + &ofile); + } + } + } while (ref_stack_enum_next(&rsenum)); +} + +/* - vmstatus <save_level> <vm_used> <vm_maximum> */ +static int +zvmstatus(i_ctx_t *i_ctx_p) +{ + os_ptr op = osp; + gs_memory_status_t mstat, dstat; + + gs_memory_status(imemory, &mstat); + if (imemory == imemory_global) { + gs_memory_status_t sstat; + + gs_memory_status(imemory_system, &sstat); + mstat.allocated += sstat.allocated; + mstat.used += sstat.used; + } + gs_memory_status(imemory->non_gc_memory, &dstat); + push(3); + make_int(op - 2, imemory_save_level(iimemory_local)); + make_int(op - 1, mstat.used); + make_int(op, mstat.allocated + dstat.allocated - dstat.used); + return 0; +} + +/* ------ Non-standard extensions ------ */ + +/* <save> .forgetsave - */ +static int +zforgetsave(i_ctx_t *i_ctx_p) +{ + os_ptr op = osp; + alloc_save_t *asave; + vm_save_t *vmsave; + int code = restore_check_operand(op, &asave, idmemory); + + if (code < 0) + return 0; + vmsave = alloc_save_client_data(asave); + /* Reset l_new in all stack entries if the new save level is zero. */ + restore_fix_stack(i_ctx_p, &o_stack, asave, false); + restore_fix_stack(i_ctx_p, &e_stack, asave, false); + restore_fix_stack(i_ctx_p, &d_stack, asave, false); + /* + * Forget the gsaves, by deleting the bottom gstate on + * the current stack and the top one on the saved stack and then + * concatenating the stacks together. + */ + { + gs_gstate *pgs = igs; + gs_gstate *last; + + while (gs_gstate_saved(last = gs_gstate_saved(pgs)) != 0) + pgs = last; + gs_gstate_swap_saved(last, vmsave->gsave); + gs_grestore(last); + gs_grestore(last); + } + /* Forget the save in the memory manager. */ + code = alloc_forget_save_in(idmemory, asave); + if (code < 0) + return code; + { + uint space = icurrent_space; + + ialloc_set_space(idmemory, avm_local); + /* See above for why we clear the gsave pointer here. */ + vmsave->gsave = 0; + ifree_object(vmsave, "zrestore"); + ialloc_set_space(idmemory, space); + } + pop(1); + return 0; +} + +/* ------ Initialization procedure ------ */ + +const op_def zvmem_op_defs[] = +{ + {"1.forgetsave", zforgetsave}, + {"1restore", zrestore}, + {"0save", zsave}, + {"0vmstatus", zvmstatus}, + op_def_end(0) +}; |