summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--gnu/local.mk3
-rw-r--r--gnu/packages/ghostscript.scm37
-rw-r--r--gnu/packages/patches/ghostscript-CVE-2018-10194.patch52
-rw-r--r--gnu/packages/patches/ghostscript-CVE-2018-16509.patch193
-rw-r--r--gnu/packages/patches/ghostscript-bug-699708.patch160
5 files changed, 389 insertions, 56 deletions
diff --git a/gnu/local.mk b/gnu/local.mk
index 278afbb257..0407fdc597 100644
--- a/gnu/local.mk
+++ b/gnu/local.mk
@@ -726,7 +726,8 @@ dist_patch_DATA = \
%D%/packages/patches/geoclue-config.patch \
%D%/packages/patches/ghc-8.0-fall-back-to-madv_dontneed.patch \
%D%/packages/patches/ghc-dont-pass-linker-flags-via-response-files.patch \
- %D%/packages/patches/ghostscript-CVE-2018-10194.patch \
+ %D%/packages/patches/ghostscript-CVE-2018-16509.patch \
+ %D%/packages/patches/ghostscript-bug-699708.patch \
%D%/packages/patches/ghostscript-no-header-id.patch \
%D%/packages/patches/ghostscript-no-header-uuid.patch \
%D%/packages/patches/ghostscript-no-header-creationdate.patch \
diff --git a/gnu/packages/ghostscript.scm b/gnu/packages/ghostscript.scm
index 1240b1dc16..518e18397b 100644
--- a/gnu/packages/ghostscript.scm
+++ b/gnu/packages/ghostscript.scm
@@ -7,6 +7,7 @@
;;; Copyright © 2017 Efraim Flashner <efraim@flashner.co.il>
;;; Copyright © 2017 Leo Famulari <leo@famulari.name>
;;; Copyright © 2018 Tobias Geerinckx-Rice <me@tobias.gr>
+;;; Copyright © 2018 Marius Bakke <mbakke@fastmail.com>
;;;
;;; This file is part of GNU Guix.
;;;
@@ -38,8 +39,10 @@
#:use-module ((guix licenses) #:prefix license:)
#:use-module (guix packages)
#:use-module (guix download)
+ #:use-module (guix utils)
#:use-module (guix build-system gnu)
- #:use-module (guix build-system trivial))
+ #:use-module (guix build-system trivial)
+ #:use-module (srfi srfi-1))
(define-public lcms
(package
@@ -255,11 +258,39 @@ output file formats and printers.")
(hidden-package
(package
(inherit ghostscript)
+ (version "9.24")
(source
(origin
(inherit (package-source ghostscript))
- (patches (append (origin-patches (package-source ghostscript))
- (search-patches "ghostscript-CVE-2018-10194.patch"))))))))
+ (uri (string-append "https://github.com/ArtifexSoftware/"
+ "ghostpdl-downloads/releases/download/gs"
+ (string-delete #\. version)
+ "/ghostscript-" version ".tar.xz"))
+ (sha256
+ (base32
+ "1mk922rnml93w2g42yxiyn8xqanc50cm65irrgh0b6lp4kgifjfl"))
+ (patches (search-patches "ghostscript-CVE-2018-16509.patch"
+ "ghostscript-bug-699708.patch"
+ "ghostscript-no-header-creationdate.patch"
+ "ghostscript-no-header-id.patch"
+ "ghostscript-no-header-uuid.patch"))))
+ (arguments
+ (substitute-keyword-arguments (package-arguments ghostscript)
+ ((#:configure-flags flags)
+ ;; Notice that we removed the 'ghostscript-runpath' patch above.
+ ;; The reason is that it conflicts with an upstream change that
+ ;; takes LDFLAGS into account.
+ `(cons (string-append "LDFLAGS=-Wl,-rpath="
+ (assoc-ref %outputs "out") "/lib")
+ ,flags))
+ ((#:phases phases)
+ `(modify-phases ,phases
+ (add-before 'configure 'create-output-directory
+ (lambda* (#:key outputs #:allow-other-keys)
+ ;; Unfortunately the configure script refuses to function if
+ ;; the directory specified as -rpath does not already exist.
+ (mkdir-p (string-append (assoc-ref outputs "out") "/lib"))
+ #t)))))))))
(define-public ghostscript/x
(package/inherit ghostscript
diff --git a/gnu/packages/patches/ghostscript-CVE-2018-10194.patch b/gnu/packages/patches/ghostscript-CVE-2018-10194.patch
deleted file mode 100644
index 242e57c27c..0000000000
--- a/gnu/packages/patches/ghostscript-CVE-2018-10194.patch
+++ /dev/null
@@ -1,52 +0,0 @@
-Fix CVE-2018-10194:
-
-https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2018-10194
-https://bugs.ghostscript.com/show_bug.cgi?id=699255
-
-Patch copied from upstream source repository:
-
-https://git.ghostscript.com/?p=ghostpdl.git;a=commit;h=39b1e54b2968620723bf32e96764c88797714879
-
-From 39b1e54b2968620723bf32e96764c88797714879 Mon Sep 17 00:00:00 2001
-From: Ken Sharp <ken.sharp@artifex.com>
-Date: Wed, 18 Apr 2018 15:46:32 +0100
-Subject: [PATCH] pdfwrite - Guard against trying to output an infinite number
-
-Bug #699255 " Buffer overflow on pprintg1 due to mishandle postscript file data to pdf"
-
-The file uses an enormous parameter to xyxhow, causing an overflow in
-the calculation of text positioning (value > 1e39).
-
-Since this is basically a nonsense value, and PostScript only supports
-real values up to 1e38, this patch follows the same approach as for
-a degenerate CTM, and treats it as 0.
-
-Adobe Acrobat Distiller throws a limitcheck error, so we could do that
-instead if this approach proves to be a problem.
----
- devices/vector/gdevpdts.c | 7 ++++++-
- 1 file changed, 6 insertions(+), 1 deletion(-)
-
-diff --git a/devices/vector/gdevpdts.c b/devices/vector/gdevpdts.c
-index 848ad781f..172fe6bc3 100644
---- a/devices/vector/gdevpdts.c
-+++ b/devices/vector/gdevpdts.c
-@@ -103,9 +103,14 @@ append_text_move(pdf_text_state_t *pts, double dw)
- static int
- set_text_distance(gs_point *pdist, double dx, double dy, const gs_matrix *pmat)
- {
-- int code = gs_distance_transform_inverse(dx, dy, pmat, pdist);
-+ int code;
- double rounded;
-
-+ if (dx > 1e38 || dy > 1e38)
-+ code = gs_error_undefinedresult;
-+ else
-+ code = gs_distance_transform_inverse(dx, dy, pmat, pdist);
-+
- if (code == gs_error_undefinedresult) {
- /* The CTM is degenerate.
- Can't know the distance in user space.
---
-2.18.0
-
diff --git a/gnu/packages/patches/ghostscript-CVE-2018-16509.patch b/gnu/packages/patches/ghostscript-CVE-2018-16509.patch
new file mode 100644
index 0000000000..50ffa3cb98
--- /dev/null
+++ b/gnu/packages/patches/ghostscript-CVE-2018-16509.patch
@@ -0,0 +1,193 @@
+Ghostscript 9.24 was released with an incomplete fix for CVE-2018-16509:
+https://nvd.nist.gov/vuln/detail/CVE-2018-16509
+https://bugs.chromium.org/p/project-zero/issues/detail?id=1640#c19
+https://bugs.ghostscript.com/show_bug.cgi?id=699718
+
+The reproducers no longer work after applying these commits:
+
+https://git.ghostscript.com/?p=ghostpdl.git;a=commitdiff;h=5812b1b78fc4d36fdc293b7859de69241140d590
+https://git.ghostscript.com/?p=ghostpdl.git;a=commitdiff;h=e914f1da46e33decc534486598dc3eadf69e6efb
+https://git.ghostscript.com/?p=ghostpdl.git;a=commitdiff;h=3e5d316b72e3965b7968bb1d96baa137cd063ac6
+https://git.ghostscript.com/?p=ghostpdl.git;a=commitdiff;h=643b24dbd002fb9c131313253c307cf3951b3d47
+
+This patch is a "squashed" version of those.
+
+diff --git a/Resource/Init/gs_setpd.ps b/Resource/Init/gs_setpd.ps
+index bba3c8c0e..8fa7c51df 100644
+--- a/Resource/Init/gs_setpd.ps
++++ b/Resource/Init/gs_setpd.ps
+@@ -95,27 +95,41 @@ level2dict begin
+ { % Since setpagedevice doesn't create new device objects,
+ % we must (carefully) reinstall the old parameters in
+ % the same device.
+- .currentpagedevice pop //null currentdevice //null .trysetparams
++ .currentpagedevice pop //null currentdevice //null
++ { .trysetparams } .internalstopped
++ {
++ //null
++ } if
+ dup type /booleantype eq
+ { pop pop }
+- { % This should never happen!
++ {
+ SETPDDEBUG { (Error in .trysetparams!) = pstack flush } if
+- cleartomark pop pop pop
++ {cleartomark pop pop pop} .internalstopped pop
++ % if resetting the entire device state failed, at least put back the
++ % security related key
++ currentdevice //null //false mark /.LockSafetyParams
++ currentpagedevice /.LockSafetyParams .knownget not
++ {systemdict /SAFER .knownget not {//false} } if
++ .putdeviceparamsonly
+ /.installpagedevice cvx /rangecheck signalerror
+ }
+ ifelse pop pop
+ % A careful reading of the Red Book reveals that an erasepage
+ % should occur, but *not* an initgraphics.
+ erasepage .beginpage
+- } bind def
++ } bind executeonly def
+
+ /.uninstallpagedevice
+- { 2 .endpage { .currentnumcopies //false .outputpage } if
++ {
++ {2 .endpage { .currentnumcopies //false .outputpage } if} .internalstopped pop
+ nulldevice
+ } bind def
+
+ (%grestorepagedevice) cvn
+- { .uninstallpagedevice grestore .installpagedevice
++ {
++ .uninstallpagedevice
++ grestore
++ .installpagedevice
+ } bind def
+
+ (%grestoreallpagedevice) cvn
+diff --git a/psi/zdevice2.c b/psi/zdevice2.c
+index 0c7080d57..159a0c0d9 100644
+--- a/psi/zdevice2.c
++++ b/psi/zdevice2.c
+@@ -251,8 +251,8 @@ z2currentgstate(i_ctx_t *i_ctx_p)
+ /* ------ Wrappers for operators that reset the graphics state. ------ */
+
+ /* Check whether we need to call out to restore the page device. */
+-static bool
+-restore_page_device(const gs_gstate * pgs_old, const gs_gstate * pgs_new)
++static int
++restore_page_device(i_ctx_t *i_ctx_p, const gs_gstate * pgs_old, const gs_gstate * pgs_new)
+ {
+ gx_device *dev_old = gs_currentdevice(pgs_old);
+ gx_device *dev_new;
+@@ -260,9 +260,10 @@ restore_page_device(const gs_gstate * pgs_old, const gs_gstate * pgs_new)
+ gx_device *dev_t2;
+ bool samepagedevice = obj_eq(dev_old->memory, &gs_int_gstate(pgs_old)->pagedevice,
+ &gs_int_gstate(pgs_new)->pagedevice);
++ bool LockSafetyParams = dev_old->LockSafetyParams;
+
+ if ((dev_t1 = (*dev_proc(dev_old, get_page_device)) (dev_old)) == 0)
+- return false;
++ return 0;
+ /* If we are going to putdeviceparams in a callout, we need to */
+ /* unlock temporarily. The device will be re-locked as needed */
+ /* by putdeviceparams from the pgs_old->pagedevice dict state. */
+@@ -271,23 +272,51 @@ restore_page_device(const gs_gstate * pgs_old, const gs_gstate * pgs_new)
+ dev_new = gs_currentdevice(pgs_new);
+ if (dev_old != dev_new) {
+ if ((dev_t2 = (*dev_proc(dev_new, get_page_device)) (dev_new)) == 0)
+- return false;
+- if (dev_t1 != dev_t2)
+- return true;
++ samepagedevice = true;
++ else if (dev_t1 != dev_t2)
++ samepagedevice = false;
++ }
++
++ if (LockSafetyParams && !samepagedevice) {
++ const int required_ops = 512;
++ const int required_es = 32;
++
++ /* The %grestorepagedevice must complete: the biggest danger
++ is operand stack overflow. As we use get/putdeviceparams
++ that means pushing all the device params onto the stack,
++ pdfwrite having by far the largest number of parameters
++ at (currently) 212 key/value pairs - thus needing (currently)
++ 424 entries on the op stack. Allowing for working stack
++ space, and safety margin.....
++ */
++ if (required_ops + ref_stack_count(&o_stack) >= ref_stack_max_count(&o_stack)) {
++ gs_currentdevice(pgs_old)->LockSafetyParams = LockSafetyParams;
++ return_error(gs_error_stackoverflow);
++ }
++ /* We also want enough exec stack space - 32 is an overestimate of
++ what we need to complete the Postscript call out.
++ */
++ if (required_es + ref_stack_count(&e_stack) >= ref_stack_max_count(&e_stack)) {
++ gs_currentdevice(pgs_old)->LockSafetyParams = LockSafetyParams;
++ return_error(gs_error_execstackoverflow);
++ }
+ }
+ /*
+ * The current implementation of setpagedevice just sets new
+ * parameters in the same device object, so we have to check
+ * whether the page device dictionaries are the same.
+ */
+- return !samepagedevice;
++ return samepagedevice ? 0 : 1;
+ }
+
+ /* - grestore - */
+ static int
+ z2grestore(i_ctx_t *i_ctx_p)
+ {
+- if (!restore_page_device(igs, gs_gstate_saved(igs)))
++ int code = restore_page_device(i_ctx_p, igs, gs_gstate_saved(igs));
++ if (code < 0) return code;
++
++ if (code == 0)
+ return gs_grestore(igs);
+ return push_callout(i_ctx_p, "%grestorepagedevice");
+ }
+@@ -297,7 +326,9 @@ static int
+ z2grestoreall(i_ctx_t *i_ctx_p)
+ {
+ for (;;) {
+- if (!restore_page_device(igs, gs_gstate_saved(igs))) {
++ int code = restore_page_device(i_ctx_p, igs, gs_gstate_saved(igs));
++ if (code < 0) return code;
++ if (code == 0) {
+ bool done = !gs_gstate_saved(gs_gstate_saved(igs));
+
+ gs_grestore(igs);
+@@ -328,11 +359,15 @@ z2restore(i_ctx_t *i_ctx_p)
+ if (code < 0) return code;
+
+ while (gs_gstate_saved(gs_gstate_saved(igs))) {
+- if (restore_page_device(igs, gs_gstate_saved(igs)))
++ code = restore_page_device(i_ctx_p, igs, gs_gstate_saved(igs));
++ if (code < 0) return code;
++ if (code > 0)
+ return push_callout(i_ctx_p, "%restore1pagedevice");
+ gs_grestore(igs);
+ }
+- if (restore_page_device(igs, gs_gstate_saved(igs)))
++ code = restore_page_device(i_ctx_p, igs, gs_gstate_saved(igs));
++ if (code < 0) return code;
++ if (code > 0)
+ return push_callout(i_ctx_p, "%restorepagedevice");
+
+ code = dorestore(i_ctx_p, asave);
+@@ -355,9 +390,12 @@ static int
+ z2setgstate(i_ctx_t *i_ctx_p)
+ {
+ os_ptr op = osp;
++ int code;
+
+ check_stype(*op, st_igstate_obj);
+- if (!restore_page_device(igs, igstate_ptr(op)))
++ code = restore_page_device(i_ctx_p, igs, igstate_ptr(op));
++ if (code < 0) return code;
++ if (code == 0)
+ return zsetgstate(i_ctx_p);
+ return push_callout(i_ctx_p, "%setgstatepagedevice");
+ }
diff --git a/gnu/packages/patches/ghostscript-bug-699708.patch b/gnu/packages/patches/ghostscript-bug-699708.patch
new file mode 100644
index 0000000000..1567be1c6f
--- /dev/null
+++ b/gnu/packages/patches/ghostscript-bug-699708.patch
@@ -0,0 +1,160 @@
+Additional security fix that missed 9.24.
+
+Taken from upstream:
+http://git.ghostscript.com/?p=ghostpdl.git;a=commitdiff;h=fb713b3818b52d8a6cf62c951eba2e1795ff9624
+
+From fb713b3818b52d8a6cf62c951eba2e1795ff9624 Mon Sep 17 00:00:00 2001
+From: Chris Liddell <chris.liddell@artifex.com>
+Date: Thu, 6 Sep 2018 09:16:22 +0100
+Subject: [PATCH] Bug 699708 (part 1): 'Hide' non-replaceable error handlers
+ for SAFER
+
+We already had a 'private' dictionary for non-standard errors: gserrordict.
+
+This now includes all the default error handlers, the dictionary is made
+noaccess and all the prodedures are bound and executeonly.
+
+When running with -dSAFER, in the event of a Postscript error, instead of
+pulling the handler from errordict, we'll pull it from gserrordict - thus
+malicious input cannot trigger problems by the use of custom error handlers.
+
+errordict remains open and writeable, so files such as the Quality Logic tests
+that install their own handlers will still 'work', with the exception that the
+custom error handlers will not be called.
+
+This is a 'first pass', 'sledgehammer' approach: a nice addition would to allow
+an integrator to specify a list of errors that are not to be replaced (for
+example, embedded applications would probably want to ensure that VMerror is
+always handled as they intend).
+---
+ Resource/Init/gs_init.ps | 29 ++++++++++++++++++-----------
+ psi/interp.c | 30 +++++++++++++++++++++---------
+ 2 files changed, 39 insertions(+), 20 deletions(-)
+
+diff --git a/Resource/Init/gs_init.ps b/Resource/Init/gs_init.ps
+index 071c39205..bc8b7951c 100644
+--- a/Resource/Init/gs_init.ps
++++ b/Resource/Init/gs_init.ps
+@@ -881,7 +881,7 @@ userdict /.currentresourcefile //null put
+ { not exch pop exit } { pop } ifelse
+ }
+ for exch pop .quit
+- } bind def
++ } bind executeonly def
+ /.errorhandler % <command> <errorname> .errorhandler -
+ { % Detect an internal 'stopped'.
+ 1 .instopped { //null eq { pop pop stop } if } if
+@@ -926,7 +926,7 @@ userdict /.currentresourcefile //null put
+ $error /globalmode get $error /.nosetlocal get and .setglobal
+ $error /.inerror //false put
+ stop
+- } bind def
++ } bind executeonly def
+ % Define the standard handleerror. We break out the printing procedure
+ % (.printerror) so that it can be extended for binary output
+ % if the Level 2 facilities are present.
+@@ -976,7 +976,7 @@ userdict /.currentresourcefile //null put
+ ifelse % newerror
+ end
+ flush
+- } bind def
++ } bind executeonly def
+ /.printerror_long % long error printout,
+ % $error is on the dict stack
+ { % Push the (anonymous) stack printing procedure.
+@@ -1053,14 +1053,14 @@ userdict /.currentresourcefile //null put
+ { (Current file position is ) print position = }
+ if
+
+- } bind def
++ } bind executeonly def
+ % Define a procedure for clearing the error indication.
+ /.clearerror
+ { $error /newerror //false put
+ $error /errorname //null put
+ $error /errorinfo //null put
+ 0 .setoserrno
+- } bind def
++ } bind executeonly def
+
+ % Define $error. This must be in local VM.
+ .currentglobal //false .setglobal
+@@ -1086,11 +1086,15 @@ end
+ /errordict ErrorNames length 3 add dict
+ .forcedef % errordict is local, systemdict is global
+ .setglobal % back to global VM
+-% For greater Adobe compatibility, we put all non-standard errors in a
+-% separate dictionary, gserrordict. It does not need to be in local VM,
+-% because PostScript programs do not access it.
++% gserrordict contains all the default error handling methods, but unlike
++% errordict it is noaccess after creation (also it is in global VM).
++% When running 'SAFER', we'll ignore the contents of errordict, which
++% may have been tampered with by the running job, and always use gserrordict
++% gserrordict also contains any non-standard errors, for better compatibility
++% with Adobe.
++%
+ % NOTE: the name gserrordict is known to the interpreter.
+-/gserrordict 5 dict def
++/gserrordict ErrorNames length 3 add dict def
+ % Register an error in errordict. We make this a procedure because we only
+ % register the Level 1 errors here: the rest are registered by "feature"
+ % files. However, ErrorNames contains all of the error names regardless of
+@@ -1119,8 +1123,11 @@ errordict begin
+ } bind def
+ end % errordict
+
+-% Put non-standard errors in gserrordict.
+-gserrordict /unknownerror errordict /unknownerror get put
++% 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
+ errordict /unknownerror .undef
+ % Define a stable private copy of handleerror that we will always use under
+ % JOBSERVER mode.
+diff --git a/psi/interp.c b/psi/interp.c
+index c27b70dca..d41a9d3f5 100644
+--- a/psi/interp.c
++++ b/psi/interp.c
+@@ -661,16 +661,28 @@ again:
+ return code;
+ if (gs_errorname(i_ctx_p, code, &error_name) < 0)
+ return code; /* out-of-range error code! */
+- /*
+- * For greater Adobe compatibility, only the standard PostScript errors
+- * are defined in errordict; the rest are in gserrordict.
++
++ /* If LockFilePermissions is true, we only refer to gserrordict, which
++ * is not accessible to Postcript jobs
+ */
+- 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 (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??? */
++ }
+ doref = *epref;
+ epref = &doref;
+ /* Push the error object on the operand stack if appropriate. */
+--
+2.18.0
+