oss-fuzz: Add three targets

From https://github.com/pnggroup/libpng/issues/764

Signed-off-by: Cosmin Truta <ctruta@gmail.com>
diff --git a/contrib/oss-fuzz/build.sh b/contrib/oss-fuzz/build.sh
index 6c0fdc2..2655d9f 100755
--- a/contrib/oss-fuzz/build.sh
+++ b/contrib/oss-fuzz/build.sh
@@ -36,24 +36,30 @@
 make -j$(nproc) clean
 make -j$(nproc) libpng16.la
 
+for f in libpng_read_fuzzer libpng_colormap_fuzzer libpng_readapi_fuzzer libpng_transformations_fuzzer;
+do
 # build libpng_read_fuzzer.
 $CXX $CXXFLAGS -std=c++11 -I. \
-     $SRC/libpng/contrib/oss-fuzz/libpng_read_fuzzer.cc \
-     -o $OUT/libpng_read_fuzzer \
+     $SRC/libpng/contrib/oss-fuzz/${f}.cc \
+     -o $OUT/${f} \
      -lFuzzingEngine .libs/libpng16.a -lz
 
 # wrapper script to duplicate target, run with env var NALLOC_FREQ=32
 # having a separate target with allocations failures
-cat << EOF > $OUT/libpng_read_fuzzer_nalloc
+cat << EOF > $OUT/${f}_nalloc
 #!/bin/sh
 # LLVMFuzzerTestOneInput for fuzzer detection.
 this_dir=\$(dirname "\$0")
-NALLOC_FREQ=32 \$this_dir/libpng_read_fuzzer \$@
+NALLOC_FREQ=32 \$this_dir/${f} \$@
 EOF
+chmod +x $OUT/${f}_nalloc
 
 # add seed corpus.
