From 4203e04ef9e6ca22ed68a1ab10a878aa9ceaeedc Mon Sep 17 00:00:00 2001
From: Ray Johnston <ray.johnston@artifex.com>
Date: Thu, 14 Feb 2019 10:20:03 -0800
Subject: [PATCH] Fix bug 700585: Restrict superexec and remove it from
 internals and gs_cet.ps

Also while changing things, restructure the CETMODE so that it will
work with -dSAFER. The gs_cet.ps is now run when we are still at save
level 0 with systemdict writeable. Allows us to undefine .makeoperator
and .setCPSImode internal operators after CETMODE is handled.

Change previous uses of superexec to using .forceput (with the usual
.bind executeonly to hide it).

CVE: CVE-2019-3835
Upstream-Status: Backport [git://git.ghostscript.com/ghostpdl.git]

Signed-off-by: Ovidiu Panait <ovidiu.panait@windriver.com>
---
 Resource/Init/gs_cet.ps   | 38 ++++++++++++++------------------------
 Resource/Init/gs_dps1.ps  |  2 +-
 Resource/Init/gs_fonts.ps |  8 ++++----
 Resource/Init/gs_init.ps  | 38 +++++++++++++++++++++++++++-----------
 Resource/Init/gs_ttf.ps   |  8 ++++----
 Resource/Init/gs_type1.ps |  6 +++---
 6 files changed, 53 insertions(+), 47 deletions(-)

diff --git a/Resource/Init/gs_cet.ps b/Resource/Init/gs_cet.ps
index dbc5c4e..3cc6883 100644
--- a/Resource/Init/gs_cet.ps
+++ b/Resource/Init/gs_cet.ps
@@ -1,37 +1,29 @@
 %!PS
 % Set defaults for Ghostscript to match Adobe CPSI behaviour for CET
 
-/.odef {		% <name> <proc> odef -
-  1 index exch .makeoperator def
-} bind def
-
+% skip if we've already run this -- based on fake "product"
 systemdict /product get (PhotoPRINT SE 5.0v2) readonly eq
 {
   (%END GS_CET) .skipeof
 } if
 
-% do this in the server level so it is persistent across jobs
-//true 0 startjob not {
-  (*** Warning: CET startup is not in server default) = flush
-} if
+% Note: this must be run at save level 0 and when systemdict is writeable
+currentglobal //true setglobal
+systemdict dup dup dup
+/version (3017.102) readonly .forceput		% match CPSI 3017.102
+/product (PhotoPRINT SE 5.0v2) readonly .forceput	% match CPSI 3017.102
+/revision 0 put			% match CPSI 3017.103 Tek shows revision 5
+/serialnumber dup {233640} readonly .makeoperator .forceput % match CPSI 3017.102 Tek shows serialnumber 1401788461
+
+systemdict /.odef {           % <name> <proc> odef -
+  1 index exch //.makeoperator def
+} .bind .forceput          % this will be undefined at the end
 
 300 .sethiresscreen	% needed for language switch build since it
                         % processes gs_init.ps BEFORE setting the resolution
 
 0 array 0 setdash % CET 09-08 wants local setdash
 
-currentglobal //true setglobal
-
-{
-  systemdict dup dup dup
-  /version (3017.102) readonly put		% match CPSI 3017.102
-  /product (PhotoPRINT SE 5.0v2) readonly put	% match CPSI 3017.102
-  /revision 0 put			% match CPSI 3017.103 Tek shows revision 5
-  /serialnumber dup {233640} readonly .makeoperator put % match CPSI 3017.102 Tek shows serialnumber 1401788461
-  systemdict /deviceinfo undef                  % for CET 20-23-1
-%  /UNROLLFORMS true put                 % CET files do unreasonable things inside forms
-} 1183615869 internaldict /superexec get exec
-
 /UNROLLFORMS true def
 
 (%.defaultbgrucrproc) cvn { } bind def
@@ -118,9 +110,7 @@ userdict /.smoothness currentsmoothness put
   ofnfa
 } bind def
 
-currentdict /.odef undef
-% end of slightly nasty hack to give consistent cluster results
-
-//false 0 startjob pop		% re-enter encapsulated mode
+systemdict /.odef .undef
 
+% end of slightly nasty hack to give consistent cluster results
 %END GS_CET
