Blob Blame History Raw
From 30cd347f37bfb293ffdc407397d1023628400b81 Mon Sep 17 00:00:00 2001
From: Ken Sharp <ken.sharp@artifex.com>
Date: Mon, 15 Oct 2018 13:35:15 +0100
Subject: [PATCH 1/6] font parsing - prevent SEGV in .cffparse

Bug #699961 "currentcolortransfer procs crash .parsecff"

zparsecff checked the operand for being an array (and not a packed
array) but the returned procedures from the default currentcolortransfer
are arrays, not packed arrays. This led to the code trying to
dereference a NULL pointer.

Add a specific check for the 'refs' pointer being NULL before we try
to use it.

Additionally, make the StartData procedure in the CFF Font Resource
executeonly to prevent pulling the hidden .parsecff operator out and
using it. Finally, extend this to other resource types.
---
 Resource/Init/gs_cff.ps   |  4 +--
 Resource/Init/gs_cidcm.ps |  6 ++--
 Resource/Init/gs_ciddc.ps |  4 +--
 Resource/Init/gs_cmap.ps  |  2 +-
 Resource/Init/gs_res.ps   | 60 +++++++++++++++++++--------------------
 psi/zfont2.c              |  4 +++
 6 files changed, 42 insertions(+), 38 deletions(-)

diff --git a/Resource/Init/gs_cff.ps b/Resource/Init/gs_cff.ps
index 20c35a5..b60e374 100644
--- a/Resource/Init/gs_cff.ps
+++ b/Resource/Init/gs_cff.ps
@@ -199,7 +199,7 @@ def
 % ordinary CFF font.
 /StartData {          % <resname> <nbytes> StartData -
   currentfile exch subfilefilter //false //false ReadData pop
-} bind def
+} bind executeonly def
 /ReadData {           % <resname> <file> <forceresname> <forcecid> ReadData <fontset>
         % Initialize.
 
@@ -234,7 +234,7 @@ def
   end		% FontSetInit ProcSet
   /FontSet defineresource
 
-} bind def
+} bind executeonly def
 
 % ---------------- Resource category definition ---------------- %
 
diff --git a/Resource/Init/gs_cidcm.ps b/Resource/Init/gs_cidcm.ps
index 0201ea8..60b0fdb 100644
--- a/Resource/Init/gs_cidcm.ps
+++ b/Resource/Init/gs_cidcm.ps
@@ -327,7 +327,7 @@ currentdict end def
       //FindResource exec
     } ifelse
   } ifelse
-} bind def
+} bind executeonly def
 
 /ResourceStatus {  % <InstName> ResourceStatus <nStatus> <nSize> true
                    % <InstName> ResourceStatus false
@@ -359,7 +359,7 @@ currentdict end def
       //false
     } ifelse
   } ifelse
-} bind def
+} bind executeonly def
 
 /ResourceForAll { % <template> <proc> <scratch> ResourceForAll -
 
@@ -440,7 +440,7 @@ currentdict end def
 
   % Make the enumerator and apply it :
   /MappedCategoryRedefiner /ProcSet findresource /MakeResourceEnumerator get exec exec
-} bind def
+} bind executeonly def
 
 currentdict end /Font exch /Category defineresource pop
 end
diff --git a/Resource/Init/gs_ciddc.ps b/Resource/Init/gs_ciddc.ps
index 54c6876..f83e960 100644
--- a/Resource/Init/gs_ciddc.ps
+++ b/Resource/Init/gs_ciddc.ps
@@ -204,7 +204,7 @@ begin
     exch pop begin                                   %
     .GetCIDDecoding
     end
-  } bind def
+  } bind executeonly def
 
   /FindResource      % <name> FindResource <dict>
   { currentglobal exch                               % bGlobal /InstName
@@ -212,7 +212,7 @@ begin
     dup //.MakeInstance exec                         % bGlobal /InstName <Inst>
     DefineResource                                   % bGlobal <Inst>
     exch setglobal                                   % <Inst>
-  } bind def
+  } bind executeonly def
 
 currentdict end
 /CIDDecoding exch /Category defineresource pop
diff --git a/Resource/Init/gs_cmap.ps b/Resource/Init/gs_cmap.ps
index 63c023b..a01afc2 100644
--- a/Resource/Init/gs_cmap.ps
+++ b/Resource/Init/gs_cmap.ps
@@ -536,7 +536,7 @@ dup /DefineResource {
   } if
   dup /CodeMap .knownget { //null eq { .buildcmap } if } if
   /Generic /Category findresource /DefineResource get exec
-} bind put
+} bind executeonly put
 /Category defineresource pop
         % We might have loaded CID font support already.
 /CIDInit /ProcSet 2 copy { findresource } .internalstopped
diff --git a/Resource/Init/gs_res.ps b/Resource/Init/gs_res.ps
index d1e10c9..9bdb8ff 100644
--- a/Resource/Init/gs_res.ps
+++ b/Resource/Init/gs_res.ps
@@ -155,10 +155,10 @@ setglobal
         } {
           /defineresource cvx /typecheck signaloperror
         } ifelse
-} bind def
+} bind executeonly def
 /FindResource		% (redefined below)
         { .Instances exch get 0 get
-        } bind def
+        } bind executeonly def
 
                 % Additional entries
 
@@ -210,7 +210,7 @@ def
       /findresource .systemvar /typecheck signalerror
     } if
     /findresource cvx //.findresource .errorexec
-} odef
+} bind executeonly odef
 
 /defineresource {	% <key> <instance> <category> defineresource <instance>
     2 .argindex 2 index 2 index	% catch stackunderflow
@@ -226,7 +226,7 @@ def
         /DefineResource .resourceexec
         4 1 roll pop pop pop
     } .errorexec
