// libpng_read_fuzzer.cc // Copyright 2017-2018 Glenn Randers-Pehrson // Copyright 2015 The Chromium Authors. All rights reserved. // Use of this source code is governed by a BSD-style license that may // be found in the LICENSE file https://cs.chromium.org/chromium/src/LICENSE // Last changed in libpng 1.6.35 [July 15, 2018] // The modifications in 2017 by Glenn Randers-Pehrson include // 1. addition of a PNG_CLEANUP macro, // 2. setting the option to ignore ADLER32 checksums, // 3. adding "#include " which is needed on some platforms // to provide memcpy(). // 4. adding read_end_info() and creating an end_info structure. // 5. adding calls to png_set_*() transforms commonly used by browsers. #include #include #include #include #include #define PNG_INTERNAL #include "libpng-1.6.37/png.h" #define PNG_CLEANUP \ if (png_handler.png_ptr) { \ if (png_handler.row_ptr) { \ png_free(png_handler.png_ptr, png_handler.row_ptr); \ } \ if (png_handler.end_info_ptr) { \ png_destroy_read_struct(&png_handler.png_ptr, &png_handler.info_ptr, \ &png_handler.end_info_ptr); \ } else if (png_handler.info_ptr) { \ png_destroy_read_struct(&png_handler.png_ptr, &png_handler.info_ptr, \ nullptr); \ } else { \ png_destroy_read_struct(&png_handler.png_ptr, nullptr, nullptr); \ } \ png_handler.png_ptr = nullptr; \ png_handler.row_ptr = nullptr; \ png_handler.info_ptr = nullptr; \ png_handler.end_info_ptr = nullptr; \ } struct BufState { const uint8_t *data; size_t bytes_left; }; struct PngObjectHandler { png_infop info_ptr = nullptr; png_structp png_ptr = nullptr; png_infop end_info_ptr = nullptr; png_voidp row_ptr = nullptr; BufState *buf_state = nullptr; ~PngObjectHandler() { if (row_ptr) { png_free(png_ptr, row_ptr); } if (end_info_ptr) { png_destroy_read_struct(&png_ptr, &info_ptr, &end_info_ptr); } else if (info_ptr) { png_destroy_read_struct(&png_ptr, &info_ptr, nullptr); } else { png_destroy_read_struct(&png_ptr, nullptr, nullptr); } delete buf_state; } }; void user_read_data(png_structp png_ptr, png_bytep data, size_t length) { BufState *buf_state = static_cast(png_get_io_ptr(png_ptr)); if (length > buf_state->bytes_left) { png_error(png_ptr, "read error"); } memcpy(data, buf_state->data, length); buf_state->bytes_left -= length; buf_state->data += length; } static const int kPngHeaderSize = 8; extern "C" int afl_libfuzzer_init() { return 0; } static char *allocation = NULL; __attribute__((noinline)) void func3(char *alloc) { // printf("func3\n"); if (random() == 0) { alloc[0x1ff] = 0xde; printf("alloc[0x200]: %d\n", alloc[0x200]); } } __attribute__((noinline)) void func2() { allocation = (char *)malloc(0xff); // printf("func2\n"); func3(allocation); } __attribute__((noinline)) void func1() { // printf("func1\n"); func2(); } // Export this symbol #define HARNESS_EXPORTS // Entry point for LibFuzzer. // Roughly follows the libpng book example: // http://www.libpng.org/pub/png/book/chapter13.html int main(int argc, char **argv) { if (argc != 3) { abort(); } size_t size = atoi(argv[1]); uint8_t *data = (uint8_t *)argv[2]; if (size >= 8 && *(uint64_t *)data == 0xABCDEFAA8F1324AA) { abort(); } if (size < kPngHeaderSize) { return 0; } #ifdef TEST_ASAN func1(); #endif std::vector v(data, data + size); if (png_sig_cmp(v.data(), 0, kPngHeaderSize)) { // not a PNG. return 0; } PngObjectHandler png_handler; png_handler.png_ptr = nullptr; png_handler.row_ptr = nullptr; png_handler.info_ptr = nullptr; png_handler.end_info_ptr = nullptr; png_handler.png_ptr = png_create_read_struct(PNG_LIBPNG_VER_STRING, nullptr, nullptr, nullptr); if (!png_handler.png_ptr) { return 0; } png_handler.info_ptr = png_create_info_struct(png_handler.png_ptr); if (!png_handler.info_ptr) { PNG_CLEANUP return 0; } png_handler.end_info_ptr = png_create_info_struct(png_handler.png_ptr); if (!png_handler.end_info_ptr) { PNG_CLEANUP return 0; } png_set_crc_action(png_handler.png_ptr, PNG_CRC_QUIET_USE, PNG_CRC_QUIET_USE); #ifdef PNG_IGNORE_ADLER32 png_set_option(png_handler.png_ptr, PNG_IGNORE_ADLER32, PNG_OPTION_ON); #endif // Setting up reading from buffer. png_handler.buf_state = new BufState(); png_handler.buf_state->data = data + kPngHeaderSize; png_handler.buf_state->bytes_left = size - kPngHeaderSize; png_set_read_fn(png_handler.png_ptr, png_handler.buf_state, user_read_data); png_set_sig_bytes(png_handler.png_ptr, kPngHeaderSize); if (setjmp(png_jmpbuf(png_handler.png_ptr))) { PNG_CLEANUP return 0; } // Reading. png_read_info(png_handler.png_ptr, png_handler.info_ptr); // reset error handler to put png_deleter into scope. if (setjmp(png_jmpbuf(png_handler.png_ptr))) { PNG_CLEANUP return 0; } png_uint_32 width, height; int bit_depth, color_type, interlace_type, compression_type; int filter_type; if (!png_get_IHDR(png_handler.png_ptr, png_handler.info_ptr, &width, &height, &bit_depth, &color_type, &interlace_type, &compression_type, &filter_type)) { PNG_CLEANUP return 0; } // This is going to be too slow. if (width && height > 100000000 / width) { PNG_CLEANUP #ifdef HAS_DUMMY_CRASH #ifdef __aarch64__ asm volatile(".word 0xf7f0a000\n"); #else asm("ud2"); #endif #endif return 0; } // Set several transforms that browsers typically use: png_set_gray_to_rgb(png_handler.png_ptr); png_set_expand(png_handler.png_ptr); png_set_packing(png_handler.png_ptr); png_set_scale_16(png_handler.png_ptr); png_set_tRNS_to_alpha(png_handler.png_ptr); int passes = png_set_interlace_handling(png_handler.png_ptr); png_read_update_info(png_handler.png_ptr, png_handler.info_ptr); png_handler.row_ptr = png_malloc(png_handler.png_ptr, png_get_rowbytes(png_handler.png_ptr, png_handler.info_ptr)); for (int pass = 0; pass < passes; ++pass) { for (png_uint_32 y = 0; y < height; ++y) { png_read_row(png_handler.png_ptr, static_cast(png_handler.row_ptr), nullptr); } } png_read_end(png_handler.png_ptr, png_handler.end_info_ptr); PNG_CLEANUP return 0; }