From 1f9a91c86bd56acf57826b9b0e020ebe1953e2ae Mon Sep 17 00:00:00 2001
From: Chris Liddell <chris.liddell@artifex.com>
Date: Thu, 4 Oct 2018 10:42:13 +0100
Subject: [PATCH 3/5] Bug 699832: add control over hiding error handlers.

With a previous commit changing error handling in SAFER so the handler gets
passed a name object (rather than executable object), it is less critical to
hide the error handlers.

This introduces a -dSAFERERRORS option to force only use of the default error
handlers.

It also adds a .setsafererrors Postscript call, meaning a caller, without
-dSAFERERRORS, can create their own default error handlers (in errordict, as
normal), and then call .setsafererrors meaning their own handlers are always
called.

With -dSAFERERRORS or after a call to .setsafererrors, .setsafererrors is
removed.

CVE: CVE-2018-17961
Upstream-Status: Backport [git://git.ghostscript.com/ghostpdl.git]
Signed-off-by: Hongxu Jia <hongxu.jia@windriver.com>
---
 Resource/Init/gs_init.ps | 42 +++++++++++++++++++++++++++++------------
 psi/interp.c             | 49 ++++++++++++++++++++++++++++--------------------
 2 files changed, 59 insertions(+), 32 deletions(-)

diff --git a/Resource/Init/gs_init.ps b/Resource/Init/gs_init.ps
index bec307d..f952f32 100644
--- a/Resource/Init/gs_init.ps
+++ b/Resource/Init/gs_init.ps
@@ -188,6 +188,16 @@ currentdict /DELAYSAFER known { /DELAYSAFER //true def /NOSAFER //true def } if
   currentdict /PARANOIDSAFER known or	% PARANOIDSAFER is equivalent
 }
 ifelse def
+
+/SAFERERRORS
+currentdict /NOSAFERERRORS known
+{
+  //false
+}
+{
+  currentdict /SAFERERRORS known
+} ifelse def
+
 currentdict /SHORTERRORS known   /SHORTERRORS exch def
 currentdict /TTYPAUSE known   /TTYPAUSE exch def
 currentdict /WRITESYSTEMDICT known   /WRITESYSTEMDICT exch def
@@ -1123,12 +1133,23 @@ errordict begin
  } bind def
 end		% errordict
 
-% Put all the default handlers in gserrordict
-gserrordict
-errordict {2 index 3 1 roll put} forall
-noaccess pop
-% remove the non-standard errors from errordict
+gserrordict /unknownerror errordict /unknownerror get put
 errordict /unknownerror .undef
+
+/.SAFERERRORLIST ErrorNames def
+/.setsafererrors
+{
+% Put all the requested handlers in gserrordict
+  gserrordict
+  //.SAFERERRORLIST
+  {dup errordict exch get 2 index 3 1 roll put} forall
+  noaccess pop
+  systemdict /.setsafeerrors .forceundef
+  systemdict /.SAFERERRORLIST .forceundef
+} bind executeonly odef
+
+SAFERERRORS {.setsafererrors} if
+
 % Define a stable private copy of handleerror that we will always use under
 % JOBSERVER mode.
 /.GShandleerror errordict /handleerror get def
@@ -1760,18 +1781,15 @@ currentdict /.runlibfile .undef
 
 % Bind all the operators defined as procedures.
 /.bindoperators		% binds operators in currentdict
- { % Temporarily disable the typecheck error.
-   errordict /typecheck 2 copy get
-   errordict /typecheck { pop } put	% pop the command
+ {
    currentdict
     { dup type /operatortype eq
-       { % This might be a real operator, so bind might cause a typecheck,
-         % but we've made the error a no-op temporarily.
-         .bind
+       {
+         % This might be a real operator, so bind might cause a typecheck
+         {.bind} .internalstopped pop
        }
       if pop pop
     } forall
-   put
  } def
 DELAYBIND not { .bindoperators } if
 
diff --git a/psi/interp.c b/psi/interp.c
index 3dd5f7a..cd894f9 100644
--- a/psi/interp.c
+++ b/psi/interp.c
@@ -662,27 +662,18 @@ again:
     if (gs_errorname(i_ctx_p, code, &error_name) < 0)
         return code;            /* out-of-range error code! */
 
-    /*  If LockFilePermissions is true, we only refer to gserrordict, which
-     *  is not accessible to Postcript jobs
+    /*  We refer to gserrordict first, which is not accessible to Postcript jobs
+     *  If we're running with SAFERERRORS all the handlers are copied to gserrordict
+     *  so we'll always find the default one. If not SAFERERRORS, only gs specific
+     *  errors are in gserrordict.
      */
-    if (i_ctx_p->LockFilePermissions) {
-        if (((dict_find_string(systemdict, "gserrordict", &perrordict) <= 0 ||
-              dict_find(perrordict, &error_name, &epref) <= 0))
-            )
-            return code;            /* error name not in errordict??? */
-    }
-    else {
-        /*
-         * For greater Adobe compatibility, only the standard PostScript errors
-         * are defined in errordict; the rest are in gserrordict.
-         */
-        if (dict_find_string(systemdict, "errordict", &perrordict) <= 0 ||
-            (dict_find(perrordict, &error_name, &epref) <= 0 &&
-             (dict_find_string(systemdict, "gserrordict", &perrordict) <= 0 ||
-              dict_find(perrordict, &error_name, &epref) <= 0))
-            )
-            return code;            /* error name not in errordict??? */
-    }
+    if (dict_find_string(systemdict, "gserrordict", &perrordict) <= 0 ||
+        (dict_find(perrordict, &error_name, &epref) <= 0 &&
+         (dict_find_string(systemdict, "errordict", &perrordict) <= 0 ||
+          dict_find(perrordict, &error_name, &epref) <= 0))
+        )
+        return code;            /* error name not in errordict??? */
+
     doref = *epref;
     epref = &doref;
     /* Push the error object on the operand stack if appropriate. */
@@ -695,6 +686,24 @@ again:
         }
         *osp = *perror_object;
         errorexec_find(i_ctx_p, osp);
+        /* If using SAFER, hand a name object to the error handler, rather than the executable
+         * object/operator itself.
+         */
+        if (i_ctx_p->LockFilePermissions) {
+            code = obj_cvs(imemory, osp, buf + 2, 256, &rlen, (const byte **)&bufptr);
+            if (code < 0) {
+                const char *unknownstr = "--unknown--";
+                rlen = strlen(unknownstr);
+                memcpy(buf, unknownstr, rlen);
+            }
+            else {
+                buf[0] = buf[1] = buf[rlen + 2] = buf[rlen + 3] = '-';
+                rlen += 4;
+            }
+            code = name_ref(imemory, buf, rlen, osp, 1);
+            if (code < 0)
+                make_null(osp);
+        }
     }
     goto again;
 }
-- 
2.7.4

