| |
| // 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 |
| |
| // 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 <string.h>" 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 <stddef.h> |
| #include <stdint.h> |
| #include <stdlib.h> |
| #include <string.h> |
| |
| #include <vector> |
| |
| #define PNG_INTERNAL |
| #include "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<BufState*>(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; |
| } |
| |
| void* limited_malloc(png_structp, png_alloc_size_t size) { |
| // libpng may allocate large amounts of memory that the fuzzer reports as |
| // an error. In order to silence these errors, make libpng fail when trying |
| // to allocate a large amount. This allocator used to be in the Chromium |
| // version of this fuzzer. |
| // This number is chosen to match the default png_user_chunk_malloc_max. |
| if (size > 8000000) |
| return nullptr; |
| |
| return malloc(size); |
| } |
| |
| void default_free(png_structp, png_voidp ptr) { |
| return free(ptr); |
| } |
| |
| static const int kPngHeaderSize = 8; |
| |
| // Entry point for LibFuzzer. |
| // Roughly follows the libpng book example: |
| // http://www.libpng.org/pub/png/book/chapter13.html |
| extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) { |
| if (size < kPngHeaderSize) { |
| return 0; |
| } |
| |
| std::vector<unsigned char> 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; |
| } |
| |
| // Use a custom allocator that fails for large allocations to avoid OOM. |
| png_set_mem_fn(png_handler.png_ptr, nullptr, limited_malloc, default_free); |
| |
| 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 |
| 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_bytep>(png_handler.row_ptr), nullptr); |
| } |
| } |
| |
| png_read_end(png_handler.png_ptr, png_handler.end_info_ptr); |
| |
| PNG_CLEANUP |
| return 0; |
| } |