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;
+}