OSS-Fuzz: cjpeg fuzz target
diff --git a/cjpeg.c b/cjpeg.c
index e5a9211..d1bf71c 100644
--- a/cjpeg.c
+++ b/cjpeg.c
@@ -5,7 +5,7 @@
* Copyright (C) 1991-1998, Thomas G. Lane.
* Modified 2003-2011 by Guido Vollbeding.
* libjpeg-turbo Modifications:
- * Copyright (C) 2010, 2013-2014, 2017, 2019-2020, D. R. Commander.
+ * Copyright (C) 2010, 2013-2014, 2017, 2019-2021, D. R. Commander.
* For conditions of distribution and use, see the accompanying README.ijg
* file.
*
@@ -27,6 +27,9 @@
* works regardless of which command line style is used.
*/
+#ifdef CJPEG_FUZZER
+#define JPEG_INTERNALS
+#endif
#include "cdjpeg.h" /* Common decls for cjpeg/djpeg applications */
#include "jversion.h" /* for version message */
#include "jconfigint.h"
@@ -146,6 +149,45 @@
boolean report; /* for -report switch */
+#ifdef CJPEG_FUZZER
+
+#include <setjmp.h>
+
+struct my_error_mgr {
+ struct jpeg_error_mgr pub;
+ jmp_buf setjmp_buffer;
+};
+
+void my_error_exit(j_common_ptr cinfo)
+{
+ struct my_error_mgr *myerr = (struct my_error_mgr *)cinfo->err;
+
+ longjmp(myerr->setjmp_buffer, 1);
+}
+
+static void my_emit_message(j_common_ptr cinfo, int msg_level)
+{
+ if (msg_level < 0)
+ cinfo->err->num_warnings++;
+}
+
+#define HANDLE_ERROR() { \
+ if (cinfo.global_state > CSTATE_START) { \
+ if (memdst && outbuffer) \
+ (*cinfo.dest->term_destination) (&cinfo); \
+ jpeg_abort_compress(&cinfo); \
+ } \
+ jpeg_destroy_compress(&cinfo); \
+ if (input_file != stdin && input_file != NULL) \
+ fclose(input_file); \
+ if (memdst) \
+ free(outbuffer); \
+ return EXIT_FAILURE; \
+}
+
+#endif
+
+
LOCAL(void)
usage(void)
/* complain about bad command line */
@@ -506,11 +548,16 @@
main(int argc, char **argv)
{
struct jpeg_compress_struct cinfo;
+#ifdef CJPEG_FUZZER
+ struct my_error_mgr myerr;
+ struct jpeg_error_mgr &jerr = myerr.pub;
+#else
struct jpeg_error_mgr jerr;
+#endif
struct cdjpeg_progress_mgr progress;
int file_index;
cjpeg_source_ptr src_mgr;
- FILE *input_file;
+ FILE *input_file = NULL;
FILE *icc_file;
JOCTET *icc_profile = NULL;
long icc_len = 0;
@@ -628,6 +675,13 @@
fclose(icc_file);
}
+#ifdef CJPEG_FUZZER
+ jerr.error_exit = my_error_exit;
+ jerr.emit_message = my_emit_message;
+ if (setjmp(myerr.setjmp_buffer))
+ HANDLE_ERROR()
+#endif
+
if (report) {
start_progress_monitor((j_common_ptr)&cinfo, &progress);
progress.report = report;
@@ -654,6 +708,13 @@
#endif
jpeg_stdio_dest(&cinfo, output_file);
+#ifdef CJPEG_FUZZER
+ if (cinfo.image_width < 1 || cinfo.image_height < 1 ||
+ (unsigned long long)cinfo.image_width * cinfo.image_height > 1048576 ||
+ setjmp(myerr.setjmp_buffer))
+ HANDLE_ERROR()
+#endif
+
/* Start compressor */
jpeg_start_compress(&cinfo, TRUE);
@@ -681,13 +742,14 @@
end_progress_monitor((j_common_ptr)&cinfo);
if (memdst) {
+#ifndef CJPEG_FUZZER
fprintf(stderr, "Compressed size: %lu bytes\n", outsize);
+#endif
free(outbuffer);
}
free(icc_profile);
/* All done. */
- exit(jerr.num_warnings ? EXIT_WARNING : EXIT_SUCCESS);
- return 0; /* suppress no-return-value warnings */
+ return (jerr.num_warnings ? EXIT_WARNING : EXIT_SUCCESS);
}
diff --git a/fuzz/CMakeLists.txt b/fuzz/CMakeLists.txt
index 61ad67e..dcd19c3 100644
--- a/fuzz/CMakeLists.txt
+++ b/fuzz/CMakeLists.txt
@@ -25,6 +25,12 @@
"${CMAKE_CXX_FLAGS} ${CMAKE_CXX_FLAGS_${CMAKE_BUILD_TYPE_UC}}")
message(STATUS "C++ Compiler flags = ${EFFECTIVE_CXX_FLAGS}")
+add_executable(cjpeg_fuzzer cjpeg.cc ../cdjpeg.c ../rdbmp.c ../rdgif.c
+ ../rdppm.c ../rdswitch.c ../rdtarga.c)
+set_property(TARGET cjpeg_fuzzer PROPERTY COMPILE_FLAGS ${COMPILE_FLAGS})
+target_link_libraries(cjpeg_fuzzer ${FUZZ_LIBRARY} jpeg-static)
+install(TARGETS cjpeg_fuzzer RUNTIME DESTINATION ${FUZZ_BINDIR})
+
macro(add_fuzz_target target source_file)
add_executable(${target}_fuzzer ${source_file})
target_link_libraries(${target}_fuzzer ${FUZZ_LIBRARY} turbojpeg-static)
diff --git a/fuzz/build.sh b/fuzz/build.sh
index 1102762..882ce7d 100644
--- a/fuzz/build.sh
+++ b/fuzz/build.sh
@@ -10,6 +10,7 @@
make "-j$(nproc)" "--load-average=$(nproc)"
make install
+cp $SRC/compress_fuzzer_seed_corpus.zip $OUT/cjpeg_fuzzer_seed_corpus.zip
cp $SRC/compress_fuzzer_seed_corpus.zip $OUT/compress_fuzzer_seed_corpus.zip
cp $SRC/compress_fuzzer_seed_corpus.zip $OUT/compress_yuv_fuzzer_seed_corpus.zip
cp $SRC/decompress_fuzzer_seed_corpus.zip $OUT/libjpeg_turbo_fuzzer_seed_corpus.zip
diff --git a/fuzz/cjpeg.cc b/fuzz/cjpeg.cc
new file mode 100644
index 0000000..b2ce5b3
--- /dev/null
+++ b/fuzz/cjpeg.cc
@@ -0,0 +1,88 @@
+/*
+ * Copyright (C)2021 D. R. Commander. All Rights Reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * - Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * - 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.
+ * - Neither the name of the libjpeg-turbo Project nor the names of its
+ * contributors may be used to endorse or promote products derived from this
+ * software without specific prior written permission.
+ *
+ * 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 THE COPYRIGHT HOLDERS OR 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.
+ */
+
+/* This fuzz target wraps cjpeg in order to test esoteric compression options
+ as well as the GIF and Targa readers. */
+
+#define main cjpeg_main
+#define CJPEG_FUZZER
+extern "C" {
+#include "../cjpeg.c"
+}
+#undef main
+#undef CJPEG_FUZZER
+
+#include <stdint.h>
+#include <unistd.h>
+
+
+extern "C" int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size)
+{
+ char filename[FILENAME_MAX] = { 0 };
+ char *argv1[] = {
+ (char *)"cjpeg", (char *)"-dct", (char *)"float", (char *)"-memdst",
+ (char *)"-optimize", (char *)"-quality", (char *)"100,99,98",
+ (char *)"-restart", (char *)"2", (char *)"-sample", (char *)"4x1,2x2,1x2",
+ (char *)"-targa", NULL
+ };
+ char *argv2[] = {
+ (char *)"cjpeg", (char *)"-arithmetic", (char *)"-dct", (char *)"float",
+ (char *)"-memdst", (char *)"-quality", (char *)"90,80,70", (char *)"-rgb",
+ (char *)"-smooth", (char *)"50", (char *)"-targa", NULL
+ };
+ int fd = -1;
+#if defined(__has_feature) && __has_feature(memory_sanitizer)
+ char env[18] = "JSIMD_FORCENONE=1";
+
+ /* The libjpeg-turbo SIMD extensions produce false positives with
+ MemorySanitizer. */
+ putenv(env);
+#endif
+
+ snprintf(filename, FILENAME_MAX, "/tmp/libjpeg-turbo_cjpeg_fuzz.XXXXXX");
+ if ((fd = mkstemp(filename)) < 0 || write(fd, data, size) < 0)
+ goto bailout;
+
+ argv1[12] = argv2[11] = filename;
+
+ cjpeg_main(13, argv1);
+ cjpeg_main(12, argv2);
+
+ argv1[12] = argv2[11] = NULL;
+ argv1[11] = argv2[10] = filename;
+
+ cjpeg_main(12, argv1);
+ cjpeg_main(11, argv2);
+
+bailout:
+ if (fd >= 0) {
+ close(fd);
+ if (strlen(filename) > 0) unlink(filename);
+ }
+ return 0;
+}