diff --git a/src/extra/gd/gd_jpeg.c b/src/extra/gd/gd_jpeg.c index 7e6dfbb..b270186 100644 --- a/src/extra/gd/gd_jpeg.c +++ b/src/extra/gd/gd_jpeg.c @@ -72,6 +72,8 @@ 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 @@ -93,8 +95,12 @@ gdImageJpegPtr (gdImagePtr im, int *size, int quality) { void *rv; gdIOCtx *out = gdNewDynamicCtx (2048, NULL); - gdImageJpegCtx (im, out, quality); - rv = gdDPExtractData (out, size); + if (out == NULL) return NULL; + if (!_gdImageJpegCtx(im, out, quality)) { + rv = gdDPExtractData(out, size); + } else { + rv = NULL; + } out->free (out); return rv; } @@ -104,6 +110,12 @@ static void jpeg_gdIOCtx_dest (j_compress_ptr cinfo, gdIOCtx * outfile); 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; int i, j, jidx; @@ -139,7 +151,7 @@ gdImageJpegCtx (gdImagePtr im, gdIOCtx * outfile, int quality) /* we're here courtesy of longjmp */ if (row) gdFree (row); - return; + return 1; } cinfo.err->error_exit = fatal_jpeg_error; @@ -173,7 +185,7 @@ gdImageJpegCtx (gdImagePtr im, gdIOCtx * outfile, int quality) fprintf (stderr, "gd-jpeg: error: unable to allocate JPEG row " "structure: gdCalloc returns NULL\n"); jpeg_destroy_compress (&cinfo); - return; + return 1; } rowptr[0] = row; @@ -254,6 +266,7 @@ error: #endif jpeg_destroy_compress (&cinfo); gdFree (row); + return 0; } gdImagePtr diff --git a/src/extra/gd/gd_jpeg.c.CVE-2019-6978 b/src/extra/gd/gd_jpeg.c.CVE-2019-6978 new file mode 100644 index 0000000..7e6dfbb --- /dev/null +++ b/src/extra/gd/gd_jpeg.c.CVE-2019-6978 @@ -0,0 +1,847 @@ + + +/* + * gd_jpeg.c: Read and write JPEG (JFIF) format image files using the + * gd graphics library (http://www.boutell.com/gd/). + * + * This software is based in part on the work of the Independent JPEG + * Group. For more information on the IJG JPEG software (and JPEG + * documentation, etc.), see ftp://ftp.uu.net/graphics/jpeg/. + * + * NOTE: IJG 12-bit JSAMPLE (BITS_IN_JSAMPLE == 12) mode is not + * supported at all on read in gd 2.0, and is not supported on write + * except for palette images, which is sort of pointless (TBB). Even that + * has never been tested according to DB. + * + * Copyright 2000 Doug Becker, mailto:thebeckers@home.com + * + * Modification 4/18/00 TBB: JPEG_DEBUG rather than just DEBUG, + * so VC++ builds don't spew to standard output, causing + * major CGI brain damage + */ + +/* TBB: move this up so include files are not brought in */ +#ifdef HAVE_LIBJPEG + +#include +#include +#include +#include +#include + +/* 1.8.1: remove dependency on jinclude.h */ +#include "jpeglib.h" +#include "jerror.h" +#include "gd.h" +#include "gdhelpers.h" + +static const char *const GD_JPEG_VERSION = "1.0"; + +typedef struct _jmpbuf_wrapper + { + jmp_buf jmpbuf; + } +jmpbuf_wrapper; + +/* Called by the IJG JPEG library upon encountering a fatal error */ +static void +fatal_jpeg_error (j_common_ptr cinfo) +{ + jmpbuf_wrapper *jmpbufw; + + fprintf (stderr, "gd-jpeg: JPEG library reports unrecoverable error: "); + (*cinfo->err->output_message) (cinfo); + fflush (stderr); + + jmpbufw = (jmpbuf_wrapper *) cinfo->client_data; + jpeg_destroy (cinfo); + + if (jmpbufw != 0) + { + longjmp (jmpbufw->jmpbuf, 1); + fprintf (stderr, "gd-jpeg: EXTREMELY fatal error: longjmp" + " returned control; terminating\n"); + } + else + { + fprintf (stderr, "gd-jpeg: EXTREMELY fatal error: jmpbuf" + " unrecoverable; terminating\n"); + } + + fflush (stderr); + exit (99); +} + +/* + * Write IM to OUTFILE as a JFIF-formatted JPEG image, using quality + * QUALITY. If QUALITY is in the range 0-100, increasing values + * represent higher quality but also larger image size. If QUALITY is + * negative, the IJG JPEG library's default quality is used (which + * should be near optimal for many applications). See the IJG JPEG + * library documentation for more details. */ + +void +gdImageJpeg (gdImagePtr im, FILE * outFile, int quality) +{ + gdIOCtx *out = gdNewFileCtx (outFile); + gdImageJpegCtx (im, out, quality); + out->free (out); +} + +void * +gdImageJpegPtr (gdImagePtr im, int *size, int quality) +{ + void *rv; + gdIOCtx *out = gdNewDynamicCtx (2048, NULL); + gdImageJpegCtx (im, out, quality); + rv = gdDPExtractData (out, size); + out->free (out); + return rv; +} + +static void jpeg_gdIOCtx_dest (j_compress_ptr cinfo, gdIOCtx * outfile); + +void +gdImageJpegCtx (gdImagePtr im, gdIOCtx * outfile, int quality) +{ + struct jpeg_compress_struct cinfo; + struct jpeg_error_mgr jerr; + int i, j, jidx; + /* volatile so we can gdFree it on return from longjmp */ + volatile JSAMPROW row = 0; + JSAMPROW rowptr[1]; + jmpbuf_wrapper jmpbufw; + JDIMENSION nlines; + char comment[255]; + +#ifdef JPEG_DEBUG + printf ("gd-jpeg: gd JPEG version %s\n", GD_JPEG_VERSION); + printf ("gd-jpeg: JPEG library version %d, %d-bit sample values\n", + JPEG_LIB_VERSION, BITS_IN_JSAMPLE); + if (!im->trueColor) + { + for (i = 0; i < im->colorsTotal; i++) + { + if (!im->open[i]) + printf ("gd-jpeg: gd colormap index %d: (%d, %d, %d)\n", i, + im->red[i], im->green[i], im->blue[i]); + } + } +#endif /* JPEG_DEBUG */ + + memset (&cinfo, 0, sizeof (cinfo)); + memset (&jerr, 0, sizeof (jerr)); + + cinfo.err = jpeg_std_error (&jerr); + cinfo.client_data = &jmpbufw; + if (setjmp (jmpbufw.jmpbuf) != 0) + { + /* we're here courtesy of longjmp */ + if (row) + gdFree (row); + return; + } + + cinfo.err->error_exit = fatal_jpeg_error; + + jpeg_create_compress (&cinfo); + + cinfo.image_width = im->sx; + cinfo.image_height = im->sy; + cinfo.input_components = 3; /* # of color components per pixel */ + cinfo.in_color_space = JCS_RGB; /* colorspace of input image */ + jpeg_set_defaults (&cinfo); + if (quality >= 0) + jpeg_set_quality (&cinfo, quality, TRUE); + + /* If user requests interlace, translate that to progressive JPEG */ + if (gdImageGetInterlaced (im)) + { +#ifdef JPEG_DEBUG + printf ("gd-jpeg: interlace set, outputting progressive" + " JPEG image\n"); +#endif + jpeg_simple_progression (&cinfo); + } + + jpeg_gdIOCtx_dest (&cinfo, outfile); + + row = (JSAMPROW) gdCalloc (1, cinfo.image_width * cinfo.input_components + * sizeof (JSAMPLE)); + if (row == 0) + { + fprintf (stderr, "gd-jpeg: error: unable to allocate JPEG row " + "structure: gdCalloc returns NULL\n"); + jpeg_destroy_compress (&cinfo); + return; + } + + rowptr[0] = row; + + jpeg_start_compress (&cinfo, TRUE); + + sprintf (comment, "CREATOR: gd-jpeg v%s (using IJG JPEG v%d),", + GD_JPEG_VERSION, JPEG_LIB_VERSION); + if (quality >= 0) + sprintf (comment + strlen (comment), " quality = %d\n", + quality); + else + strcat (comment + strlen (comment), " default quality\n"); + jpeg_write_marker (&cinfo, JPEG_COM, (unsigned char *) comment, + (unsigned int) strlen (comment)); + if (im->trueColor) + { +#if BITS_IN_JSAMPLE == 12 + fprintf (stderr, "gd-jpeg: error: jpeg library was compiled for 12-bit\n" + "precision. This is mostly useless, because JPEGs on the web are\n" + "8-bit and such versions of the jpeg library won't read or write\n" + "them. GD doesn't support these unusual images. Edit your\n" + "jmorecfg.h file to specify the correct precision and completely\n" + "'make clean' and 'make install' libjpeg again. Sorry.\n"); + goto error; +#endif /* BITS_IN_JSAMPLE == 12 */ + for (i = 0; i < im->sy; i++) + { + for (jidx = 0, j = 0; j < im->sx; j++) + { + int val = im->tpixels[i][j]; + row[jidx++] = gdTrueColorGetRed (val); + row[jidx++] = gdTrueColorGetGreen (val); + row[jidx++] = gdTrueColorGetBlue (val); + } + + nlines = jpeg_write_scanlines (&cinfo, rowptr, 1); + if (nlines != 1) + fprintf (stderr, "gd_jpeg: warning: jpeg_write_scanlines" + " returns %u -- expected 1\n", nlines); + } + } + else + { + for (i = 0; i < im->sy; i++) + { + for (jidx = 0, j = 0; j < im->sx; j++) + { + int idx = im->pixels[i][j]; + + /* + * NB: Although gd RGB values are ints, their max value is + * 255 (see the documentation for gdImageColorAllocate()) + * -- perfect for 8-bit JPEG encoding (which is the norm) + */ +#if BITS_IN_JSAMPLE == 8 + row[jidx++] = im->red[idx]; + row[jidx++] = im->green[idx]; + row[jidx++] = im->blue[idx]; +#elif BITS_IN_JSAMPLE == 12 + row[jidx++] = im->red[idx] << 4; + row[jidx++] = im->green[idx] << 4; + row[jidx++] = im->blue[idx] << 4; +#else +#error IJG JPEG library BITS_IN_JSAMPLE value must be 8 or 12 +#endif + } + + nlines = jpeg_write_scanlines (&cinfo, rowptr, 1); + if (nlines != 1) + fprintf (stderr, "gd_jpeg: warning: jpeg_write_scanlines" + " returns %u -- expected 1\n", nlines); + } + } + jpeg_finish_compress (&cinfo); +#if BITS_IN_JSAMPLE == 12 +error: +#endif + jpeg_destroy_compress (&cinfo); + gdFree (row); +} + +gdImagePtr +gdImageCreateFromJpeg (FILE * inFile) +{ + gdImagePtr im; + gdIOCtx *in = gdNewFileCtx (inFile); + im = gdImageCreateFromJpegCtx (in); + in->free (in); + return im; +} + +static void + jpeg_gdIOCtx_src (j_decompress_ptr cinfo, + gdIOCtx * infile); + +/* + * Create a gd-format image from the JPEG-format INFILE. Returns the + * image, or NULL upon error. + */ +gdImagePtr +gdImageCreateFromJpegCtx (gdIOCtx * infile) +{ + struct jpeg_decompress_struct cinfo; + struct jpeg_error_mgr jerr; + jmpbuf_wrapper jmpbufw; + /* volatile so we can gdFree them after longjmp */ + volatile JSAMPROW row = 0; + volatile gdImagePtr im = 0; + JSAMPROW rowptr[1]; + JDIMENSION i, j; + int retval; + JDIMENSION nrows; + +#ifdef JPEG_DEBUG + printf ("gd-jpeg: gd JPEG version %s\n", GD_JPEG_VERSION); + printf ("gd-jpeg: JPEG library version %d, %d-bit sample values\n", + JPEG_LIB_VERSION, BITS_IN_JSAMPLE); +#endif + + memset (&cinfo, 0, sizeof (cinfo)); + memset (&jerr, 0, sizeof (jerr)); + + cinfo.err = jpeg_std_error (&jerr); + cinfo.client_data = &jmpbufw; + if (setjmp (jmpbufw.jmpbuf) != 0) + { + /* we're here courtesy of longjmp */ + if (row) + gdFree (row); + if (im) + gdImageDestroy (im); + return 0; + } + + cinfo.err->error_exit = fatal_jpeg_error; + + jpeg_create_decompress (&cinfo); + + jpeg_gdIOCtx_src (&cinfo, infile); + + retval = jpeg_read_header (&cinfo, TRUE); + if (retval != JPEG_HEADER_OK) + fprintf (stderr, "gd-jpeg: warning: jpeg_read_header returns" + " %d, expected %d\n", retval, JPEG_HEADER_OK); + + if (cinfo.image_height > INT_MAX) + fprintf (stderr, "gd-jpeg: warning: JPEG image height (%u) is" + " greater than INT_MAX (%d) (and thus greater than" + " gd can handle)", cinfo.image_height, + INT_MAX); + + if (cinfo.image_width > INT_MAX) + fprintf (stderr, "gd-jpeg: warning: JPEG image width (%u) is" + " greater than INT_MAX (%d) (and thus greater than" + " gd can handle)\n", cinfo.image_width, INT_MAX); + + im = gdImageCreateTrueColor ((int) cinfo.image_width, + (int) cinfo.image_height); + if (im == 0) + { + fprintf (stderr, "gd-jpeg error: cannot allocate gdImage" + " struct\n"); + goto error; + } + + /* + * Force the image into RGB colorspace, but don't + * reduce the number of colors anymore (GD 2.0) + */ + cinfo.out_color_space = JCS_RGB; + + if (jpeg_start_decompress (&cinfo) != TRUE) + fprintf (stderr, "gd-jpeg: warning: jpeg_start_decompress" + " reports suspended data source\n"); + +#ifdef JPEG_DEBUG + printf ("gd-jpeg: JPEG image information:"); + if (cinfo.saw_JFIF_marker) + printf (" JFIF version %d.%.2d", + (int) cinfo.JFIF_major_version, + (int) cinfo.JFIF_minor_version); + else if (cinfo.saw_Adobe_marker) + printf (" Adobe format"); + else + printf (" UNKNOWN format"); + + printf (" %ux%u (raw) / %ux%u (scaled) %d-bit", cinfo.image_width, + cinfo.image_height, cinfo.output_width, + cinfo.output_height, cinfo.data_precision); + printf (" %s", (cinfo.progressive_mode ? "progressive" : + "baseline")); + printf (" image, %d quantized colors, ", + cinfo.actual_number_of_colors); + + switch (cinfo.jpeg_color_space) + { + case JCS_GRAYSCALE: + printf ("grayscale"); + break; + + case JCS_RGB: + printf ("RGB"); + break; + + case JCS_YCbCr: + printf ("YCbCr (a.k.a. YUV)"); + break; + + case JCS_CMYK: + printf ("CMYK"); + break; + + case JCS_YCCK: + printf ("YCbCrK"); + break; + + default: + printf ("UNKNOWN (value: %d)", (int) cinfo.jpeg_color_space); + break; + } + printf (" colorspace\n"); + fflush (stdout); +#endif /* JPEG_DEBUG */ + + /* REMOVED by TBB 2/12/01. This field of the structure is + documented as private, and sure enough it's gone in the + latest libjpeg, replaced by something else. Unfortunately + there is still no right way to find out if the file was + progressive or not; just declare your intent before you + write one by calling gdImageInterlace(im, 1) yourself. + After all, we're not really supposed to rework JPEGs and + write them out again anyway. Lossy compression, remember? */ +#if 0 + gdImageInterlace (im, cinfo.progressive_mode != 0); +#endif + if (cinfo.output_components != 3) + { + fprintf (stderr, "gd-jpeg: error: JPEG color quantization" + " request resulted in output_components == %d" + " (expected 3)\n", cinfo.output_components); + goto error; + } + +#if BITS_IN_JSAMPLE == 12 + fprintf (stderr, "gd-jpeg: error: jpeg library was compiled for 12-bit\n" + "precision. This is mostly useless, because JPEGs on the web are\n" + "8-bit and such versions of the jpeg library won't read or write\n" + "them. GD doesn't support these unusual images. Edit your\n" + "jmorecfg.h file to specify the correct precision and completely\n" + "'make clean' and 'make install' libjpeg again. Sorry.\n"); + goto error; +#endif /* BITS_IN_JSAMPLE == 12 */ + + row = gdCalloc (cinfo.output_width * 3, sizeof (JSAMPLE)); + if (row == 0) + { + fprintf (stderr, "gd-jpeg: error: unable to allocate row for" + " JPEG scanline: gdCalloc returns NULL\n"); + goto error; + } + rowptr[0] = row; + + for (i = 0; i < cinfo.output_height; i++) + { + nrows = jpeg_read_scanlines (&cinfo, rowptr, 1); + if (nrows != 1) + { + fprintf (stderr, "gd-jpeg: error: jpeg_read_scanlines" + " returns %u, expected 1\n", nrows); + goto error; + } + + for (j = 0; j < cinfo.output_width; j++) + im->tpixels[i][j] = gdTrueColor (row[j * 3], row[j * 3 + 1], + row[j * 3 + 2]); + } + + if (jpeg_finish_decompress (&cinfo) != TRUE) + fprintf (stderr, "gd-jpeg: warning: jpeg_finish_decompress" + " reports suspended data source\n"); + + + jpeg_destroy_decompress (&cinfo); + gdFree (row); + return im; + +error: + jpeg_destroy_decompress (&cinfo); + if (row) + gdFree (row); + if (im) + gdImageDestroy (im); + return 0; +} + +/* + + * gdIOCtx JPEG data sources and sinks, T. Boutell + * almost a simple global replace from T. Lane's stdio versions. + * + */ + +/* Different versions of libjpeg use either 'jboolean' or 'boolean', and + some platforms define 'boolean', and so forth. Deal with this + madness by typedeffing 'safeboolean' to 'boolean' if HAVE_BOOLEAN + is already set, because this is the test that libjpeg uses. + Otherwise, typedef it to int, because that's what libjpeg does + if HAVE_BOOLEAN is not defined. -TBB */ + +#ifdef HAVE_BOOLEAN +typedef boolean safeboolean; +#else +typedef int safeboolean; +#endif /* HAVE_BOOLEAN */ + +/* Expanded data source object for gdIOCtx input */ + +typedef struct + { + struct jpeg_source_mgr pub; /* public fields */ + + gdIOCtx *infile; /* source stream */ + unsigned char *buffer; /* start of buffer */ + safeboolean start_of_file; /* have we gotten any data yet? */ + + } +my_source_mgr; + +typedef my_source_mgr *my_src_ptr; + +#define INPUT_BUF_SIZE 4096 /* choose an efficiently fread'able size */ + +/* + * Initialize source --- called by jpeg_read_header + * before any data is actually read. + */ + +static void +init_source (j_decompress_ptr cinfo) +{ + my_src_ptr src = (my_src_ptr) cinfo->src; + + /* We reset the empty-input-file flag for each image, + * but we don't clear the input buffer. + * This is correct behavior for reading a series of images from one source. + */ + src->start_of_file = TRUE; +} + + +/* + * Fill the input buffer --- called whenever buffer is emptied. + * + * In typical applications, this should read fresh data into the buffer + * (ignoring the current state of next_input_byte & bytes_in_buffer), + * reset the pointer & count to the start of the buffer, and return TRUE + * indicating that the buffer has been reloaded. It is not necessary to + * fill the buffer entirely, only to obtain at least one more byte. + * + * There is no such thing as an EOF return. If the end of the file has been + * reached, the routine has a choice of ERREXIT() or inserting fake data into + * the buffer. In most cases, generating a warning message and inserting a + * fake EOI marker is the best course of action --- this will allow the + * decompressor to output however much of the image is there. However, + * the resulting error message is misleading if the real problem is an empty + * input file, so we handle that case specially. + * + * In applications that need to be able to suspend compression due to input + * not being available yet, a FALSE return indicates that no more data can be + * obtained right now, but more may be forthcoming later. In this situation, + * the decompressor will return to its caller (with an indication of the + * number of scanlines it has read, if any). The application should resume + * decompression after it has loaded more data into the input buffer. Note + * that there are substantial restrictions on the use of suspension --- see + * the documentation. + * + * When suspending, the decompressor will back up to a convenient restart point + * (typically the start of the current MCU). next_input_byte & bytes_in_buffer + * indicate where the restart point will be if the current call returns FALSE. + * Data beyond this point must be rescanned after resumption, so move it to + * the front of the buffer rather than discarding it. + */ + +#define END_JPEG_SEQUENCE "\r\n[*]--:END JPEG:--[*]\r\n" + +static safeboolean +fill_input_buffer (j_decompress_ptr cinfo) +{ + my_src_ptr src = (my_src_ptr) cinfo->src; + int nbytes = 0; + + /* size_t got; */ + /* char *s; */ + memset (src->buffer, 0, INPUT_BUF_SIZE); + + while (nbytes < INPUT_BUF_SIZE) + { + + int got = gdGetBuf (src->buffer + nbytes, + INPUT_BUF_SIZE - nbytes, + src->infile); + + if ((got == EOF) || (got == 0)) + { + /* EOF or error. If we got any data, don't worry about it. + If we didn't, then this is unexpected. */ + if (!nbytes) + { + nbytes = -1; + } + break; + } + + nbytes += got; + } + + if (nbytes <= 0) + { + if (src->start_of_file) /* Treat empty input file as fatal error */ + ERREXIT (cinfo, JERR_INPUT_EMPTY); + WARNMS (cinfo, JWRN_JPEG_EOF); + /* Insert a fake EOI marker */ + src->buffer[0] = (unsigned char) 0xFF; + src->buffer[1] = (unsigned char) JPEG_EOI; + nbytes = 2; + } + + src->pub.next_input_byte = src->buffer; + src->pub.bytes_in_buffer = nbytes; + src->start_of_file = FALSE; + + return TRUE; +} + + +/* + * Skip data --- used to skip over a potentially large amount of + * uninteresting data (such as an APPn marker). + * + * Writers of suspendable-input applications must note that skip_input_data + * is not granted the right to give a suspension return. If the skip extends + * beyond the data currently in the buffer, the buffer can be marked empty so + * that the next read will cause a fill_input_buffer call that can suspend. + * Arranging for additional bytes to be discarded before reloading the input + * buffer is the application writer's problem. + */ + +static void +skip_input_data (j_decompress_ptr cinfo, long num_bytes) +{ + my_src_ptr src = (my_src_ptr) cinfo->src; + + /* Just a dumb implementation for now. Not clear that being smart is worth + * any trouble anyway --- large skips are infrequent. + */ + if (num_bytes > 0) + { + while (num_bytes > (long) src->pub.bytes_in_buffer) + { + num_bytes -= (long) src->pub.bytes_in_buffer; + (void) fill_input_buffer (cinfo); + /* note we assume that fill_input_buffer will never return FALSE, + * so suspension need not be handled. + */ + } + src->pub.next_input_byte += (size_t) num_bytes; + src->pub.bytes_in_buffer -= (size_t) num_bytes; + } +} + + +/* + * An additional method that can be provided by data source modules is the + * resync_to_restart method for error recovery in the presence of RST markers. + * For the moment, this source module just uses the default resync method + * provided by the JPEG library. That method assumes that no backtracking + * is possible. + */ + + +/* + * Terminate source --- called by jpeg_finish_decompress + * after all data has been read. Often a no-op. + * + * NB: *not* called by jpeg_abort or jpeg_destroy; surrounding + * application must deal with any cleanup that should happen even + * for error exit. + */ + +static void +term_source (j_decompress_ptr cinfo) +{ + (void)cinfo; +#if 0 +/* never used */ + my_src_ptr src = (my_src_ptr) cinfo->src; +#endif +} + + +/* + * Prepare for input from a gdIOCtx stream. + * The caller must have already opened the stream, and is responsible + * for closing it after finishing decompression. + */ + +void +jpeg_gdIOCtx_src (j_decompress_ptr cinfo, + gdIOCtx * infile) +{ + my_src_ptr src; + + /* The source object and input buffer are made permanent so that a series + * of JPEG images can be read from the same file by calling jpeg_gdIOCtx_src + * only before the first one. (If we discarded the buffer at the end of + * one image, we'd likely lose the start of the next one.) + * This makes it unsafe to use this manager and a different source + * manager serially with the same JPEG object. Caveat programmer. + */ + if (cinfo->src == NULL) + { /* first time for this JPEG object? */ + cinfo->src = (struct jpeg_source_mgr *) + (*cinfo->mem->alloc_small) ((j_common_ptr) cinfo, JPOOL_PERMANENT, + sizeof (my_source_mgr)); + src = (my_src_ptr) cinfo->src; + src->buffer = (unsigned char *) + (*cinfo->mem->alloc_small) ((j_common_ptr) cinfo, JPOOL_PERMANENT, + INPUT_BUF_SIZE * sizeof (unsigned char)); + } + + src = (my_src_ptr) cinfo->src; + src->pub.init_source = init_source; + src->pub.fill_input_buffer = fill_input_buffer; + src->pub.skip_input_data = skip_input_data; + src->pub.resync_to_restart = jpeg_resync_to_restart; /* use default method */ + src->pub.term_source = term_source; + src->infile = infile; + src->pub.bytes_in_buffer = 0; /* forces fill_input_buffer on first read */ + src->pub.next_input_byte = NULL; /* until buffer loaded */ +} + +/* Expanded data destination object for stdio output */ + +typedef struct +{ + struct jpeg_destination_mgr pub; /* public fields */ + gdIOCtx *outfile; /* target stream */ + unsigned char *buffer; /* start of buffer */ +} +my_destination_mgr; + +typedef my_destination_mgr *my_dest_ptr; + +#define OUTPUT_BUF_SIZE 4096 /* choose an efficiently fwrite'able size */ + +/* + * Initialize destination --- called by jpeg_start_compress + * before any data is actually written. + */ + +static void +init_destination (j_compress_ptr cinfo) +{ + my_dest_ptr dest = (my_dest_ptr) cinfo->dest; + + /* Allocate the output buffer --- it will be released when done with image */ + dest->buffer = (unsigned char *) + (*cinfo->mem->alloc_small) ((j_common_ptr) cinfo, JPOOL_IMAGE, + OUTPUT_BUF_SIZE * sizeof (unsigned char)); + + dest->pub.next_output_byte = dest->buffer; + dest->pub.free_in_buffer = OUTPUT_BUF_SIZE; +} + + +/* + * Empty the output buffer --- called whenever buffer fills up. + * + * In typical applications, this should write the entire output buffer + * (ignoring the current state of next_output_byte & free_in_buffer), + * reset the pointer & count to the start of the buffer, and return TRUE + * indicating that the buffer has been dumped. + * + * In applications that need to be able to suspend compression due to output + * overrun, a FALSE return indicates that the buffer cannot be emptied now. + * In this situation, the compressor will return to its caller (possibly with + * an indication that it has not accepted all the supplied scanlines). The + * application should resume compression after it has made more room in the + * output buffer. Note that there are substantial restrictions on the use of + * suspension --- see the documentation. + * + * When suspending, the compressor will back up to a convenient restart point + * (typically the start of the current MCU). next_output_byte & free_in_buffer + * indicate where the restart point will be if the current call returns FALSE. + * Data beyond this point will be regenerated after resumption, so do not + * write it out when emptying the buffer externally. + */ + +static safeboolean +empty_output_buffer (j_compress_ptr cinfo) +{ + my_dest_ptr dest = (my_dest_ptr) cinfo->dest; + + if (gdPutBuf (dest->buffer, OUTPUT_BUF_SIZE, dest->outfile) != + (size_t) OUTPUT_BUF_SIZE) + ERREXIT (cinfo, JERR_FILE_WRITE); + + dest->pub.next_output_byte = dest->buffer; + dest->pub.free_in_buffer = OUTPUT_BUF_SIZE; + + return TRUE; +} + + +/* + * Terminate destination --- called by jpeg_finish_compress + * after all data has been written. Usually needs to flush buffer. + * + * NB: *not* called by jpeg_abort or jpeg_destroy; surrounding + * application must deal with any cleanup that should happen even + * for error exit. + */ + +static void +term_destination (j_compress_ptr cinfo) +{ + my_dest_ptr dest = (my_dest_ptr) cinfo->dest; + int datacount = OUTPUT_BUF_SIZE - dest->pub.free_in_buffer; + + /* Write any data remaining in the buffer */ + if (datacount > 0) + { + if (gdPutBuf (dest->buffer, datacount, dest->outfile) != datacount) + ERREXIT (cinfo, JERR_FILE_WRITE); + } +} + + +/* + * Prepare for output to a stdio stream. + * The caller must have already opened the stream, and is responsible + * for closing it after finishing compression. + */ + +void +jpeg_gdIOCtx_dest (j_compress_ptr cinfo, gdIOCtx * outfile) +{ + my_dest_ptr dest; + + /* The destination object is made permanent so that multiple JPEG images + * can be written to the same file without re-executing jpeg_stdio_dest. + * This makes it dangerous to use this manager and a different destination + * manager serially with the same JPEG object, because their private object + * sizes may be different. Caveat programmer. + */ + if (cinfo->dest == NULL) + { /* first time for this JPEG object? */ + cinfo->dest = (struct jpeg_destination_mgr *) + (*cinfo->mem->alloc_small) ((j_common_ptr) cinfo, JPOOL_PERMANENT, + sizeof (my_destination_mgr)); + } + + dest = (my_dest_ptr) cinfo->dest; + dest->pub.init_destination = init_destination; + dest->pub.empty_output_buffer = empty_output_buffer; + dest->pub.term_destination = term_destination; + dest->outfile = outfile; +} + +#endif /* HAVE_JPEG */ diff --git a/src/extra/gd/gd_wbmp.c b/src/extra/gd/gd_wbmp.c index f1258da..bbe9f2d 100644 --- a/src/extra/gd/gd_wbmp.c +++ b/src/extra/gd/gd_wbmp.c @@ -85,6 +85,7 @@ gd_getin (void *in) return (gdGetC ((gdIOCtx *) in)); } +static int _gdImageWBMPCtx(gdImagePtr image, int fg, gdIOCtx *out); /* gdImageWBMPCtx ** -------------- @@ -98,6 +99,12 @@ gd_getin (void *in) 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 +114,7 @@ gdImageWBMPCtx (gdImagePtr image, int fg, gdIOCtx * out) if ((wbmp = createwbmp (gdImageSX (image), gdImageSY (image), WBMP_WHITE)) == NULL) { fprintf (stderr, "Could not create WBMP\n"); - return; + return 1; } /* fill up the WBMP structure */ @@ -126,9 +133,16 @@ gdImageWBMPCtx (gdImagePtr image, int fg, gdIOCtx * out) /* write the WBMP to a gd file descriptor */ if (writewbmp (wbmp, &gd_putout, out)) + { fprintf (stderr, "Could not save WBMP\n"); + freewbmp (wbmp); + return 1; + } + /* des submitted this bugfix: gdFree the memory. */ freewbmp (wbmp); + + return 0; } @@ -214,8 +228,12 @@ gdImageWBMPPtr (gdImagePtr im, int *size, int fg) { void *rv; gdIOCtx *out = gdNewDynamicCtx (2048, NULL); - gdImageWBMPCtx (im, fg, out); - rv = gdDPExtractData (out, size); + if (out == NULL) return NULL; + if (!_gdImageWBMPCtx(im, fg, out)) { + rv = gdDPExtractData(out, size); + } else { + rv = NULL; + } out->free (out); return rv; } diff --git a/src/extra/gd/gd_wbmp.c.CVE-2019-6978 b/src/extra/gd/gd_wbmp.c.CVE-2019-6978 new file mode 100644 index 0000000..f1258da --- /dev/null +++ b/src/extra/gd/gd_wbmp.c.CVE-2019-6978 @@ -0,0 +1,221 @@ + + +/* + WBMP: Wireless Bitmap Type 0: B/W, Uncompressed Bitmap + Specification of the WBMP format can be found in the file: + SPEC-WAESpec-19990524.pdf + You can download the WAP specification on: http://www.wapforum.com/ + + gd_wbmp.c + + Copyright (C) Johan Van den Brande (johan@vandenbrande.com) + + Fixed: gdImageWBMPPtr, gdImageWBMP + + Recoded: gdImageWBMPCtx for use with my wbmp library + (wbmp library included, but you can find the latest distribution + at http://www.vandenbrande.com/wbmp) + + Implemented: gdImageCreateFromWBMPCtx, gdImageCreateFromWBMP + + --------------------------------------------------------------------------- + + Parts of this code are from Maurice Smurlo. + + + ** Copyright (C) Maurice Szmurlo --- T-SIT --- January 2000 + ** (Maurice.Szmurlo@info.unicaen.fr) + + ** Permission to use, copy, modify, and distribute this software and its + ** documentation for any purpose and without fee is hereby granted, provided + ** that the above copyright notice appear in all copies and that both that + ** copyright notice and this permission notice appear in supporting + ** documentation. This software is provided "as is" without express or + ** implied warranty. + + --------------------------------------------------------------------------- + Parts od this code are inspired by 'pbmtowbmp.c' and 'wbmptopbm.c' by + Terje Sannum . + ** + ** Permission to use, copy, modify, and distribute this software and its + ** documentation for any purpose and without fee is hereby granted, provided + ** that the above copyright notice appear in all copies and that both that + ** copyright notice and this permission notice appear in supporting + ** documentation. This software is provided "as is" without express or + ** implied warranty. + ** + --------------------------------------------------------------------------- + + Todo: + + gdCreateFromWBMP function for reading WBMP files + + ---------------------------------------------------------------------------- + */ + +#include +#include +#include +#include +#include + +#include "wbmp.h" + + +/* gd_putout + ** --------- + ** Wrapper around gdPutC for use with writewbmp + ** + */ +static void +gd_putout (int i, void *out) +{ + gdPutC (i, (gdIOCtx *) out); +} + + +/* gd_getin + ** -------- + ** Wrapper around gdGetC for use with readwbmp + ** + */ +static int +gd_getin (void *in) +{ + return (gdGetC ((gdIOCtx *) in)); +} + + +/* gdImageWBMPCtx + ** -------------- + ** Write the image as a wbmp file + ** Parameters are: + ** image: gd image structure; + ** fg: the index of the foreground color. any other value will be + ** considered as background and will not be written + ** out: the stream where to write + */ +void +gdImageWBMPCtx (gdImagePtr image, int fg, gdIOCtx * out) +{ + + int x, y, pos; + Wbmp *wbmp; + + + /* create the WBMP */ + if ((wbmp = createwbmp (gdImageSX (image), gdImageSY (image), WBMP_WHITE)) == NULL) + { + fprintf (stderr, "Could not create WBMP\n"); + return; + } + + /* fill up the WBMP structure */ + pos = 0; + for (y = 0; y < gdImageSY (image); y++) + { + for (x = 0; x < gdImageSX (image); x++) + { + if (gdImageGetPixel (image, x, y) == fg) + { + wbmp->bitmap[pos] = WBMP_BLACK; + } + pos++; + } + } + + /* write the WBMP to a gd file descriptor */ + if (writewbmp (wbmp, &gd_putout, out)) + fprintf (stderr, "Could not save WBMP\n"); + /* des submitted this bugfix: gdFree the memory. */ + freewbmp (wbmp); +} + + +/* gdImageCreateFromWBMPCtx + ** ------------------------ + ** Create a gdImage from a WBMP file input from an gdIOCtx + */ +gdImagePtr +gdImageCreateFromWBMPCtx (gdIOCtx * infile) +{ + /* FILE *wbmp_file; */ + Wbmp *wbmp; + gdImagePtr im = NULL; + int black, white; + int col, row, pos; + + if (readwbmp (&gd_getin, infile, &wbmp)) + return (NULL); + + if (!(im = gdImageCreate (wbmp->width, wbmp->height))) + { + freewbmp (wbmp); + return (NULL); + } + + /* create the background color */ + white = gdImageColorAllocate (im, 255, 255, 255); + /* create foreground color */ + black = gdImageColorAllocate (im, 0, 0, 0); + + /* fill in image (in a wbmp 1 = white/ 0 = black) */ + pos = 0; + for (row = 0; row < wbmp->height; row++) + { + for (col = 0; col < wbmp->width; col++) + { + if (wbmp->bitmap[pos++] == WBMP_WHITE) + { + gdImageSetPixel (im, col, row, white); + } + else + { + gdImageSetPixel (im, col, row, black); + } + } + } + + freewbmp (wbmp); + + return (im); +} + + +/* gdImageCreateFromWBMP + ** --------------------- + */ +gdImagePtr +gdImageCreateFromWBMP (FILE * inFile) +{ + gdImagePtr im; + gdIOCtx *in = gdNewFileCtx (inFile); + im = gdImageCreateFromWBMPCtx (in); + in->free (in); + return (im); +} + +/* gdImageWBMP + ** ----------- + */ +void +gdImageWBMP (gdImagePtr im, int fg, FILE * outFile) +{ + gdIOCtx *out = gdNewFileCtx (outFile); + gdImageWBMPCtx (im, fg, out); + out->free (out); +} + +/* gdImageWBMPPtr + ** -------------- + */ +void * +gdImageWBMPPtr (gdImagePtr im, int *size, int fg) +{ + void *rv; + gdIOCtx *out = gdNewDynamicCtx (2048, NULL); + gdImageWBMPCtx (im, fg, out); + rv = gdDPExtractData (out, size); + out->free (out); + return rv; +}