diff --git a/Resource/Init/gs_dps1.ps b/Resource/Init/gs_dps1.ps
index 3d2cf7a..c4fd839 100644
--- a/Resource/Init/gs_dps1.ps
+++ b/Resource/Init/gs_dps1.ps
@@ -89,7 +89,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 .forceput } % readonly
          if
        }
       if
diff --git a/Resource/Init/gs_fonts.ps b/Resource/Init/gs_fonts.ps
index 0562235..f2b4e19 100644
--- a/Resource/Init/gs_fonts.ps
+++ b/Resource/Init/gs_fonts.ps
@@ -519,11 +519,11 @@ buildfontdict 3 /.buildfont3 cvx put
                 % the font in LocalFontDirectory.
    .currentglobal
     { //systemdict /LocalFontDirectory .knownget
-       { 2 index 2 index { .growput } systemdict /superexec known {//superexec}{1183615869 internaldict /superexec get exec} ifelse }	% readonly
+       { 2 index 2 index .forceput }	% readonly
       if
     }
    if
-   dup //.FontDirectory 4 -2 roll { .growput } systemdict /superexec known {//superexec}{1183615869 internaldict /superexec get exec} ifelse	% readonly
+   dup //.FontDirectory 4 -2 roll .forceput % readonly
                 % If the font originated as a resource, register it.
    currentfile .currentresourcefile eq { dup .registerfont } if
    readonly
@@ -1191,13 +1191,13 @@ $error /SubstituteFont { } put
           //.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 //.forceput exec % readonly
           } {
             pop
           } ifelse
         } forall
       } forall
-    }
+    } executeonly	% hide .forceput
 FAKEFONTS { exch } if pop def   % don't bind, .current/setglobal get redefined
 
 % Install initial fonts from Fontmap.
diff --git a/Resource/Init/gs_init.ps b/Resource/Init/gs_init.ps
index 80d9585..0d5c4f7 100644
--- a/Resource/Init/gs_init.ps
+++ b/Resource/Init/gs_init.ps
@@ -2188,9 +2188,6 @@ SAFER { .setsafeglobal } if
   /.endtransparencygroup     % transparency-example.ps
   /.setdotlength             % Bug687720.ps
   /.sort /.setdebug /.mementolistnewblocks /getenv
-
-  /.makeoperator /.setCPSImode              % gs_cet.ps, this won't work on cluster with -dSAFER
-
   /unread
   ]
   {systemdict exch .forceundef} forall
@@ -2270,7 +2267,6 @@ SAFER { .setsafeglobal } if
 
   % Used by our own test suite files
   %/.fileposition %image-qa.ps
-  %/.makeoperator /.setCPSImode % gs_cet.ps
 
   % Either our code uses these in ways which mean they can't be undefined, or they are used directly by
   % test files/utilities, or engineers expressed a desire to keep them visible.
@@ -2457,6 +2453,16 @@ end
 /vmreclaim where
  { pop NOGC not { 2 .vmreclaim 0 vmreclaim } if
  } if