-} bind odef
+} bind executeonly odef
 % We must prevent resourceforall from automatically restoring the stacks,
 % because we don't want the stacks restored if proc causes an error or
 % executes a 'stop'. On the other hand, resourceforall is defined in the
@@ -240,10 +240,10 @@ def
         % Stack: <template> <proc> <scratch> <category> proc
         exch pop		% pop the category
         exec end
-} bind def
+} bind executeonly def
 /resourceforall {	% <template> <proc> <scratch> <category> resourceforall1 -
         //resourceforall1 exec		% see above
-} bind odef
+} bind executeonly odef
 /resourcestatus {	% <key> <category> resourcestatus <status> <size> true
                         % <key> <category> resourcestatus false
   {
@@ -259,7 +259,7 @@ def
     % for error reporting. CET 23-26
     /resourcestatus cvx $error /errorname get signalerror
   } if
-} bind odef
+} bind executeonly odef
 /undefineresource {	% <key> <category> undefineresource -
   0 .argindex type /nametype ne {
     /undefinedresource cvx /typecheck signaloperror
@@ -272,7 +272,7 @@ def
     % here but uses operator for the errors above. CET 23-33
     /undefineresource cvx $error /errorname get signalerror
   } if
-} bind odef
+} bind executeonly odef
 
 % Define the system parameters used for the Generic implementation of
 % ResourceFileName.
@@ -457,7 +457,7 @@ status {
            { 2 copy .Instances exch exec
            }
           if .LocalInstances exch exec
-        } bind
+        } bind executeonly
 % Because of some badly designed code in Adobe's CID font downloader that
 % makes findresource and resourcestatus deliberately inconsistent with each
 % other, the default FindResource must not call ResourceStatus if there is
@@ -483,7 +483,7 @@ status {
            /findresource cvx .undefinedresource
           } ifelse
         } ifelse
-} bind
+} bind executeonly
 % Because of some badly designed code in Adobe's CID font downloader, the
 % definition of ResourceStatus for Generic and Font must be the same (!).
 % We patch around this by using an intermediate .ResourceFileStatus procedure.
@@ -493,10 +493,10 @@ status {
         } {
           .ResourceFileStatus
         } ifelse
-} bind
+} bind executeonly
 /.ResourceFileStatus {
         .ResourceFile { closefile 2 -1 //true } { pop //false } ifelse
-} bind
+} bind executeonly
 /ResourceForAll {
                 % Construct a new procedure to hold the arguments.
                 % All objects constructed here must be in local VM to avoid
@@ -554,7 +554,7 @@ status {
         3 2 roll pop % args
         { forall } 0 get
         currentdict end 2 .execn begin
-} bind
+} bind executeonly
 
 /ResourceFileName  {                          % /in (scr) --> (p/c/n)
   exch //.rfnstring cvs                       % (scr) (n)
@@ -586,7 +586,7 @@ status {
     ifelse
   } ifelse
   exch copy                                   % (p/c/n)
-} bind
+} bind executeonly
 
                 % Additional entries
 
@@ -752,17 +752,17 @@ counttomark 2 idiv
              ifelse
            }
           ifelse
-        } bind
+        } bind executeonly
    /UndefineResource
-        { /undefineresource cvx /invalidaccess signaloperror } bind
+        { /undefineresource cvx /invalidaccess signaloperror } bind executeonly
    /FindResource
         { .Instances 1 index .knownget
            { exch pop }
            { /findresource cvx .undefinedresource }
           ifelse
-        } bind
+        } bind executeonly
    /ResourceStatus
-        { .Instances exch known { 0 0 //true } { //false } ifelse } bind
+        { .Instances exch known { 0 0 //true } { //false } ifelse } bind executeonly
    /ResourceForAll
         /Generic .findcategory /ResourceForAll load end
 
@@ -865,7 +865,7 @@ userdict /.localcsdefaults //false put
     1 index .definedefaultcs
     currentglobal not { .userdict /.localcsdefaults //true put } if
   } if
-} bind
+} bind executeonly
 
 /UndefineResource {
   dup /Generic /Category findresource /UndefineResource get exec
@@ -888,7 +888,7 @@ userdict /.localcsdefaults //false put
   } {
     pop
   } ifelse
-} bind
+} bind executeonly
 
 .definecategory			% ColorSpace
 
@@ -918,7 +918,7 @@ userdict /.localcsdefaults //false put
     { exch copy exch pop }
     { /Generic /Category findresource /ResourceFileName get exec }
    ifelse
- } bind
+ } bind executeonly
 
 .definecategory			% Encoding
 
@@ -991,11 +991,11 @@ currentdict /.fontstatusaux .undef
 /DefineResource
         { 2 copy //definefont exch pop
           /Generic /Category findresource /DefineResource get exec
-        } bind
+        } bind executeonly
 /UndefineResource
         { dup //undefinefont
           /Generic /Category findresource /UndefineResource get exec
-        } bind
+        } bind executeonly
 /FindResource {
         dup .getvminstance {
           exch pop 0 get
@@ -1006,14 +1006,14 @@ currentdict /.fontstatusaux .undef
             .loadfontresource
           } ifelse
         } ifelse
-} bind
+} bind executeonly
 /ResourceForAll {
         { .scannextfontdir not { exit } if } loop
         /Generic /Category findresource /ResourceForAll get exec
-} bind
+} bind executeonly
 /.ResourceFileStatus {
         .fontstatus { pop 2 -1 //true } { pop //false } ifelse
-} bind
+} bind executeonly
 
 /.loadfontresource {
         dup .vmused exch
@@ -1063,20 +1063,20 @@ end
   { /Font defineresource } stopped {
       /definefont cvx $error /errorname get signalerror
   } if
-} bind odef
+} bind executeonly odef
 /undefinefont {
   /Font undefineresource
-} bind odef
+} bind executeonly odef
 % The Red Book requires that findfont be a procedure, not an operator,
 % but it still needs to restore the stacks reliably if it fails.
 /.findfontop {
   { /Font findresource } stopped {
     pop /findfont $error /errorname get signalerror
   } if
-} bind odef
+} bind executeonly odef
 /findfont {
   .findfontop
-} bind def	% Must be a procedure, not an operator
+} bind executeonly def	% Must be a procedure, not an operator
 
 % Remove initialization utilities.
 currentdict /.definecategory .undef
