From 9096beaa4451c12dd2a2caf000658fbac4a5bcdf Mon Sep 17 00:00:00 2001
From: Ken Sharp <ken.sharp@artifex.com>
Date: Mon, 5 Nov 2018 15:51:32 +0800
Subject: [PATCH] Make .forceput unavailable from '.policyprocs' helper
 dictionary

Bug #69963 "1Policy is a dangerous operator, any callers should be odef"

Leaving the .policyprocs dictionary with a procedure which is a simple
wrapper for .forceput effectively leaves .forceput available.

It seems that the only reason to have .policyprocs is to minimise the
code in .applypolicies, so we can remove the dictionary and put the
code straight into .applypolicies, which we can then bind and make
executeonly, which hides the .forceput. Also, since we don't need
.applypolicies after startup, we can undefine that from systemdict too.

While we're here, review all the uses of .force* to make certain that
there are no other similar cases. This showed a few places where we
hadn't made a function executeonly, so do that too. Its probably not
required, since I'm reasonably sure its impossible to load those
functions as packed arrays (they are all defined as operators), but lets
have a belt and braces approach, the additional time cost is negligible.

CVE: CVE-2018-18284
Upstream-Status: Backport [git://git.ghostscript.com/ghostpdl.git]

Signed-off-by: Hongxu Jia <hongxu.jia@windriver.com>
---
 Resource/Init/gs_diskn.ps |   2 +-
 Resource/Init/gs_dps.ps   |   2 +-
 Resource/Init/gs_epsf.ps  |   2 +-
 Resource/Init/gs_fonts.ps |   4 +-
 Resource/Init/gs_init.ps  |   2 +-
 Resource/Init/gs_setpd.ps | 100 ++++++++++++++++++++++++----------------------
 6 files changed, 58 insertions(+), 54 deletions(-)

diff --git a/Resource/Init/gs_diskn.ps b/Resource/Init/gs_diskn.ps
index 26ec0b5..fd694bc 100644
--- a/Resource/Init/gs_diskn.ps
+++ b/Resource/Init/gs_diskn.ps
@@ -61,7 +61,7 @@ systemdict begin
   % doesn't get run enough to justify the complication
   //.putdevparams
   //systemdict /.searchabledevs .forceundef
-} .bind odef % must be bound and hidden for .forceundef
+} .bind executeonly odef % must be bound and hidden for .forceundef
 
 % ------ extend filenameforall to handle wildcards in %dev% part of pattern -------%
 /filenameforall {
diff --git a/Resource/Init/gs_dps.ps b/Resource/Init/gs_dps.ps
index daf7b0f..00c14d5 100644
--- a/Resource/Init/gs_dps.ps
+++ b/Resource/Init/gs_dps.ps
@@ -124,7 +124,7 @@
   /savedinitialgstate .systemvar setgstate gsave
                 % Wrap up.
   end .setglobal
-} odef
+} bind executeonly odef
 
 % Check whether an object is a procedure.
 /.proccheck {			% <obj> .proccheck <bool>
diff --git a/Resource/Init/gs_epsf.ps b/Resource/Init/gs_epsf.ps
index e4037d9..2d0f677 100644
--- a/Resource/Init/gs_epsf.ps
+++ b/Resource/Init/gs_epsf.ps
@@ -31,7 +31,7 @@
 /EPSBoundingBoxState 5 def
 /EPSBoundingBoxSetState {
   //systemdict /EPSBoundingBoxState 3 -1 roll .forceput
-} .bind odef % .forceput must be bound and hidden
+} .bind executeonly odef % .forceput must be bound and hidden
 
 % Parse 4 numbers for a bounding box
 /EPSBoundingBoxParse { % (llx lly urx ury) -- llx lly urx ury true OR false
diff --git a/Resource/Init/gs_fonts.ps b/Resource/Init/gs_fonts.ps
index 72feff2..803faca 100644
--- a/Resource/Init/gs_fonts.ps
+++ b/Resource/Init/gs_fonts.ps
@@ -583,7 +583,7 @@ buildfontdict 3 /.buildfont3 cvx put
 } bind def
 /.setloadingfont {
    //systemdict /.loadingfont 3 -1 roll .forceput
-} .bind odef % .forceput must be bound and hidden
+} .bind executeonly odef % .forceput must be bound and hidden
 /.loadfont
  {              % Some buggy fonts leave extra junk on the stack,
                 % so we have to make a closure that records the stack depth
@@ -1012,7 +1012,7 @@ $error /SubstituteFont { } put
     dup length string copy
     .forceput setglobal
   } ifelse
