diff --git a/ppd/rastertops.c b/ppd/rastertops.c index dc8bac7d..a6de81ba 100644 --- a/ppd/rastertops.c +++ b/ppd/rastertops.c @@ -247,8 +247,13 @@ write_flate(cups_raster_t *ras, // I - Image data // by deflate unsigned char *pixdata, *convertedpix; - unsigned char in[header.cupsBytesPerLine * 6], // Input data buffer - out[header.cupsBytesPerLine * 6]; // Output data buffer + size_t line_bytes = header.cupsBytesPerLine ? + (size_t)header.cupsBytesPerLine : 1; + unsigned char in[line_bytes * 6], // Input data buffer + out[line_bytes * 6]; // Output data buffer + + if (header.cupsBytesPerLine == 0 || header.cupsHeight == 0) + return (Z_DATA_ERROR); // allocate deflate state strm.zalloc = Z_NULL; @@ -267,17 +272,37 @@ write_flate(cups_raster_t *ras, // I - Image data // compress until end of file do { - pixdata = malloc(header.cupsBytesPerLine); - cupsRasterReadPixels(ras, pixdata, header.cupsBytesPerLine); + if ((pixdata = malloc(header.cupsBytesPerLine)) == NULL) + { + (void)deflateEnd(&strm); + return (Z_MEM_ERROR); + } + if (cupsRasterReadPixels(ras, pixdata, header.cupsBytesPerLine) != + header.cupsBytesPerLine) + { + free(pixdata); + (void)deflateEnd(&strm); + return (Z_DATA_ERROR); + } if (flag) { - convertedpix = malloc(header.cupsBytesPerLine * 6); + if ((convertedpix = malloc(header.cupsBytesPerLine * 6)) == NULL) + { + free(pixdata); + (void)deflateEnd(&strm); + return (Z_MEM_ERROR); + } convert_pixels(pixdata,convertedpix, header.cupsBytesPerLine); alloc = header.cupsBytesPerLine * 6; } else { - convertedpix = malloc(header.cupsBytesPerLine); + if ((convertedpix = malloc(header.cupsBytesPerLine)) == NULL) + { + free(pixdata); + (void)deflateEnd(&strm); + return (Z_MEM_ERROR); + } memcpy(convertedpix, pixdata, header.cupsBytesPerLine); alloc = header.cupsBytesPerLine; } @@ -307,6 +332,7 @@ write_flate(cups_raster_t *ras, // I - Image data if (fwrite(out, 1, have, doc->outputfp) != have) { (void)deflateEnd(&strm); + free(pixdata); if (convertedpix != NULL) free(convertedpix); return (Z_ERRNO); @@ -410,7 +436,8 @@ ppdFilterRasterToPS(int inputfd, // I - File descriptor input stream cups_page_header_t header; // Page header from file int empty, // Is the input empty? Page = 0, // variable for counting the pages - ret; // Return value of deflate compression + ret, // Return value of deflate compression + status = 0; // Filter return status cf_logfunc_t log = data->logfunc; void *ld = data->logdata; cf_filter_iscanceledfunc_t iscanceled = data->iscanceledfunc; @@ -516,7 +543,12 @@ ppdFilterRasterToPS(int inputfd, // I - File descriptor input stream // Write the compressed image data ret = write_flate(ras, header, &doc); if (ret != Z_OK) + { z_error(ret, &doc); + write_end_page(&doc); + status = 1; + break; + } write_end_page(&doc); } @@ -542,5 +574,5 @@ ppdFilterRasterToPS(int inputfd, // I - File descriptor input stream fclose(outputfp); close(outputfd); - return (0); + return (status); } diff --git a/ppd/test_ppd_filter.c b/ppd/test_ppd_filter.c index fe2da460..442057fc 100644 --- a/ppd/test_ppd_filter.c +++ b/ppd/test_ppd_filter.c @@ -6,7 +6,7 @@ // Licensed under Apache License v2.0. See the file "LICENSE" for more // information. // -// Tests covered (50 assertions across 12 groups): +// Tests covered (51 assertions across 13 groups): // // Group 1 (T01-T08) NULL / argument guards. ppdFilterLoadPPDFile // rejects NULL / empty / non-existent paths with @@ -135,6 +135,7 @@ #include #include #include +#include #include "test-internal.h" #include @@ -416,6 +417,31 @@ stub_filter(int inputfd, int outputfd, } +// +// 'find_bytes()' - Locate a byte sequence inside a buffer that may contain NULs. +// + +static const char * +find_bytes(const char *haystack, + size_t hay_len, + const char *needle, + size_t needle_len) +{ + size_t i; + + if (needle_len == 0) + return (haystack); + if (hay_len < needle_len) + return (NULL); + + for (i = 0; i <= hay_len - needle_len; i ++) + if (memcmp(haystack + i, needle, needle_len) == 0) + return (haystack + i); + + return (NULL); +} + + int // O - Exit status (0 = all pass) main(void) { @@ -1268,6 +1294,93 @@ main(void) free_filter_data(&data); + // ========================================================================= + // Group 13: ppdFilterRasterToPS truncated row safety (T51) + // ========================================================================= + + // T51 — A two-page CUPS Raster document where page 1 contains marker bytes + // and page 2 is truncated immediately after its header must fail cleanly + // (rc != 0) without compressing uninitialized heap data into page 2. + testBegin("ppdFilterRasterToPS(truncated row) fails without leaking heap"); + { + char ras_path[] = "/tmp/test_ppd_raster_XXXXXX"; + char ps_path[] = "/tmp/test_ppd_ps_XXXXXX"; + int wfd = mkstemp(ras_path); + int pfd = mkstemp(ps_path); + int rfd = -1; + cups_raster_t *ras = NULL; + cups_page_header_t hdr; + unsigned char row[64]; + FILE *fp = NULL; + char ps_buf[8192]; + size_t ps_len = 0; + const char *img1 = NULL, *img2 = NULL, *g2 = NULL; + size_t rem1 = 0, rem2 = 0; + bool leaked = true; + + memset(ps_buf, 0, sizeof(ps_buf)); + if (wfd >= 0 && pfd >= 0 && + (ras = cupsRasterOpen(wfd, CUPS_RASTER_WRITE)) != NULL) + { + memset(&hdr, 0, sizeof(hdr)); + hdr.PageSize[0] = 612; + hdr.PageSize[1] = 792; + hdr.cupsWidth = 64; + hdr.cupsHeight = 1; + hdr.cupsBitsPerColor = 8; + hdr.cupsBitsPerPixel = 8; + hdr.cupsBytesPerLine = 64; + hdr.cupsColorSpace = CUPS_CSPACE_K; + hdr.HWResolution[0] = 300; + hdr.HWResolution[1] = 300; + + memset(row, 'A', sizeof(row)); + memcpy(row, "LEAK-MARKER-0123456789abcdef", 28); + cupsRasterWriteHeader(ras, &hdr); + cupsRasterWritePixels(ras, row, sizeof(row)); + + // Page 2 header with 0 pixel rows (truncated) + cupsRasterWriteHeader(ras, &hdr); + cupsRasterClose(ras); + close(wfd); + + init_filter_data(&data); + rfd = open(ras_path, O_RDONLY); + rc = (rfd >= 0) ? ppdFilterRasterToPS(rfd, pfd, 1, &data, NULL) : -1; + if (rfd < 0) + close(pfd); + free_filter_data(&data); + + fp = fopen(ps_path, "rb"); + if (fp) + { + ps_len = fread(ps_buf, 1, sizeof(ps_buf), fp); + fclose(fp); + } + + img1 = find_bytes(ps_buf, ps_len, ">> image\n", 9); + rem1 = img1 ? ps_len - (size_t)((img1 + 9) - ps_buf) : 0; + img2 = img1 ? find_bytes(img1 + 9, rem1, ">> image\n", 9) : NULL; + rem2 = img2 ? ps_len - (size_t)((img2 + 9) - ps_buf) : 0; + g2 = img2 ? find_bytes(img2 + 9, rem2, "\ngrestore", 9) : NULL; + if (img2 && g2 && g2 == img2 + 9) + leaked = false; + + testEndMessage(rc != 0 && !leaked, "rc=%d page2_stream_bytes=%ld", + rc, (img2 && g2) ? (long)(g2 - (img2 + 9)) : -1L); + } + else + { + if (wfd >= 0) close(wfd); + if (pfd >= 0) close(pfd); + testEndMessage(false, "mkstemp failed"); + } + + unlink(ras_path); + unlink(ps_path); + } + + // ========================================================================= // Tear-down: remove temp PPDs. // =========================================================================