diff --git a/psi/zfont2.c b/psi/zfont2.c
index bed6c81..3123f19 100644
--- a/psi/zfont2.c
+++ b/psi/zfont2.c
@@ -2745,9 +2745,13 @@ zparsecff(i_ctx_t *i_ctx_p)
     ref blk_wrap[1];
 
     check_read(*op);
+
     if (r_has_type(op, t_array)) {  /* no packedarrays */
         int i, blk_sz, blk_cnt;
 
+        if (op->value.refs == NULL)
+            return_error(gs_error_typecheck);
+
         data.blk_ref = op->value.refs;
         blk_cnt  = r_size(op);
         blk_sz = r_size(data.blk_ref);
-- 
2.17.2


From 8e18fcdaa2e2247363c4cc8f851f3096cc5756fa Mon Sep 17 00:00:00 2001
From: Chris Liddell <chris.liddell@artifex.com>
Date: Fri, 19 Oct 2018 13:14:24 +0100
Subject: [PATCH 2/6] "Hide" a final use of a .force* operator

There was one use of .forceput remaining that was in a regular procedure
rather than being "hidden" behind an operator.

In this case, it's buried in the resource machinery, and hard to access (I
would not be confident in claiming it was impossible). This ensures it's
not accessible.
---
 Resource/Init/gs_res.ps | 6 +++---
 1 file changed, 3 insertions(+), 3 deletions(-)

diff --git a/Resource/Init/gs_res.ps b/Resource/Init/gs_res.ps
index 9bdb8ff..8eb8bb0 100644
--- a/Resource/Init/gs_res.ps
+++ b/Resource/Init/gs_res.ps
@@ -155,7 +155,7 @@ setglobal
         } {
           /defineresource cvx /typecheck signaloperror
         } ifelse
-} bind executeonly def
+} bind executeonly odef
 /FindResource		% (redefined below)
         { .Instances exch get 0 get
         } bind executeonly def
@@ -412,7 +412,7 @@ status {
   } ifelse
 } bind def
 