-} .bind odef % must be bound and hidden for .forceput
+} .bind executeonly odef % must be bound and hidden for .forceput
 
 % Attempt to load a font from a file.
 /.tryloadfont {         % <fontname> .tryloadfont <font> true
diff --git a/Resource/Init/gs_init.ps b/Resource/Init/gs_init.ps
index f4c1053..07ee968 100644
--- a/Resource/Init/gs_init.ps
+++ b/Resource/Init/gs_init.ps
@@ -2230,7 +2230,7 @@ SAFER { .setsafeglobal } if
   /.localvmarray /.localvmdict /.localvmpackedarray /.localvmstring /.systemvmarray /.systemvmdict /.systemvmpackedarray /.systemvmstring /.systemvmfile /.systemvmlibfile
   /.systemvmSFD /.settrapparams /.currentsystemparams /.currentuserparams /.getsystemparam /.getuserparam /.setsystemparams /.setuserparams
   /.checkpassword /.locale_to_utf8 /.currentglobal /.gcheck /.imagepath
-  /.type /.writecvs /.setSMask /.currentSMask /.countexecstack /.execstack
+  /.type /.writecvs /.setSMask /.currentSMask /.countexecstack /.execstack /.applypolicies
 
   % Used by a free user in the Library of Congress. Apparently this is used to
   % draw a partial page, which is then filled in by the results of a barcode
diff --git a/Resource/Init/gs_setpd.ps b/Resource/Init/gs_setpd.ps
index afb4ffa..7c076ad 100644
--- a/Resource/Init/gs_setpd.ps
+++ b/Resource/Init/gs_setpd.ps
@@ -609,6 +609,23 @@ NOMEDIAATTRS {
 % and we replace the key in the <merged> dictionary with its prior value
 % (or remove it if it had no prior value).
 
+% These procedures are called with the following on the stack:
+%   <orig> <merged> <failed> <Policies> <key> <policy>
+% They are expected to consume the top 2 operands.
+% NOTE: we currently treat all values other than 0, 1, or 7 (for PageSize)
+% the same as 0, i.e., we signal an error.
+/0Policy {		% Set errorinfo and signal a configurationerror.
+  NOMEDIAATTRS {
+    % NOMEDIAATTRS means that the default policy is 7...
+    pop 2 index exch 7 put
+  } {
+    pop dup 4 index exch get 2 array astore
+    $error /errorinfo 3 -1 roll put
+    cleartomark
+    /setpagedevice .systemvar /configurationerror signalerror
+  } ifelse
+} bind executeonly odef
+
 % Making this an operator means we can properly hide
 % the contents - specifically .forceput
 /1Policy
@@ -617,59 +634,46 @@ 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 }
-   { 3 index exch .undef }
+  { 4 index 3 1 roll .forceput }
+  { 3 index exch .undef }
   ifelse
 } bind executeonly odef
 
-/.policyprocs mark
-% These procedures are called with the following on the stack:
-%   <orig> <merged> <failed> <Policies> <key> <policy>
-% They are expected to consume the top 2 operands.
-% NOTE: we currently treat all values other than 0, 1, or 7 (for PageSize)
-% the same as 0, i.e., we signal an error.
-%
-% M. Sweet, Easy Software Products:
-%
-% Define NOMEDIAATTRS to turn off the default (but unimplementable) media
-% selection policies for setpagedevice.  This is used by CUPS to support
-% the standard Adobe media attributes.
-  0 {		% Set errorinfo and signal a configurationerror.
-      NOMEDIAATTRS {
-        % NOMEDIAATTRS means that the default policy is 7...
-        pop 2 index exch 7 put
-      } {
-        pop dup 4 index exch get 2 array astore
-        $error /errorinfo 3 -1 roll put
-        cleartomark
-        /setpagedevice .systemvar /configurationerror signalerror
-      } ifelse
-  } bind
-  1 /1Policy load
-  7 {		% For PageSize only, just impose the request.
-        1 index /PageSize eq
-         { pop pop 1 index /PageSize 7 put }
-         { .policyprocs 0 get exec }
-        ifelse
-  } bind
-.dicttomark readonly def
-currentdict /1Policy undef
+/7Policy {		% For PageSize only, just impose the request.
+  1 index /PageSize eq
+  { pop pop 1 index /PageSize 7 put }
+  { .policyprocs 0 get exec }
+  ifelse
+} bind executeonly odef
 
 /.applypolicies		% <orig> <merged> <failed> .applypolicies
                         %   <orig> <merged'> <failed'>
- { 1 index /Policies get 1 index
-    { type /integertype eq
-       { pop		% already processed
-       }
-       { 2 copy .knownget not { 1 index /PolicyNotFound get } if
-                        % Stack: <orig> <merged> <failed> <Policies> <key>
-                        %   <policy>
-         .policyprocs 1 index .knownget not { .policyprocs 0 get } if exec
-       }
-      ifelse
-    }
-   forall pop
- } bind def
+{
+  1 index /Policies get 1 index
+  { type /integertype eq
+     {
+       pop		% already processed
+     }{
+       2 copy .knownget not { 1 index /PolicyNotFound get } if
+                      % Stack: <orig> <merged> <failed> <Policies> <key>
+                      %   <policy>
+        dup 1 eq {
+          1Policy
+        }{
+          dup 7 eq {
+            7Policy
+          }{
+            0Policy
+          } ifelse
+        } ifelse
+     } ifelse
+  }
+  forall pop
+} bind executeonly odef
+
+currentdict /0Policy undef
+currentdict /1Policy undef
+currentdict /7Policy undef
 
 % Prepare to present parameters to the device, by spreading them onto the
 % operand stack and removing any that shouldn't be presented.
@@ -1006,7 +1010,7 @@ SETPDDEBUG { (Installing.) = pstack flush } if
     .postinstall
   } ifelse
   setglobal           % return to original VM allocation mode
-} odef
+} bind executeonly odef
 
 % We break out the code after calling the Install procedure into a
 % separate procedure, since it is executed even if Install causes an error.
-- 
2.7.4

