You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
209 lines
6.2 KiB
209 lines
6.2 KiB
|
|
// 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 <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 <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; |
|
}
|
|
|