From mboxrd@z Thu Jan 1 00:00:00 1970 Received: from eggs.gnu.org ([209.51.188.92]:39312) by lists.gnu.org with esmtp (Exim 4.71) (envelope-from ) id 1guhDg-00034F-C1 for guix-patches@gnu.org; Fri, 15 Feb 2019 12:24:14 -0500 Received: from Debian-exim by eggs.gnu.org with spam-scanned (Exim 4.71) (envelope-from ) id 1guhDb-0003fB-Iy for guix-patches@gnu.org; Fri, 15 Feb 2019 12:24:10 -0500 Received: from debbugs.gnu.org ([209.51.188.43]:50485) by eggs.gnu.org with esmtps (TLS1.0:RSA_AES_128_CBC_SHA1:16) (Exim 4.71) (envelope-from ) id 1guhDW-0003ds-Kn for guix-patches@gnu.org; Fri, 15 Feb 2019 12:24:04 -0500 Received: from Debian-debbugs by debbugs.gnu.org with local (Exim 4.84_2) (envelope-from ) id 1guhDW-0007ww-Ao for guix-patches@gnu.org; Fri, 15 Feb 2019 12:24:02 -0500 Subject: [bug#34491] [PATCH] gnu: libgd: Fix CVE-2019-{6977,6978}. Resent-Message-ID: Received: from eggs.gnu.org ([209.51.188.92]:39132) by lists.gnu.org with esmtp (Exim 4.71) (envelope-from ) id 1guhD3-0002cZ-WF for guix-patches@gnu.org; Fri, 15 Feb 2019 12:23:35 -0500 Received: from Debian-exim by eggs.gnu.org with spam-scanned (Exim 4.71) (envelope-from ) id 1guhD2-0003KL-6G for guix-patches@gnu.org; Fri, 15 Feb 2019 12:23:33 -0500 Received: from out2-smtp.messagingengine.com ([66.111.4.26]:52105) by eggs.gnu.org with esmtps (TLS1.0:DHE_RSA_AES_256_CBC_SHA1:32) (Exim 4.71) (envelope-from ) id 1guhD1-0003Io-Se for guix-patches@gnu.org; Fri, 15 Feb 2019 12:23:32 -0500 Received: from jasmine.lan (c-76-124-202-137.hsd1.pa.comcast.net [76.124.202.137]) by mail.messagingengine.com (Postfix) with ESMTPA id A0671E409E for ; Fri, 15 Feb 2019 12:23:28 -0500 (EST) From: Leo Famulari Date: Fri, 15 Feb 2019 12:23:24 -0500 Message-Id: MIME-Version: 1.0 Content-Transfer-Encoding: 8bit List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Errors-To: guix-patches-bounces+kyle=kyleam.com@gnu.org Sender: "Guix-patches" To: 34491@debbugs.gnu.org * gnu/packages/gd.scm (gd)[replacement]: New field. (gd/fixed): New variable. * gnu/packages/patches/gd-CVE-2019-6977.patch, gnu/packages/patches/gd-CVE-2019-6978.patch: New files. * gnu/local.mk (dist_patch_DATA): Add them. --- gnu/local.mk | 2 + gnu/packages/gd.scm | 11 + gnu/packages/patches/gd-CVE-2019-6977.patch | 36 +++ gnu/packages/patches/gd-CVE-2019-6978.patch | 301 ++++++++++++++++++++ 4 files changed, 350 insertions(+) create mode 100644 gnu/packages/patches/gd-CVE-2019-6977.patch create mode 100644 gnu/packages/patches/gd-CVE-2019-6978.patch diff --git a/gnu/local.mk b/gnu/local.mk index 0484b3e085..32edd2286d 100644 --- a/gnu/local.mk +++ b/gnu/local.mk @@ -792,6 +792,8 @@ dist_patch_DATA = \ %D%/packages/patches/gcr-fix-collection-tests-to-work-with-gpg-21.patch \ %D%/packages/patches/gd-CVE-2018-5711.patch \ %D%/packages/patches/gd-CVE-2018-1000222.patch \ + %D%/packages/patches/gd-CVE-2019-6977.patch \ + %D%/packages/patches/gd-CVE-2019-6978.patch \ %D%/packages/patches/gd-fix-tests-on-i686.patch \ %D%/packages/patches/gd-freetype-test-failure.patch \ %D%/packages/patches/gdm-CVE-2018-14424.patch \ diff --git a/gnu/packages/gd.scm b/gnu/packages/gd.scm index a53a4f2c2f..c08c1f6758 100644 --- a/gnu/packages/gd.scm +++ b/gnu/packages/gd.scm @@ -39,6 +39,7 @@ (define-public gd (package (name "gd") + (replacement gd/fixed) ;; Note: With libgd.org now pointing to github.com, genuine old ;; tarballs are no longer available. Notably, versions 2.0.x are ;; missing. @@ -94,6 +95,16 @@ most common applications of GD involve website development.") "See COPYING file in the distribution.")) (properties '((cpe-name . "libgd"))))) +(define-public gd/fixed + (hidden-package + (package + (inherit gd) + (source (origin + (inherit (package-source gd)) + (patches (append (origin-patches (package-source gd)) + (search-patches "gd-CVE-2019-6977.patch" + "gd-CVE-2019-6978.patch")))))))) + (define-public perl-gd (package (name "perl-gd") diff --git a/gnu/packages/patches/gd-CVE-2019-6977.patch b/gnu/packages/patches/gd-CVE-2019-6977.patch new file mode 100644 index 0000000000..b21a8ac619 --- /dev/null +++ b/gnu/packages/patches/gd-CVE-2019-6977.patch @@ -0,0 +1,36 @@ +Fix CVE-2019-6977: + +https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2019-6977 + +Patch copied from Debian: + +https://salsa.debian.org/debian/libgd2/commit/2d7d3b68bb79843e5271a05543e996fd5a3a8cd1 + +Description: Heap-based buffer overflow in gdImageColorMatch +Origin: other, https://gist.github.com/cmb69/1f36d285eb297ed326f5c821d7aafced +Bug-PHP: https://bugs.php.net/bug.php?id=77270 +Bug-Debian: https://bugs.debian.org/920645 +Bug-Debian-Security: https://security-tracker.debian.org/tracker/CVE-2019-6977 +Forwarded: no +Author: "Christoph M. Becker" +Last-Update: 2019-02-01 + +At least some of the image reading functions may return images which +use color indexes greater than or equal to im->colorsTotal. We cater +to this by always using a buffer size which is sufficient for +`gdMaxColors` in `gdImageColorMatch()`. +--- + +--- a/src/gd_color_match.c ++++ b/src/gd_color_match.c +@@ -31,8 +31,8 @@ BGD_DECLARE(int) gdImageColorMatch (gdIm + return -4; /* At least 1 color must be allocated */ + } + +- buf = (unsigned long *)gdMalloc(sizeof(unsigned long) * 5 * im2->colorsTotal); +- memset (buf, 0, sizeof(unsigned long) * 5 * im2->colorsTotal ); ++ buf = (unsigned long *)gdMalloc(sizeof(unsigned long) * 5 * gdMaxColors); ++ memset (buf, 0, sizeof(unsigned long) * 5 * gdMaxColors ); + + for (x=0; x < im1->sx; x++) { + for( y=0; ysy; y++ ) { diff --git a/gnu/packages/patches/gd-CVE-2019-6978.patch b/gnu/packages/patches/gd-CVE-2019-6978.patch new file mode 100644 index 0000000000..69fc5056fc --- /dev/null +++ b/gnu/packages/patches/gd-CVE-2019-6978.patch @@ -0,0 +1,301 @@ +Fix CVE-2019-6978: + +https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2019-6978 + +Patch copied from upstream source repository: + +https://github.com/libgd/libgd/commit/553702980ae89c83f2d6e254d62cf82e204956d0 + +From 553702980ae89c83f2d6e254d62cf82e204956d0 Mon Sep 17 00:00:00 2001 +From: "Christoph M. Becker" +Date: Thu, 17 Jan 2019 11:54:55 +0100 +Subject: [PATCH] Fix #492: Potential double-free in gdImage*Ptr() + +Whenever `gdImage*Ptr()` calls `gdImage*Ctx()` and the latter fails, we +must not call `gdDPExtractData()`; otherwise a double-free would +happen. Since `gdImage*Ctx()` are void functions, and we can't change +that for BC reasons, we're introducing static helpers which are used +internally. + +We're adding a regression test for `gdImageJpegPtr()`, but not for +`gdImageGifPtr()` and `gdImageWbmpPtr()` since we don't know how to +trigger failure of the respective `gdImage*Ctx()` calls. + +This potential security issue has been reported by Solmaz Salimi (aka. +Rooney). +--- + src/gd_gif_out.c | 18 +++++++++++++++--- + src/gd_jpeg.c | 20 ++++++++++++++++---- + src/gd_wbmp.c | 21 ++++++++++++++++++--- + tests/jpeg/.gitignore | 1 + + tests/jpeg/CMakeLists.txt | 1 + + tests/jpeg/Makemodule.am | 3 ++- + tests/jpeg/jpeg_ptr_double_free.c | 31 +++++++++++++++++++++++++++++++ + 7 files changed, 84 insertions(+), 11 deletions(-) + create mode 100644 tests/jpeg/jpeg_ptr_double_free.c + +diff --git a/src/gd_gif_out.c b/src/gd_gif_out.c +index 298a581..d5a9534 100644 +--- a/src/gd_gif_out.c ++++ b/src/gd_gif_out.c +@@ -99,6 +99,7 @@ static void char_init(GifCtx *ctx); + static void char_out(int c, GifCtx *ctx); + static void flush_char(GifCtx *ctx); + ++static int _gdImageGifCtx(gdImagePtr im, gdIOCtxPtr out); + + + +@@ -131,8 +132,11 @@ BGD_DECLARE(void *) gdImageGifPtr(gdImagePtr im, int *size) + void *rv; + gdIOCtx *out = gdNewDynamicCtx(2048, NULL); + if (out == NULL) return NULL; +- gdImageGifCtx(im, out); +- rv = gdDPExtractData(out, size); ++ if (!_gdImageGifCtx(im, out)) { ++ rv = gdDPExtractData(out, size); ++ } else { ++ rv = NULL; ++ } + out->gd_free(out); + return rv; + } +@@ -220,6 +224,12 @@ BGD_DECLARE(void) gdImageGif(gdImagePtr im, FILE *outFile) + + */ + BGD_DECLARE(void) gdImageGifCtx(gdImagePtr im, gdIOCtxPtr out) ++{ ++ _gdImageGifCtx(im, out); ++} ++ ++/* returns 0 on success, 1 on failure */ ++static int _gdImageGifCtx(gdImagePtr im, gdIOCtxPtr out) + { + gdImagePtr pim = 0, tim = im; + int interlace, BitsPerPixel; +@@ -231,7 +241,7 @@ BGD_DECLARE(void) gdImageGifCtx(gdImagePtr im, gdIOCtxPtr out) + based temporary image. */ + pim = gdImageCreatePaletteFromTrueColor(im, 1, 256); + if(!pim) { +- return; ++ return 1; + } + tim = pim; + } +@@ -247,6 +257,8 @@ BGD_DECLARE(void) gdImageGifCtx(gdImagePtr im, gdIOCtxPtr out) + /* Destroy palette based temporary image. */ + gdImageDestroy( pim); + } ++ ++ return 0; + } + + +diff --git a/src/gd_jpeg.c b/src/gd_jpeg.c +index fc05842..96ef430 100644 +--- a/src/gd_jpeg.c ++++ b/src/gd_jpeg.c +@@ -117,6 +117,8 @@ static void fatal_jpeg_error(j_common_ptr cinfo) + exit(99); + } + ++static int _gdImageJpegCtx(gdImagePtr im, gdIOCtx *outfile, int quality); ++ + /* + * Write IM to OUTFILE as a JFIF-formatted JPEG image, using quality + * QUALITY. If QUALITY is in the range 0-100, increasing values +@@ -231,8 +233,11 @@ BGD_DECLARE(void *) gdImageJpegPtr(gdImagePtr im, int *size, int quality) + void *rv; + gdIOCtx *out = gdNewDynamicCtx(2048, NULL); + if (out == NULL) return NULL; +- gdImageJpegCtx(im, out, quality); +- rv = gdDPExtractData(out, size); ++ if (!_gdImageJpegCtx(im, out, quality)) { ++ rv = gdDPExtractData(out, size); ++ } else { ++ rv = NULL; ++ } + out->gd_free(out); + return rv; + } +@@ -253,6 +258,12 @@ void jpeg_gdIOCtx_dest(j_compress_ptr cinfo, gdIOCtx *outfile); + + */ + BGD_DECLARE(void) gdImageJpegCtx(gdImagePtr im, gdIOCtx *outfile, int quality) ++{ ++ _gdImageJpegCtx(im, outfile, quality); ++} ++ ++/* returns 0 on success, 1 on failure */ ++static int _gdImageJpegCtx(gdImagePtr im, gdIOCtx *outfile, int quality) + { + struct jpeg_compress_struct cinfo; + struct jpeg_error_mgr jerr; +@@ -287,7 +298,7 @@ BGD_DECLARE(void) gdImageJpegCtx(gdImagePtr im, gdIOCtx *outfile, int quality) + if(row) { + gdFree(row); + } +- return; ++ return 1; + } + + cinfo.err->emit_message = jpeg_emit_message; +@@ -328,7 +339,7 @@ BGD_DECLARE(void) gdImageJpegCtx(gdImagePtr im, gdIOCtx *outfile, int quality) + if(row == 0) { + gd_error("gd-jpeg: error: unable to allocate JPEG row structure: gdCalloc returns NULL\n"); + jpeg_destroy_compress(&cinfo); +- return; ++ return 1; + } + + rowptr[0] = row; +@@ -405,6 +416,7 @@ BGD_DECLARE(void) gdImageJpegCtx(gdImagePtr im, gdIOCtx *outfile, int quality) + jpeg_finish_compress(&cinfo); + jpeg_destroy_compress(&cinfo); + gdFree(row); ++ return 0; + } + + +diff --git a/src/gd_wbmp.c b/src/gd_wbmp.c +index f19a1c9..a49bdbe 100644 +--- a/src/gd_wbmp.c ++++ b/src/gd_wbmp.c +@@ -88,6 +88,8 @@ int gd_getin(void *in) + return (gdGetC((gdIOCtx *)in)); + } + ++static int _gdImageWBMPCtx(gdImagePtr image, int fg, gdIOCtx *out); ++ + /* + Function: gdImageWBMPCtx + +@@ -100,6 +102,12 @@ int gd_getin(void *in) + out - the stream where to write + */ + BGD_DECLARE(void) gdImageWBMPCtx(gdImagePtr image, int fg, gdIOCtx *out) ++{ ++ _gdImageWBMPCtx(image, fg, out); ++} ++ ++/* returns 0 on success, 1 on failure */ ++static int _gdImageWBMPCtx(gdImagePtr image, int fg, gdIOCtx *out) + { + int x, y, pos; + Wbmp *wbmp; +@@ -107,7 +115,7 @@ BGD_DECLARE(void) gdImageWBMPCtx(gdImagePtr image, int fg, gdIOCtx *out) + /* create the WBMP */ + if((wbmp = createwbmp(gdImageSX(image), gdImageSY(image), WBMP_WHITE)) == NULL) { + gd_error("Could not create WBMP\n"); +- return; ++ return 1; + } + + /* fill up the WBMP structure */ +@@ -123,11 +131,15 @@ BGD_DECLARE(void) gdImageWBMPCtx(gdImagePtr image, int fg, gdIOCtx *out) + + /* write the WBMP to a gd file descriptor */ + if(writewbmp(wbmp, &gd_putout, out)) { ++ freewbmp(wbmp); + gd_error("Could not save WBMP\n"); ++ return 1; + } + + /* des submitted this bugfix: gdFree the memory. */ + freewbmp(wbmp); ++ ++ return 0; + } + + /* +@@ -271,8 +283,11 @@ BGD_DECLARE(void *) gdImageWBMPPtr(gdImagePtr im, int *size, int fg) + void *rv; + gdIOCtx *out = gdNewDynamicCtx(2048, NULL); + if (out == NULL) return NULL; +- gdImageWBMPCtx(im, fg, out); +- rv = gdDPExtractData(out, size); ++ if (!_gdImageWBMPCtx(im, fg, out)) { ++ rv = gdDPExtractData(out, size); ++ } else { ++ rv = NULL; ++ } + out->gd_free(out); + return rv; + } +#diff --git a/tests/jpeg/.gitignore b/tests/jpeg/.gitignore +#index c28aa87..13bcf04 100644 +#--- a/tests/jpeg/.gitignore +#+++ b/tests/jpeg/.gitignore +#@@ -3,5 +3,6 @@ +# /jpeg_empty_file +# /jpeg_im2im +# /jpeg_null +#+/jpeg_ptr_double_free +# /jpeg_read +# /jpeg_resolution +diff --git a/tests/jpeg/CMakeLists.txt b/tests/jpeg/CMakeLists.txt +index 19964b0..a8d8162 100644 +--- a/tests/jpeg/CMakeLists.txt ++++ b/tests/jpeg/CMakeLists.txt +@@ -2,6 +2,7 @@ IF(JPEG_FOUND) + LIST(APPEND TESTS_FILES + jpeg_empty_file + jpeg_im2im ++ jpeg_ptr_double_free + jpeg_null + ) + +diff --git a/tests/jpeg/Makemodule.am b/tests/jpeg/Makemodule.am +index 7e5d317..b89e169 100644 +--- a/tests/jpeg/Makemodule.am ++++ b/tests/jpeg/Makemodule.am +@@ -2,7 +2,8 @@ if HAVE_LIBJPEG + libgd_test_programs += \ + jpeg/jpeg_empty_file \ + jpeg/jpeg_im2im \ +- jpeg/jpeg_null ++ jpeg/jpeg_null \ ++ jpeg/jpeg_ptr_double_free + + if HAVE_LIBPNG + libgd_test_programs += \ +diff --git a/tests/jpeg/jpeg_ptr_double_free.c b/tests/jpeg/jpeg_ptr_double_free.c +new file mode 100644 +index 0000000..df5a510 +--- /dev/null ++++ b/tests/jpeg/jpeg_ptr_double_free.c +@@ -0,0 +1,31 @@ ++/** ++ * Test that failure to convert to JPEG returns NULL ++ * ++ * We are creating an image, set its width to zero, and pass this image to ++ * `gdImageJpegPtr()` which is supposed to fail, and as such should return NULL. ++ * ++ * See also ++ */ ++ ++ ++#include "gd.h" ++#include "gdtest.h" ++ ++ ++int main() ++{ ++ gdImagePtr src, dst; ++ int size; ++ ++ src = gdImageCreateTrueColor(1, 10); ++ gdTestAssert(src != NULL); ++ ++ src->sx = 0; /* this hack forces gdImageJpegPtr() to fail */ ++ ++ dst = gdImageJpegPtr(src, &size, 0); ++ gdTestAssert(dst == NULL); ++ ++ gdImageDestroy(src); ++ ++ return gdNumFailures(); ++} +-- +2.20.1 + -- 2.20.1