+
+% Do this before systemdict is locked (see below for additional CETMODE setup using gs_cet.ps)
+systemdict /CETMODE .knownget {
+  {
+    (gs_cet.ps) runlibfile
+  } if
+} if
+systemdict /.makeoperator .undef	% must be after gs_cet.ps
+systemdict /.setCPSImode .undef		% must be after gs_cet.ps
+
 DELAYBIND not {
   systemdict /.bindnow .undef       % We only need this for DELAYBIND
   systemdict /.forcecopynew .undef	% remove temptation
@@ -2464,16 +2470,29 @@ DELAYBIND not {
   systemdict /.forceundef .undef	% ditto
 } if
 
-% Move superexec to internaldict if superexec is defined.
-systemdict /superexec .knownget {
-  1183615869 internaldict /superexec 3 -1 roll put
-  systemdict /superexec .undef
+% Move superexec to internaldict if superexec is defined. (Level 2 or later)
+systemdict /superexec known {
+  % restrict superexec to single known use by PScript5.dll
+  % We could do this only for SAFER mode, but internaldict and superexec are
+  % not very well documented, and we don't want them to be used.
+  1183615869 internaldict /superexec {
+    2 index /Private eq		% first check for typical use in PScript5.dll
+    1 index length 1 eq and	% expected usage is: dict /Private <value> {put} superexec
+    1 index 0 get systemdict /put get eq and
+    {
+      //superexec exec		% the only usage we allow
+    } {
+      /superexec load /invalidaccess signalerror
+    } ifelse
+  } bind cvx executeonly put
+  systemdict /superexec .undef	% get rid of the dangerous (unrestricted) operator
 } if
 
 % Can't remove this one until the last minute :-)
 DELAYBIND not {
 systemdict /.undef .undef
 } if
+
 WRITESYSTEMDICT {
    SAFER {
        (\n *** WARNING - you have selected SAFER, indicating you want Ghostscript\n) print
@@ -2500,7 +2519,4 @@ WRITESYSTEMDICT {
 % be 'true' in some cases.
 userdict /AGM_preserve_spots //false put
 
-systemdict /CETMODE .knownget
-{ { (gs_cet.ps) runlibfile } if } if
-
 % The interpreter will run the initial procedure (start).
diff --git a/Resource/Init/gs_ttf.ps b/Resource/Init/gs_ttf.ps
index 05943c5..da97afa 100644
--- a/Resource/Init/gs_ttf.ps
+++ b/Resource/Init/gs_ttf.ps
@@ -1421,7 +1421,7 @@ mark
           TTFDEBUG { (\n1 setting alias: ) print dup ==only
                 ( to be the same as  ) print 2 index //== exec } if
 
-          7 index 2 index 3 -1 roll exch //.growput systemdict /superexec known {//superexec}{1183615869 internaldict /superexec get exec} ifelse
+          7 index 2 index 3 -1 roll exch .forceput
         } forall
         pop pop pop
       }
@@ -1439,7 +1439,7 @@ mark
           exch pop
           TTFDEBUG { (\n2 setting alias: ) print 1 index ==only
                      ( to use glyph index: ) print dup //== exec } if
-          5 index 3 1 roll //.growput systemdict /superexec known {//superexec}{1183615869 internaldict /superexec get exec} ifelse
+          5 index 3 1 roll .forceput
           //false
         }
         {
@@ -1456,7 +1456,7 @@ mark
         {                            %  CharStrings(dict) isunicode(boolean) cmap(dict) RAGL(dict) gname(name) codep(integer) gindex(integer)
           TTFDEBUG { (\3 nsetting alias: ) print 1 index ==only
                 ( to be index: ) print dup //== exec } if
-          exch pop 5 index 3 1 roll //.growput systemdict /superexec known {//superexec}{1183615869 internaldict /superexec get exec} ifelse
+          exch pop 5 index 3 1 roll .forceput
         }
         {
           pop pop
@@ -1486,7 +1486,7 @@ mark
       } ifelse
     ]
   TTFDEBUG { (Encoding: ) print dup === flush } if
-} bind def
+} .bind executeonly odef		% hides .forceput
 
 % to be removed 9.09......
 currentdict /postalias undef
diff --git a/Resource/Init/gs_type1.ps b/Resource/Init/gs_type1.ps
index 96e1ced..61f5269 100644
--- a/Resource/Init/gs_type1.ps
+++ b/Resource/Init/gs_type1.ps
@@ -116,7 +116,7 @@
                  {                                               % scratch(string) RAGL(dict) AGL(dict) CharStrings(dict) cstring gname aglname
                    CFFDEBUG { (\nsetting alias: ) print dup ==only
                          ( to be the same as glyph: ) print 1 index //== exec } if
-                   3 index exch 3 index //.growput systemdict /superexec known {//superexec}{1183615869 internaldict /superexec get exec} ifelse
+                   3 index exch 3 index .forceput
                                                                  % scratch(string) RAGL(dict) AGL(dict) CharStrings(dict) cstring gname
                  }
                  {pop} ifelse
@@ -135,7 +135,7 @@
          3 1 roll pop pop
      } if
      pop
-     dup /.AGLprocessed~GS //true //.growput systemdict /superexec known {//superexec}{1183615869 internaldict /superexec get exec} ifelse
+     dup /.AGLprocessed~GS //true .forceput
    } if
 
    %% We need to excute the C .buildfont1 in a stopped context so that, if there
@@ -148,7 +148,7 @@
    {//.buildfont1} stopped
    4 3 roll .setglobal
    {//.buildfont1 $error /errorname get signalerror} if
- } bind def
+ } .bind executeonly def	% hide .forceput
 
 % If the diskfont feature isn't included, define a dummy .loadfontdict.
 /.loadfontdict where
-- 
2.20.1

