/*====================================================================* - Copyright (C) 2001 Leptonica. All rights reserved. - - Redistribution and use in source and binary forms, with or without - modification, are permitted provided that the following conditions - are met: - 1. Redistributions of source code must retain the above copyright - notice, this list of conditions and the following disclaimer. - 2. Redistributions in binary form must reproduce the above - copyright notice, this list of conditions and the following - disclaimer in the documentation and/or other materials - provided with the distribution. - - THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS - ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT - LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR - A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL ANY - CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, - EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, - PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR - PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY - OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING - NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS - SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. *====================================================================*/ /* * corrupttest.c * * Excises or permutes a given fraction of bytes, starting from * a specified location. The parameters @loc and @size are fractions * of the entire file (between 0.0 and 1.0). * * Syntax: corrupttest [loc size] * * where == 1 means that bytes are deleted * == 0 means that random bytes are substituted * * Use: "fuzz testing" jpeg, png, tiff, bmp, webp and pnm reading, * under corruption by either random byte substitution or * deletion of part of the compressed file. * * For example, * corrupttest rabi.png 0 0.0001 0.0001 * which tests read functions on rabi.png after 23 bytes (0.01%) * starting at byte 23 have been randomly permuted, emits the following: * > Info in fileCorruptByMutation: Randomizing 23 bytes at location 23 * > libpng error: IHDR: CRC error * > Error in pixReadMemPng: internal png error * > Error in pixReadStream: png: no pix returned * > Error in pixRead: pix not read * > libpng error: IHDR: CRC error * > Error in fgetPngResolution: internal png error */ #ifdef HAVE_CONFIG_H #include #endif /* HAVE_CONFIG_H */ #include "string.h" #include "allheaders.h" static const char *corruptfile = "/tmp/lept/corrupt/badfile"; int main(int argc, char **argv) { size_t filesize; l_float32 loc, size; l_float32 coeff1[15], coeff2[25]; l_int32 i, j, w, xres, yres, format, ret, nwarn, hint, deletion, show; l_uint8 *comment, *filedata; char *filein; size_t nbytes; FILE *fp; PIX *pix; static char mainName[] = "corrupttest"; if (argc != 3 && argc != 5) return ERROR_INT("syntax: corrupttest filein deletion [loc size]", mainName, 1); filein = argv[1]; deletion = atoi(argv[2]); findFileFormat(filein, &format); nbytes = nbytesInFile(filein); lept_stderr("file size: %lu bytes\n", (unsigned long)nbytes); setLeptDebugOK(1); lept_mkdir("lept/corrupt"); hint = 0; if (argc == 5) { /* Single test */ loc = atof(argv[3]); size = atof(argv[4]); if (deletion == TRUE) { fileCorruptByDeletion(filein, loc, size, corruptfile); } else { /* mutation */ fileCorruptByMutation(filein, loc, size, corruptfile); } if ((fp = fopenReadStream(corruptfile)) == NULL) return ERROR_INT("stream not opened", mainName, 1); if (format == IFF_JFIF_JPEG) { if ((pix = pixReadJpeg(corruptfile, 0, 1, &nwarn, hint)) != NULL) { pixDisplay(pix, 100, 100); pixDestroy(&pix); } freadHeaderJpeg(fp, &w, NULL, NULL, NULL, NULL); fgetJpegResolution(fp, &xres, &yres); ret = fgetJpegComment(fp, &comment); if (!ret && comment) { lept_stderr("comment: %s\n", comment); lept_free(comment); } } else if (format == IFF_PNG) { if ((pix = pixRead(corruptfile)) != NULL) { pixDisplay(pix, 100, 100); pixDestroy(&pix); } freadHeaderPng(fp, &w, NULL, NULL, NULL, NULL); fgetPngResolution(fp, &xres, &yres); } else if (format == IFF_WEBP) { if ((pix = pixRead(corruptfile)) != NULL) { pixDisplay(pix, 100, 100); pixDestroy(&pix); } readHeaderWebP(corruptfile, &w, NULL, NULL); } else if (format == IFF_PNM) { if ((pix = pixRead(corruptfile)) != NULL) { pixDisplay(pix, 100, 100); pixDestroy(&pix); } freadHeaderPnm(fp, &w, NULL, NULL, NULL, NULL, NULL); } fclose(fp); return 0; } /* Generate coefficients so that the size of the mangled * or deleted data can range from 0.001% to 1% of the file, * and the location of deleted data ranges from 0.001% * to 90% of the file. */ for (i = 0; i < 15; i++) { if (i < 5) coeff1[i] = 0.00001; else if (i < 10) coeff1[i] = 0.0001; else coeff1[i] = 0.001; } for (i = 0; i < 25; i++) { if (i < 5) coeff2[i] = 0.00001; else if (i < 10) coeff2[i] = 0.0001; else if (i < 15) coeff2[i] = 0.001; else if (i < 20) coeff2[i] = 0.01; else coeff2[i] = 0.1; } /* Multiple test (argc == 3) */ show = TRUE; for (i = 0; i < 25; i++) { loc = coeff2[i] * (2 * (i % 5) + 1); for (j = 0; j < 15; j++) { size = coeff1[j] * (2 * (j % 5) + 1); /* Write corrupt file */ if (deletion == TRUE) { fileCorruptByDeletion(filein, loc, size, corruptfile); } else { fileCorruptByMutation(filein, loc, size, corruptfile); } /* Attempt to read the file */ pix = NULL; if (format == IFF_JFIF_JPEG) { /* The pix is usually returned as long as the header * information is not damaged. * We expect nwarn > 0 (typically 1) for nearly every * corrupted image. In the situation where only a few * bytes are removed, a corrupted image will occasionally * have nwarn == 0 even though it's visually defective. */ pix = pixReadJpeg(corruptfile, 0, 1, &nwarn, 0); if (pix && nwarn != 1 && deletion == TRUE) { lept_stderr("nwarn[%d,%d] = %d\n", j, i, nwarn); if (show) pixDisplay(pix, 20 * i, 30 * j); show = FALSE; } } else if (format == IFF_PNG) { pix = pixRead(corruptfile); if (pix) { lept_stderr("pix[%d,%d] is read\n", j, i); if (show) pixDisplay(pix, 20 * i, 30 * j); show = FALSE; } pixDestroy(&pix); filedata = l_binaryRead(corruptfile, &filesize); pix = pixReadMemPng(filedata, filesize); lept_free(filedata); } else if (L_FORMAT_IS_TIFF(format)) { /* A corrupted pix is often returned, as long as the * header is not damaged, so we do not display them. */ pix = pixRead(corruptfile); if (pix) lept_stderr("pix[%d,%d] is read\n", j, i); pixDestroy(&pix); filedata = l_binaryRead(corruptfile, &filesize); pix = pixReadMemTiff(filedata, filesize, 0); if (!pix) lept_stderr("no pix!\n"); lept_free(filedata); } else if (format == IFF_BMP) { /* A corrupted pix is always returned if the header is * not damaged, so we do not display them. */ pix = pixRead(corruptfile); if (pix) lept_stderr("pix[%d,%d] is read\n", j, i); pixDestroy(&pix); filedata = l_binaryRead(corruptfile, &filesize); pix = pixReadMemBmp(filedata, filesize); lept_free(filedata); } else if (format == IFF_WEBP) { /* A corrupted pix is always returned if the header is * not damaged, so we do not display them. */ pix = pixRead(corruptfile); if (pix) lept_stderr("pix[%d,%d] is read\n", j, i); pixDestroy(&pix); filedata = l_binaryRead(corruptfile, &filesize); pix = pixReadMemWebP(filedata, filesize); lept_free(filedata); } else if (format == IFF_PNM) { /* A corrupted pix is always returned if the header is * not damaged, so we do not display them. */ pix = pixRead(corruptfile); if (pix) lept_stderr("pix[%d,%d] is read\n", j, i); pixDestroy(&pix); filedata = l_binaryRead(corruptfile, &filesize); pix = pixReadMemPnm(filedata, filesize); lept_free(filedata); } else { lept_stderr("Format %d unknown\n", format); continue; } /* Effect of 1% byte mangling from interior of data stream */ if (pix && j == 14 && i == 10 && deletion == FALSE) pixDisplay(pix, 0, 0); pixDestroy(&pix); /* Attempt to read the header and the resolution */ fp = fopenReadStream(corruptfile); if (format == IFF_JFIF_JPEG) { freadHeaderJpeg(fp, &w, NULL, NULL, NULL, NULL); if (fgetJpegResolution(fp, &xres, &yres) == 0) lept_stderr("w = %d, xres = %d, yres = %d\n", w, xres, yres); } else if (format == IFF_PNG) { freadHeaderPng(fp, &w, NULL, NULL, NULL, NULL); if (fgetPngResolution(fp, &xres, &yres) == 0) lept_stderr("w = %d, xres = %d, yres = %d\n", w, xres, yres); } else if (L_FORMAT_IS_TIFF(format)) { freadHeaderTiff(fp, 0, &w, NULL, NULL, NULL, NULL, NULL, NULL); getTiffResolution(fp, &xres, &yres); lept_stderr("w = %d, xres = %d, yres = %d\n", w, xres, yres); } else if (format == IFF_WEBP) { if (readHeaderWebP(corruptfile, &w, NULL, NULL) == 0) lept_stderr("w = %d\n", w); } else if (format == IFF_PNM) { if (freadHeaderPnm(fp, &w, NULL, NULL, NULL, NULL, NULL) == 0) lept_stderr("w = %d\n", w); } fclose(fp); } } return 0; }