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 ;;; Copyright © 2017 Leo Famulari ;;; Copyright © 2018 Tobias Geerinckx-Rice +;;; Copyright © 2018 Marius Bakke ;;; ;;; This file is part of GNU Guix. ;;; @@ -38,8 +39,10 @@ (define-module (gnu packages ghostscript) #: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 @@ (define-public ghostscript/fixed (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 -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 +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 % .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 +