-/DefineResource {
+/DefineResource dup {
         .CheckResource
            { dup [ exch 0 -1 ]
                         % Stack: key value instance
@@ -441,7 +441,7 @@ status {
            { /defineresource cvx /typecheck signaloperror
            }
         ifelse
-} .bind executeonly		% executeonly to prevent access to .forcedef
+} .bind executeonly .makeoperator		% executeonly to prevent access to .forcedef
 /UndefineResource
         {  { dup 2 index .knownget
               { dup 1 get 1 ge
-- 
2.17.2


From 13b0a36f8181db66a91bcc8cea139998b53a8996 Mon Sep 17 00:00:00 2001
From: Chris Liddell <chris.liddell@artifex.com>
Date: Wed, 5 Dec 2018 12:22:13 +0000
Subject: [PATCH 3/6] Sanitize op stack for error conditions

We save the stacks to an array and store the array for the error handler to
access.

For SAFER, we traverse the array, and deep copy any op arrays (procedures). As
we make these copies, we check for operators that do *not* exist in systemdict,
when we find one, we replace the operator with a name object (of the form
"/--opname--").
---
 psi/int.mak  |  3 +-
 psi/interp.c |  8 ++++++
 psi/istack.c | 78 ++++++++++++++++++++++++++++++++++++++++++++++++++++
 psi/istack.h |  3 ++
 4 files changed, 91 insertions(+), 1 deletion(-)

diff --git a/psi/int.mak b/psi/int.mak
index 6ab5bf0..6b349cb 100644
--- a/psi/int.mak
+++ b/psi/int.mak
@@ -204,7 +204,8 @@ $(PSOBJ)iparam.$(OBJ) : $(PSSRC)iparam.c $(GH)\
 $(PSOBJ)istack.$(OBJ) : $(PSSRC)istack.c $(GH) $(memory__h)\
  $(ierrors_h) $(gsstruct_h) $(gsutil_h)\
  $(ialloc_h) $(istack_h) $(istkparm_h) $(istruct_h) $(iutil_h) $(ivmspace_h)\
- $(store_h) $(INT_MAK) $(MAKEDIRS)
+ $(store_h) $(icstate_h) $(iname_h) $(dstack_h) $(idict_h) \
+ $(INT_MAK) $(MAKEDIRS)
 	$(PSCC) $(PSO_)istack.$(OBJ) $(C_) $(PSSRC)istack.c
 
 $(PSOBJ)iutil.$(OBJ) : $(PSSRC)iutil.c $(GH) $(math__h) $(memory__h) $(string__h)\
diff --git a/psi/interp.c b/psi/interp.c
index 6dc0dda..aa5779c 100644
--- a/psi/interp.c
+++ b/psi/interp.c
@@ -761,6 +761,7 @@ copy_stack(i_ctx_t *i_ctx_p, const ref_stack_t * pstack, int skip, ref * arr)
     uint size = ref_stack_count(pstack) - skip;
     uint save_space = ialloc_space(idmemory);
     int code, i;
+    ref *safety, *safe;
 
     if (size > 65535)
         size = 65535;
@@ -778,6 +779,13 @@ copy_stack(i_ctx_t *i_ctx_p, const ref_stack_t * pstack, int skip, ref * arr)
                 make_null(&arr->value.refs[i]);
         }
     }
+    if (pstack == &o_stack && dict_find_string(systemdict, "SAFETY", &safety) > 0 &&
+        dict_find_string(safety, "safe", &safe) > 0 && r_has_type(safe, t_boolean) &&
+        safe->value.boolval == true) {
+        code = ref_stack_array_sanitize(i_ctx_p, arr, arr);
+        if (code < 0)
+            return code;
+    }
     ialloc_set_space(idmemory, save_space);
     return code;
 }
diff --git a/psi/istack.c b/psi/istack.c
index 8fe151f..f1a3e51 100644
--- a/psi/istack.c
+++ b/psi/istack.c
@@ -27,6 +27,10 @@
 #include "iutil.h"
 #include "ivmspace.h"		/* for local/global test */
 #include "store.h"
+#include "icstate.h"
+#include "iname.h"
+#include "dstack.h"
+#include "idict.h"
 
 /* Forward references */
 static void init_block(ref_stack_t *pstack, const ref *pblock_array,
@@ -294,6 +298,80 @@ ref_stack_store_check(const ref_stack_t *pstack, ref *parray, uint count,
     return 0;
 }
 
+int
+ref_stack_array_sanitize(i_ctx_t *i_ctx_p, ref *sarr, ref *darr)
+{
+    int i, code;
+    ref obj, arr2;
+    ref *pobj2;
+    gs_memory_t *mem = (gs_memory_t *)idmemory->current;
+
+    if (!r_is_array(sarr) || !r_has_type(darr, t_array))
+        return_error(gs_error_typecheck);
+
+    for (i = 0; i < r_size(sarr); i++) {
+        code = array_get(mem, sarr, i, &obj);
+        if (code < 0)
+            make_null(&obj);
+        switch(r_type(&obj)) {
+          case t_operator:
+          {
+            int index = op_index(&obj);
+
+            if (index > 0 && index < op_def_count) {
+                const byte *data = (const byte *)(op_index_def(index)->oname + 1);
+                if (dict_find_string(systemdict, (const char *)data, &pobj2) <= 0) {
+                    byte *s = gs_alloc_bytes(mem, strlen((char *)data) + 5, "ref_stack_array_sanitize");
+                    if (s) {
+                        s[0] =  '\0';
+                        strcpy((char *)s, "--");
+                        strcpy((char *)s + 2, (char *)data);
+                        strcpy((char *)s + strlen((char *)data) + 2, "--");
+                    }
+                    else {
+                        s = (byte *)data;
+                    }
+                    code = name_ref(imemory, s, strlen((char *)s), &obj, 1);
+                    if (code < 0) make_null(&obj);
+                    if (s != data)
+                        gs_free_object(mem, s, "ref_stack_array_sanitize");
+                }
+            }
+            else {
+                make_null(&obj);
+            }
+            ref_assign(darr->value.refs + i, &obj);
+            break;
+          }
+          case t_array:
+          case t_shortarray:
+          case t_mixedarray:
+          {
+            int attrs = r_type_attrs(&obj) & (a_write | a_read | a_execute | a_executable);
+            /* We only want to copy executable arrays */
+            if (attrs & (a_execute | a_executable)) {
+                code = ialloc_ref_array(&arr2, attrs, r_size(&obj), "ref_stack_array_sanitize");
+                if (code < 0) {
+                    make_null(&arr2);
+                }
+                else {
+                    code = ref_stack_array_sanitize(i_ctx_p, &obj, &arr2);
+                }
+                ref_assign(darr->value.refs + i, &arr2);
+            }
+            else {
+                ref_assign(darr->value.refs + i, &obj);
+            }
+            break;
+          }
+          default:
+            ref_assign(darr->value.refs + i, &obj);
+        }
+    }
+    return 0;
+}
+
+
 /*
  * Store the top 'count' elements of a stack, starting 'skip' elements below
  * the top, into an array, with or without store/undo checking.  age=-1 for
diff --git a/psi/istack.h b/psi/istack.h
index 051dcbe..54be405 100644
--- a/psi/istack.h
+++ b/psi/istack.h
@@ -129,6 +129,9 @@ int ref_stack_store(const ref_stack_t *pstack, ref *parray, uint count,
                     uint skip, int age, bool check,
                     gs_dual_memory_t *idmem, client_name_t cname);
 
+int
+ref_stack_array_sanitize(i_ctx_t *i_ctx_p, ref *sarr, ref *darr);
+
 /*
  * Pop the top N elements off a stack.
  * The number must not exceed the number of elements in use.
-- 
2.17.2


From 2db98f9c66135601efb103d8db7d020a672308db Mon Sep 17 00:00:00 2001
From: Chris Liddell <chris.liddell@artifex.com>
Date: Thu, 13 Dec 2018 15:28:34 +0000
Subject: [PATCH 4/6] Any transient procedures that call .force* operators

(i.e. for conditionals or loops) make them executeonly.
---
 Resource/Init/gs_diskn.ps |  2 +-
 Resource/Init/gs_dps1.ps  |  4 ++--
 Resource/Init/gs_fntem.ps |  4 ++--
 Resource/Init/gs_fonts.ps | 12 ++++++------
 Resource/Init/gs_init.ps  |  4 ++--
 Resource/Init/gs_lev2.ps  | 11 ++++++-----
 Resource/Init/gs_pdfwr.ps |  2 +-
 Resource/Init/gs_res.ps   |  4 ++--
 Resource/Init/gs_setpd.ps |  2 +-
 Resource/Init/pdf_base.ps | 13 ++++++++-----
 Resource/Init/pdf_draw.ps | 16 +++++++++-------
 Resource/Init/pdf_font.ps |  6 +++---
 Resource/Init/pdf_main.ps |  4 ++--
 Resource/Init/pdf_ops.ps  |  7 ++++---
 14 files changed, 49 insertions(+), 42 deletions(-)

diff --git a/Resource/Init/gs_diskn.ps b/Resource/Init/gs_diskn.ps
index fd694bc..8bf2054 100644
--- a/Resource/Init/gs_diskn.ps
+++ b/Resource/Init/gs_diskn.ps
@@ -51,7 +51,7 @@ systemdict begin
     mark 5 1 roll ] mark exch { { } forall } forall ]
     //systemdict /.searchabledevs 2 index .forceput
     exch .setglobal
-  }
+  } executeonly
   if
 } .bind executeonly odef % must be bound and hidden for .forceput
 
diff --git a/Resource/Init/gs_dps1.ps b/Resource/Init/gs_dps1.ps
index ec5db61..4fae283 100644
--- a/Resource/Init/gs_dps1.ps
+++ b/Resource/Init/gs_dps1.ps
@@ -78,7 +78,7 @@ level2dict begin
    .currentglobal
     {		% Current mode is global; delete from local directory too.
       //systemdict /LocalFontDirectory .knownget
-       { 1 index .forceundef }		% LocalFontDirectory is readonly
+       { 1 index .forceundef } executeonly		% LocalFontDirectory is readonly
       if
     }
     {		% Current mode is local; if there was a shadowed global
@@ -126,7 +126,7 @@ level2dict begin
           }
          ifelse
        } forall
-      pop counttomark 2 idiv { .forceundef } repeat pop		% readonly
+      pop counttomark 2 idiv { .forceundef } executeonly repeat pop		% readonly
     }
    if
    //SharedFontDirectory exch .forcecopynew pop
diff --git a/Resource/Init/gs_fntem.ps b/Resource/Init/gs_fntem.ps
index c1f7651..6eb672a 100644
--- a/Resource/Init/gs_fntem.ps
+++ b/Resource/Init/gs_fntem.ps
@@ -401,12 +401,12 @@ currentdict end def
       .forceput % FontInfo can be read-only.
       pop                                                        % bool <font>
       exit
-    } if
+    } executeonly if
     dup /FontInfo get                                            % bool <font> <FI>
     /GlyphNames2Unicode /Unicode /Decoding findresource
     .forceput % FontInfo can be read-only.
     exit
-  } loop
+  } executeonly loop
   exch setglobal
 } .bind executeonly odef % must be bound and hidden for .forceput
 
diff --git a/Resource/Init/gs_fonts.ps b/Resource/Init/gs_fonts.ps
index 052a191..6d2c26b 100644
--- a/Resource/Init/gs_fonts.ps
+++ b/Resource/Init/gs_fonts.ps
@@ -374,7 +374,7 @@ FONTPATH length 0 eq { (%END FONTPATH) .skipeof } if
 /.setnativefontmapbuilt { % set whether we've been run
   dup type /booleantype eq {
       systemdict exch /.nativefontmapbuilt exch .forceput
-  }
+  } executeonly
   {pop}
   ifelse
 } .bind executeonly odef
@@ -1007,11 +1007,11 @@ $error /SubstituteFont { } put
 { 2 index gcheck currentglobal
   2 copy eq {
     pop pop .forceput
-  } {
+  } executeonly {
     5 1 roll setglobal
     dup length string copy
     .forceput setglobal
-  } ifelse
+  } executeonly ifelse
 } .bind executeonly odef % must be bound and hidden for .forceput
 
 % Attempt to load a font from a file.
@@ -1084,7 +1084,7 @@ $error /SubstituteFont { } put
            .FontDirectory 3 index .forceundef		% readonly
            1 index (r) file .loadfont .FontDirectory exch
            /.setglobal .systemvar exec
-         }
+         } executeonly
          { .loadfont .FontDirectory
          }
         ifelse
@@ -1105,7 +1105,7 @@ $error /SubstituteFont { } put
         dup 3 index .fontknownget
          { dup /PathLoad 4 index //.putgstringcopy
            4 1 roll pop pop pop //true exit
-         } if
+         } executeonly if
 
                 % Maybe the file had a different FontName.
                 % See if we can get a FontName from the file, and if so,
@@ -1134,7 +1134,7 @@ $error /SubstituteFont { } put
               ifelse  % Stack: origfontname fontdict
               exch pop //true exit
                       % Stack: fontdict
-            }
+            } executeonly
            if pop % Stack: origfontname fontdirectory path
          }
         if pop pop  % Stack: origfontname
diff --git a/Resource/Init/gs_init.ps b/Resource/Init/gs_init.ps
index 0b900e6..9015f90 100644
--- a/Resource/Init/gs_init.ps
+++ b/Resource/Init/gs_init.ps
@@ -2347,7 +2347,7 @@ SAFER { .setsafeglobal } if
         % Update the copy of the user parameters.
   mark .currentuserparams counttomark 2 idiv {
     userparams 3 1 roll .forceput	% userparams is read-only
-  } repeat pop
+  } executeonly repeat pop
         % Turn on idiom recognition, if available.
   currentuserparams /IdiomRecognition known {
     /IdiomRecognition //true .definepsuserparam
@@ -2366,7 +2366,7 @@ SAFER { .setsafeglobal } if
         % Remove real system params from pssystemparams.
   mark .currentsystemparams counttomark 2 idiv {
     pop pssystemparams exch .forceundef
-  } repeat pop
+  } executeonly repeat pop
 } if
 
 % Set up AlignToPixels :
diff --git a/Resource/Init/gs_lev2.ps b/Resource/Init/gs_lev2.ps
index a8ed892..b69303d 100644
--- a/Resource/Init/gs_lev2.ps
+++ b/Resource/Init/gs_lev2.ps
@@ -154,7 +154,8 @@ end
       % protect top level of parameters that we copied
       dup type dup /arraytype eq exch /stringtype eq or { readonly } if
       /userparams .systemvar 3 1 roll .forceput  % userparams is read-only
-    } {
+    } executeonly
+    {
       pop pop
     } ifelse
   } forall
@@ -224,7 +225,7 @@ end
          % protect top level parameters that we copied
          dup type dup /arraytype eq exch /stringtype eq or { readonly } if
          //pssystemparams 3 1 roll .forceput	% pssystemparams is read-only
-       }
+       } executeonly
        { pop pop
        }
       ifelse
@@ -920,7 +921,7 @@ mark
   dup /PaintProc get
   1 index /Implementation known not {
     1 index dup /Implementation //null .forceput readonly pop
-  } if
+  } executeonly if
   exec
 }.bind odef
 
@@ -944,7 +945,7 @@ mark
   dup /PaintProc get
   1 index /Implementation known not {
     1 index dup /Implementation //null .forceput readonly pop
-  } if
+  } executeonly if
   /UNROLLFORMS where {/UNROLLFORMS get}{false}ifelse not
   %% [CTM] <<Form>> PaintProc .beginform -
   {
@@ -991,7 +992,7 @@ mark
         %% Form dictioanry using the /Implementation key).
         1 dict dup /FormID 4 -1 roll put
         1 index exch /Implementation exch .forceput readonly pop
-      }
+      } executeonly
       ifelse
     }
     {
diff --git a/Resource/Init/gs_pdfwr.ps b/Resource/Init/gs_pdfwr.ps
index 58e75d3..b425103 100644
--- a/Resource/Init/gs_pdfwr.ps
+++ b/Resource/Init/gs_pdfwr.ps
@@ -650,7 +650,7 @@ currentdict /.pdfmarkparams .undef
             } ifelse
           } bind .makeoperator .forceput
           systemdict /.pdf_hooked_DSC_Creator //true .forceput
-        } if
+        } executeonly if
         pop
       } if
     } {
diff --git a/Resource/Init/gs_res.ps b/Resource/Init/gs_res.ps
index 8eb8bb0..d9b3459 100644
--- a/Resource/Init/gs_res.ps
+++ b/Resource/Init/gs_res.ps
@@ -152,7 +152,7 @@ setglobal
                 % use .forceput / .forcedef later to replace the dummy,
                 % empty .Instances dictionary with the real one later.
           readonly
-        } {
+        }{
           /defineresource cvx /typecheck signaloperror
         } ifelse
 } bind executeonly odef
@@ -424,7 +424,7 @@ status {
                         % As noted above, Category dictionaries are read-only,
                         % so we have to use .forcedef here.
                   /.Instances 1 index .forcedef	% Category dict is read-only
-                } if
+                } executeonly if
               }
               { .LocalInstances dup //.emptydict eq
                  { pop 3 dict localinstancedict Category 2 index put
diff --git a/Resource/Init/gs_setpd.ps b/Resource/Init/gs_setpd.ps
index 71eb622..46e5810 100644
--- a/Resource/Init/gs_setpd.ps
+++ b/Resource/Init/gs_setpd.ps
@@ -634,7 +634,7 @@ NOMEDIAATTRS {
   SETPDDEBUG { (Rolling back.) = pstack flush } if
   3 index 2 index 3 -1 roll .forceput
   4 index 1 index .knownget
-  { 4 index 3 1 roll .forceput }
+  { 4 index 3 1 roll .forceput } executeonly
   { 3 index exch .undef }
   ifelse
 } bind executeonly odef
diff --git a/Resource/Init/pdf_base.ps b/Resource/Init/pdf_base.ps
index 7ccd4cd..c62ac0e 100644
--- a/Resource/Init/pdf_base.ps
+++ b/Resource/Init/pdf_base.ps
@@ -130,26 +130,29 @@ currentdict /num-chars-dict .undef
 
 /.pdfexectoken {		% <count> <opdict> <exectoken> .pdfexectoken ?
   PDFDEBUG {
-    pdfdict /PDFSTEPcount known not { pdfdict /PDFSTEPcount 1 .forceput } if
+    pdfdict /PDFSTEPcount known not { pdfdict /PDFSTEPcount 1 .forceput } executeonly if
     PDFSTEP {
       pdfdict /PDFtokencount 2 copy .knownget { 1 add } { 1 } ifelse .forceput
       PDFSTEPcount 1 gt {
         pdfdict /PDFSTEPcount PDFSTEPcount 1 sub .forceput
-      } {
+      } executeonly
+      {
         dup ==only
         (    step # ) print PDFtokencount =only
         ( ? ) print flush 1 //false .outputpage
         (%stdin) (r) file 255 string readline {
           token {
             exch pop pdfdict /PDFSTEPcount 3 -1 roll .forceput
-          } {
+          } executeonly
+          {
             pdfdict /PDFSTEPcount 1 .forceput
-          } ifelse % token
+          } executeonly ifelse % token
         } {
           pop /PDFSTEP //false def	 % EOF on stdin
         } ifelse % readline
       } ifelse % PDFSTEPcount > 1
-    } {
+    } executeonly
+    {
       dup ==only () = flush
     } ifelse % PDFSTEP
   } if % PDFDEBUG
diff --git a/Resource/Init/pdf_draw.ps b/Resource/Init/pdf_draw.ps
index c239daf..d743ae1 100644
--- a/Resource/Init/pdf_draw.ps
+++ b/Resource/Init/pdf_draw.ps
@@ -1118,14 +1118,14 @@ currentdict end readonly def
           pdfdict /.Qqwarning_issued //true .forceput
           .setglobal
           pdfformaterror
-        } ifelse
+        } executeonly ifelse
       }
       {
         currentglobal pdfdict gcheck .setglobal
         pdfdict /.Qqwarning_issued //true .forceput
         .setglobal
         pdfformaterror
-      } ifelse
+      } executeonly ifelse
       end
     } ifelse
   } loop
@@ -1141,14 +1141,14 @@ currentdict end readonly def
         pdfdict /.Qqwarning_issued //true .forceput
         .setglobal
         pdfformaterror
-      } ifelse
+      } executeonly ifelse
     }
     {
       currentglobal pdfdict gcheck .setglobal
       pdfdict /.Qqwarning_issued //true .forceput
       .setglobal
       pdfformaterror
-    } ifelse
+    } executeonly ifelse
   } if
   pop
 
@@ -2350,9 +2350,10 @@ currentdict /last-ditch-bpc-csp undef
 /IncrementAppearanceNumber {
   pdfdict /AppearanceNumber .knownget {
     1 add pdfdict /AppearanceNumber 3 -1 roll .forceput
-  }{
+  } executeonly
+  {
     pdfdict /AppearanceNumber 0 .forceput
-  } ifelse
+  } executeonly ifelse
 }bind executeonly odef
 
 /MakeAppearanceName {
@@ -2510,7 +2511,8 @@ currentdict /last-ditch-bpc-csp undef
     %% want to preserve it.
     pdfdict /.PreservePDFForm false .forceput
     /q cvx /execform cvx 5 -2 roll
-  }{
+  } executeonly
+  {
     /q cvx /PDFexecform cvx 5 -2 roll
   } ifelse
 
diff --git a/Resource/Init/pdf_font.ps b/Resource/Init/pdf_font.ps
index 535b14a..f1d1728 100644
--- a/Resource/Init/pdf_font.ps
+++ b/Resource/Init/pdf_font.ps
@@ -714,7 +714,7 @@ currentdict end readonly def
     pop pop pop
     currentdict /.stackdepth .forceundef
     currentdict /.dstackdepth .forceundef
-  }
+  } executeonly
   {pop pop pop}
   ifelse
 
@@ -1224,7 +1224,7 @@ currentdict /eexec_pdf_param_dict .undef
                 (\n   **** Warning: Type 3 glyph has unbalanced q/Q operators \(too many q's\)\n               Output may be incorrect.\n)
                 pdfformatwarning
                 pdfdict /.Qqwarning_issued //true .forceput
-              } if
+              } executeonly if
               Q
             } repeat
             Q
@@ -1989,7 +1989,7 @@ currentdict /CMap_read_dict undef
               /CIDFallBack /CIDFont findresource
             } if
             exit
-          } if
+          } executeonly if
         } if
       } if
 
diff --git a/Resource/Init/pdf_main.ps b/Resource/Init/pdf_main.ps
index c823e69..dd1480b 100644
--- a/Resource/Init/pdf_main.ps
+++ b/Resource/Init/pdf_main.ps
@@ -2694,14 +2694,14 @@ currentdict /PDF2PS_matrix_key undef
           pdfdict /.Qqwarning_issued //true .forceput
           .setglobal
           pdfformaterror
-        } ifelse
+        } executeonly ifelse
       }
       {
         currentglobal pdfdict gcheck .setglobal
         pdfdict /.Qqwarning_issued //true .forceput
         .setglobal
         pdfformaterror
-      } ifelse
+      } executeonly ifelse
     } if
   } if
   pop
diff --git a/Resource/Init/pdf_ops.ps b/Resource/Init/pdf_ops.ps
index 8672d61..aa09641 100644
--- a/Resource/Init/pdf_ops.ps
+++ b/Resource/Init/pdf_ops.ps
@@ -184,14 +184,14 @@ currentdict /gput_always_allow .undef
         pdfdict /.Qqwarning_issued //true .forceput
         .setglobal
         pdfformaterror
-      } ifelse
+      } executeonly ifelse
     }
     {
       currentglobal pdfdict gcheck .setglobal
       pdfdict /.Qqwarning_issued //true .forceput
       .setglobal
       pdfformaterror
-    } ifelse
+    } executeonly ifelse
   } if
 } bind executeonly odef
 
@@ -439,7 +439,8 @@ currentdict /gput_always_allow .undef
   dup type /booleantype eq {
     .currentSMask type /dicttype eq {
       .currentSMask /Processed 2 index .forceput
-    } {
+  } executeonly
+  {
       .setSMask
   }ifelse
   }{
-- 
2.17.2


From 99f13091a3f309bdc95d275ea9fec10bb9f42d9a Mon Sep 17 00:00:00 2001
From: Chris Liddell <chris.liddell@artifex.com>
Date: Sat, 15 Dec 2018 09:08:32 +0000
Subject: [PATCH 5/6] Bug700317: Fix logic for an older change

Unlike almost every other function in gs, dict_find_string() returns 1 on
success 0 or <0 on failure. The logic for this case was wrong.
---
 psi/interp.c | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/psi/interp.c b/psi/interp.c
index aa5779c..f6c45bb 100644
--- a/psi/interp.c
+++ b/psi/interp.c
@@ -703,7 +703,7 @@ again:
                  * i.e. it's an internal operator we have hidden
                  */
                 code = dict_find_string(systemdict, (const char *)bufptr, &tobj);
-                if (code < 0) {
+                if (code <= 0) {
                     buf[0] = buf[1] = buf[rlen + 2] = buf[rlen + 3] = '-';
                     rlen += 4;
                     bufptr = buf;
-- 
2.17.2


From 59d8f4deef90c1598ff50616519d5576756b4495 Mon Sep 17 00:00:00 2001
From: Chris Liddell <chris.liddell@artifex.com>
Date: Tue, 18 Dec 2018 10:42:10 +0000
Subject: [PATCH 6/6] Harden some uses of .force* operators

by adding a few immediate evalutions
---
 Resource/Init/gs_dps1.ps  |  4 ++--
 Resource/Init/gs_fonts.ps | 20 ++++++++++----------
 Resource/Init/gs_init.ps  |  6 +++---
 3 files changed, 15 insertions(+), 15 deletions(-)

diff --git a/Resource/Init/gs_dps1.ps b/Resource/Init/gs_dps1.ps
index 4fae283..b75ea14 100644
--- a/Resource/Init/gs_dps1.ps
+++ b/Resource/Init/gs_dps1.ps
@@ -74,7 +74,7 @@ level2dict begin
  } odef
 % undefinefont has to take local/global VM into account.
 /undefinefont		% <fontname> undefinefont -
- { .FontDirectory 1 .argindex .forceundef	% FontDirectory is readonly
+ { //.FontDirectory 1 .argindex .forceundef	% FontDirectory is readonly
    .currentglobal
     {		% Current mode is global; delete from local directory too.
       //systemdict /LocalFontDirectory .knownget
@@ -85,7 +85,7 @@ level2dict begin
                 % definition, copy it into the local directory.
       //systemdict /SharedFontDirectory .knownget
        { 1 index .knownget
-          { .FontDirectory 2 index 3 -1 roll { put } systemdict /superexec known {//superexec}{1183615869 internaldict /superexec get exec} ifelse } % readonly
+          { //.FontDirectory 2 index 3 -1 roll { put } systemdict /superexec known {//superexec}{1183615869 internaldict /superexec get exec} ifelse } % readonly
          if
        }
       if
diff --git a/Resource/Init/gs_fonts.ps b/Resource/Init/gs_fonts.ps
index 6d2c26b..4807f81 100644
--- a/Resource/Init/gs_fonts.ps
+++ b/Resource/Init/gs_fonts.ps
@@ -516,7 +516,7 @@ buildfontdict 3 /.buildfont3 cvx put
       if
     }
    if
-   dup .FontDirectory 4 -2 roll { .growput } systemdict /superexec known {//superexec}{1183615869 internaldict /superexec get exec} ifelse	% readonly
+   dup //.FontDirectory 4 -2 roll { .growput } systemdict /superexec known {//superexec}{1183615869 internaldict /superexec get exec} ifelse	% readonly
                 % If the font originated as a resource, register it.
    currentfile .currentresourcefile eq { dup .registerfont } if
    readonly
@@ -943,7 +943,7 @@ $error /SubstituteFont { } put
 % Try to find a font using only the present contents of Fontmap.
 /.tryfindfont {         % <fontname> .tryfindfont <font> true
                         % <fontname> .tryfindfont false
-  .FontDirectory 1 index .fontknownget
+  //.FontDirectory 1 index .fontknownget
     {                   % Already loaded
       exch pop //true
     }
@@ -975,7 +975,7 @@ $error /SubstituteFont { } put
                {                % Font with a procedural definition
                  exec           % The procedure will load the font.
                                 % Check to make sure this really happened.
-                 .FontDirectory 1 index .knownget
+                 //.FontDirectory 1 index .knownget
                   { exch pop //true exit }
                  if
                }
@@ -1081,11 +1081,11 @@ $error /SubstituteFont { } put
                 % because it's different depending on language level.
            .currentglobal exch /.setglobal .systemvar exec
                 % Remove the fake definition, if any.
-           .FontDirectory 3 index .forceundef		% readonly
-           1 index (r) file .loadfont .FontDirectory exch
+           //.FontDirectory 3 index .forceundef		% readonly
+           1 index (r) file .loadfont //.FontDirectory exch
            /.setglobal .systemvar exec
          } executeonly
-         { .loadfont .FontDirectory
+         { .loadfont //.FontDirectory
          }
         ifelse
                 % Stack: fontname fontfilename fontdirectory
@@ -1119,8 +1119,8 @@ $error /SubstituteFont { } put
                       % Stack: origfontname fontdirectory filefontname fontdict
               3 -1 roll pop
                       % Stack: origfontname filefontname fontdict
-              dup /FontName get dup FontDirectory exch .forceundef
-              GlobalFontDirectory exch .forceundef
+              dup /FontName get dup //.FontDirectory exch .forceundef
+              /GlobalFontDirectory .systemvar exch .forceundef
               dup length dict .copydict dup 3 index /FontName exch put
               2 index exch definefont
               exch
@@ -1175,10 +1175,10 @@ currentdict /.putgstringcopy .forceundef
       {
         {
           pop dup type /stringtype eq { cvn } if
-          .FontDirectory 1 index known not {
+          //.FontDirectory 1 index known not {
             2 dict dup /FontName 3 index put
             dup /FontType 1 put
-            .FontDirectory 3 1 roll { put } systemdict /superexec known {//superexec}{1183615869 internaldict /superexec get exec} ifelse   % readonly
+            //.FontDirectory 3 1 roll { put } systemdict /superexec known {//superexec}{1183615869 internaldict /superexec get exec} ifelse   % readonly
           } {
             pop
           } ifelse
diff --git a/Resource/Init/gs_init.ps b/Resource/Init/gs_init.ps
index 9015f90..48bb96d 100644
--- a/Resource/Init/gs_init.ps
+++ b/Resource/Init/gs_init.ps
@@ -1158,8 +1158,8 @@ errordict /unknownerror .undef
   //.SAFERERRORLIST
   {dup errordict exch get 2 index 3 1 roll put} forall
   noaccess pop
-  systemdict /.setsafeerrors .forceundef
-  systemdict /.SAFERERRORLIST .forceundef
+  //systemdict /.setsafeerrors .forceundef
+  //systemdict /.SAFERERRORLIST .forceundef
 } bind executeonly odef
 
 SAFERERRORS {.setsafererrors} if
@@ -2104,7 +2104,7 @@ currentdict /tempfilepaths undef
 
 /.locksafe {
   .locksafe_userparams
-  systemdict /getenv {pop //false} .forceput
+  //systemdict /getenv {pop //false} .forceput
   % setpagedevice has the side effect of clearing the page, but
   % we will just document that. Using setpagedevice keeps the device
   % properties and pagedevice .LockSafetyParams in agreement even
-- 
2.17.2