-find $SRC/libpng -name "*.png" | grep -v crashers | \
-     xargs zip $OUT/libpng_read_fuzzer_seed_corpus.zip
+find $SRC/libpng -name "*.png" | \
+     xargs zip $OUT/${f}_seed_corpus.zip
+
+cp $SRC/libpng/contrib/oss-fuzz/png.dict $OUT/${f}.dict
+done
 
 cp $SRC/libpng/contrib/oss-fuzz/*.dict \
      $SRC/libpng/contrib/oss-fuzz/*.options $OUT/
diff --git a/contrib/oss-fuzz/libpng_colormap_fuzzer.cc b/contrib/oss-fuzz/libpng_colormap_fuzzer.cc
new file mode 100644
index 0000000..c91172b
--- /dev/null
+++ b/contrib/oss-fuzz/libpng_colormap_fuzzer.cc
@@ -0,0 +1,76 @@
+#include <stdint.h>
+#include <stdlib.h>
+#include <string.h>
+#include <setjmp.h>
+#include <png.h>
+
+/* Test png_image API and colormap functions */
+static void test_png_image_colormap(const uint8_t *data, size_t size) {
+    png_image image;
+    memset(&image, 0, sizeof(image));
+    image.version = PNG_IMAGE_VERSION;
+    
+    if (!png_image_begin_read_from_memory(&image, data, size)) {
+        return;
+    }
+    
+    /* Limit image size for performance */
+    if (image.width > 1024 || image.height > 1024) {
+        png_image_free(&image);
+        return;
+    }
+    
+    /* Test different colormap formats to trigger uncovered functions */
+    png_uint_32 formats[] = {
+        PNG_FORMAT_GRAY,
+        PNG_FORMAT_GA,
+        PNG_FORMAT_RGB,
+        PNG_FORMAT_RGBA,
+        PNG_FORMAT_RGB_COLORMAP,
+        PNG_FORMAT_RGBA_COLORMAP
+    };
+    
+    int i;
+    for (i = 0; i < 6; i++) {
+        image.format = formats[i];
+        
+        size_t buffer_size = PNG_IMAGE_SIZE(image);
+        if (buffer_size > 0 && buffer_size < 5*1024*1024) { /* Limit to 5MB */
+            void *img_buffer = malloc(buffer_size);
+            if (img_buffer) {
+                if (image.format & PNG_FORMAT_FLAG_COLORMAP) {
+                    png_bytep colormap = (png_bytep)malloc(PNG_IMAGE_COLORMAP_SIZE(image));
+                    if (colormap) {
+                        /* This triggers png_image_finish_read and colormap generation */
+                        png_image_finish_read(&image, NULL, img_buffer, 0, colormap);
+                        free(colormap);
+                    }
+                } else {
+                    png_image_finish_read(&image, NULL, img_buffer, 0, NULL);
+                }
+                free(img_buffer);
+            }
+        }
+        
+        /* Reset for next format */
+        if (i < 5) {
+            png_image_free(&image);
+            memset(&image, 0, sizeof(image));
+            image.version = PNG_IMAGE_VERSION;
+            if (!png_image_begin_read_from_memory(&image, data, size)) {
+                break;
+            }
+        }
+    }
+    
+    png_image_free(&image);
+}
+
+extern "C" int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size) {
+    if (size < 8) return 0;
+    
+    /* Test 2: PNG image API with colormap processing */
+    test_png_image_colormap(data, size);
+    
+    return 0;
+}
diff --git a/contrib/oss-fuzz/libpng_readapi_fuzzer.cc b/contrib/oss-fuzz/libpng_readapi_fuzzer.cc
new file mode 100644
index 0000000..8a9d06a
--- /dev/null
+++ b/contrib/oss-fuzz/libpng_readapi_fuzzer.cc
@@ -0,0 +1,61 @@
+#include <stdint.h>
+#include <stdlib.h>
+#include <string.h>
+#include <setjmp.h>
+#include <png.h>
+
+/* Memory buffer structure for PNG reading */
+struct png_mem_buffer {
+    const uint8_t *data;
+    size_t size;
+    size_t pos;
+};
+
+/* Custom read function for memory buffer */
+static void png_read_from_buffer(png_structp png_ptr, png_bytep data, png_size_t length) {
+    struct png_mem_buffer *buf = (struct png_mem_buffer*)png_get_io_ptr(png_ptr);
+    if (buf->pos + length > buf->size) {
+        png_error(png_ptr, "Read error");
+    }
+    memcpy(data, buf->data + buf->pos, length);
+    buf->pos += length;
+}
+
+/* Test png_read_png API */
+static void test_png_read_png_api(const uint8_t *data, size_t size) {
+    png_structp png_ptr = png_create_read_struct(PNG_LIBPNG_VER_STRING, NULL, NULL, NULL);
+    if (!png_ptr) return;
+    
+    png_infop info_ptr = png_create_info_struct(png_ptr);
+    if (!info_ptr) {
+        png_destroy_read_struct(&png_ptr, NULL, NULL);
+        return;
+    }
+    
+    if (setjmp(png_jmpbuf(png_ptr))) {
+        png_destroy_read_struct(&png_ptr, &info_ptr, NULL);
+        return;
+    }
+    
+    struct png_mem_buffer buffer = {data, size, 0};
+    png_set_read_fn(png_ptr, &buffer, png_read_from_buffer);
+    
+    /* Set up transformations before reading */
+    png_set_scale_16(png_ptr);
+    png_set_packing(png_ptr);
+    png_set_expand(png_ptr);
+    
+    /* Use png_read_png which should trigger OSS_FUZZ_png_read_png path */
+    png_read_png(png_ptr, info_ptr, PNG_TRANSFORM_IDENTITY, NULL);
+    
+    png_destroy_read_struct(&png_ptr, &info_ptr, NULL);
+}
+
+extern "C" int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size) {
+    if (size < 8) return 0;
+    
+    /* Test 3: png_read_png API */
+    test_png_read_png_api(data, size);
+    
+    return 0;
+}
diff --git a/contrib/oss-fuzz/libpng_transformations_fuzzer.cc b/contrib/oss-fuzz/libpng_transformations_fuzzer.cc
new file mode 100644
index 0000000..8906d68
--- /dev/null
+++ b/contrib/oss-fuzz/libpng_transformations_fuzzer.cc
@@ -0,0 +1,211 @@
+#include <stdint.h>
+#include <stdlib.h>
+#include <string.h>
+#include <setjmp.h>
+#include <png.h>
+
+/* Memory buffer structure for PNG reading */
+struct png_mem_buffer {
+    const uint8_t *data;
+    size_t size;
+    size_t pos;
+};
+
+/* Custom read function for memory buffer */
+static void png_read_from_buffer(png_structp png_ptr, png_bytep data, png_size_t length) {
+    struct png_mem_buffer *buf = (struct png_mem_buffer*)png_get_io_ptr(png_ptr);
+    if (buf->pos + length > buf->size) {
+        png_error(png_ptr, "Read error");
+    }
+    memcpy(data, buf->data + buf->pos, length);
+    buf->pos += length;
+}
+
+/* Test PNG transformations to trigger uncovered code paths */
+static void test_png_transformations(const uint8_t *data, size_t size) {
+    png_structp png_ptr = png_create_read_struct(PNG_LIBPNG_VER_STRING, NULL, NULL, NULL);
+    if (!png_ptr) return;
+    
+    png_infop info_ptr = png_create_info_struct(png_ptr);
+    if (!info_ptr) {
+        png_destroy_read_struct(&png_ptr, NULL, NULL);
+        return;
+    }
+    
+    if (setjmp(png_jmpbuf(png_ptr))) {
+        png_destroy_read_struct(&png_ptr, &info_ptr, NULL);
+        return;
+    }
+    
+    struct png_mem_buffer buffer = {data, size, 0};
+    png_set_read_fn(png_ptr, &buffer, png_read_from_buffer);
+    
+    png_read_info(png_ptr, info_ptr);
+    
+    int width = png_get_image_width(png_ptr, info_ptr);
+    int height = png_get_image_height(png_ptr, info_ptr);
+    int color_type = png_get_color_type(png_ptr, info_ptr);
+    int bit_depth = png_get_bit_depth(png_ptr, info_ptr);
+    
+    /* Sanity check dimensions */
+    if (width <= 0 || height <= 0 || width > 2048 || height > 2048) {
+        png_destroy_read_struct(&png_ptr, &info_ptr, NULL);
+        return;
+    }
+    
+    /* Target 1: 16-bit depth processing (triggers png_do_chop) */
+    if (bit_depth == 16) {
+        png_set_scale_16(png_ptr);
+    }
+    
+    /* Target 2: Color quantization (triggers png_do_quantize) */
+    if (color_type == PNG_COLOR_TYPE_RGB || color_type == PNG_COLOR_TYPE_RGB_ALPHA) {
+        png_colorp palette = (png_colorp)malloc(256 * sizeof(png_color));
+        if (palette) {
+            int i;
+            for (i = 0; i < 256; i++) {
+                palette[i].red = palette[i].green = palette[i].blue = (png_byte)i;
+            }
+            png_set_quantize(png_ptr, palette, 256, 256, NULL, 0);
+            free(palette);
+        }
+    }
+    
+    /* Target 3: Alpha channel transformations */
+    if (color_type & PNG_COLOR_MASK_ALPHA) {
+        png_set_invert_alpha(png_ptr);  /* png_do_read_invert_alpha */
+        png_set_swap_alpha(png_ptr);    /* png_do_read_swap_alpha */
+    }
+    
+    /* Target 4: Bit shifting (triggers png_do_unshift) */
+    if (bit_depth < 8) {
+        png_set_packing(png_ptr);
+    }
+    
+    /* Target 5: Alpha encoding (triggers png_do_encode_alpha) */
+    png_set_alpha_mode(png_ptr, PNG_ALPHA_STANDARD, PNG_GAMMA_LINEAR);
+    
+    /* Update transformations */
+    png_read_update_info(png_ptr, info_ptr);
+    
+    /* Read image data to execute transformations */
+    size_t rowbytes = png_get_rowbytes(png_ptr, info_ptr);
+    png_bytep row = (png_bytep)malloc(rowbytes);
+    if (row) {
+        int y;
+        for (y = 0; y < height && y < 100; y++) { /* Limit rows for performance */
+            png_read_row(png_ptr, row, NULL);
+        }
+        free(row);
+    }
+    
+    png_destroy_read_struct(&png_ptr, &info_ptr, NULL);
+}
+
+/* Test png_image API and colormap functions */
+static void test_png_image_colormap(const uint8_t *data, size_t size) {
+    png_image image;
+    memset(&image, 0, sizeof(image));
+    image.version = PNG_IMAGE_VERSION;
+    
+    if (!png_image_begin_read_from_memory(&image, data, size)) {
+        return;
+    }
+    
+    /* Limit image size for performance */
+    if (image.width > 1024 || image.height > 1024) {
+        png_image_free(&image);
+        return;
+    }
+    
+    /* Test different colormap formats to trigger uncovered functions */
+    png_uint_32 formats[] = {
+        PNG_FORMAT_GRAY,
+        PNG_FORMAT_GA,
+        PNG_FORMAT_RGB,
+        PNG_FORMAT_RGBA,
+        PNG_FORMAT_RGB_COLORMAP,
+        PNG_FORMAT_RGBA_COLORMAP
+    };
+    
+    int i;
+    for (i = 0; i < 6; i++) {
+        image.format = formats[i];
+        
+        size_t buffer_size = PNG_IMAGE_SIZE(image);
+        if (buffer_size > 0 && buffer_size < 5*1024*1024) { /* Limit to 5MB */
+            void *img_buffer = malloc(buffer_size);
+            if (img_buffer) {
+                if (image.format & PNG_FORMAT_FLAG_COLORMAP) {
+                    png_bytep colormap = (png_bytep)malloc(PNG_IMAGE_COLORMAP_SIZE(image));
+                    if (colormap) {
+                        /* This triggers png_image_finish_read and colormap generation */
+                        png_image_finish_read(&image, NULL, img_buffer, 0, colormap);
+                        free(colormap);
+                    }
+                } else {
+                    png_image_finish_read(&image, NULL, img_buffer, 0, NULL);
+                }
+                free(img_buffer);
+            }
+        }
+        
+        /* Reset for next format */
+        if (i < 5) {
+            png_image_free(&image);
+            memset(&image, 0, sizeof(image));
+            image.version = PNG_IMAGE_VERSION;
+            if (!png_image_begin_read_from_memory(&image, data, size)) {
+                break;
+            }
+        }
+    }
+    
+    png_image_free(&image);
+}
+
+
+/* Test png_read_png API */
+static void test_png_read_png_api(const uint8_t *data, size_t size) {
+    png_structp png_ptr = png_create_read_struct(PNG_LIBPNG_VER_STRING, NULL, NULL, NULL);
+    if (!png_ptr) return;
+    
+    png_infop info_ptr = png_create_info_struct(png_ptr);
+    if (!info_ptr) {
+        png_destroy_read_struct(&png_ptr, NULL, NULL);
+        return;
+    }
+    
+    if (setjmp(png_jmpbuf(png_ptr))) {
+        png_destroy_read_struct(&png_ptr, &info_ptr, NULL);
+        return;
+    }
+    
+    struct png_mem_buffer buffer = {data, size, 0};
+    png_set_read_fn(png_ptr, &buffer, png_read_from_buffer);
+    
+    /* Set up transformations before reading */
+    png_set_scale_16(png_ptr);
+    png_set_packing(png_ptr);
+    png_set_expand(png_ptr);
+    
+    /* Use png_read_png which should trigger OSS_FUZZ_png_read_png path */
+    png_read_png(png_ptr, info_ptr, PNG_TRANSFORM_IDENTITY, NULL);
+    
+    png_destroy_read_struct(&png_ptr, &info_ptr, NULL);
+}
+
+extern "C" int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size) {
+    if (size < 8) return 0;
+    
+    /* Test 1: Standard PNG reading with transformations */
+    test_png_transformations(data, size);
+    
+    /* Test 2: PNG image API with colormap processing */
+    test_png_image_colormap(data, size);
+    
+    /* Test 3: png_read_png API */
+    test_png_read_png_api(data, size);
+    
+    return 0;
+}