Skip to main content
← All libraries
Image · C

How to fuzz libpng

The reference PNG decoder — heavily fuzzed, yet bugs keep surfacing.

libpng is the canonical PNG reference implementation shipped in essentially every OS and browser. Its chunk-driven parser and IDAT zlib decompression surface have a long history of memory-safety bugs, and any new regression directly impacts a vast install base.

Common bug classes

  • Heap buffer overflow in IDAT row decompression
  • Integer overflow in width/height chunk validation
  • Out-of-bounds read in ancillary chunk handlers (tEXt, zTXt, iTXt)
  • Use-after-free in progressive read state machine
  • Divide-by-zero in colour-space conversion

Recommended setup

Fuzzers

  • AFL++
  • libFuzzer
  • Honggfuzz

Sanitizers

  • ASan
  • UBSan

Harness scaffold

#include <stdint.h>
#include <stddef.h>
#include <string.h>
#include <setjmp.h>
#include <png.h>

static void error_fn(png_structp ps, png_const_charp msg) {
  (void)msg;
  longjmp(png_jmpbuf(ps), 1);
}
static void warning_fn(png_structp ps, png_const_charp msg) {
  (void)ps; (void)msg;
}

int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size) {
  png_structp ps = png_create_read_struct(PNG_LIBPNG_VER_STRING,
                                          NULL, error_fn, warning_fn);
  if (!ps) return 0;
  png_infop info = png_create_info_struct(ps);
  if (!info) { png_destroy_read_struct(&ps, NULL, NULL); return 0; }
  if (setjmp(png_jmpbuf(ps))) {
    png_destroy_read_struct(&ps, &info, NULL); return 0;
  }
  png_set_read_fn(ps, NULL, NULL);
  /* feed via io_ptr — simplified; real harness uses membuf */
  png_destroy_read_struct(&ps, &info, NULL);
  return 0;
}

Save this as fuzz_target.cc, build with your compiler + sanitizer flags, and you have a working starting point.

Notable CVEs found by fuzzing

  • CVE-2018-14048
  • CVE-2019-7317
Start fuzzing libpng on Fuzze.rs →

Push the harness above + a Dockerfile. First month 50% off.