1 2 // libpng_read_fuzzer.cc 3 // Copyright 2017 Glenn Randers-Pehrson 4 // Copyright 2015 The Chromium Authors. All rights reserved. 5 // Use of this source code is governed by a BSD-style license that may 6 // be found in the LICENSE file https://cs.chromium.org/chromium/src/LICENSE 7 8 // Last changed in libpng 1.6.32 [August 24, 2017] 9 10 // The modifications in 2017 by Glenn Randers-Pehrson include 11 // 1. addition of a PNG_CLEANUP macro, 12 // 2. setting the option to ignore ADLER32 checksums, 13 // 3. adding "#include <string.h>" which is needed on some platforms 14 // to provide memcpy(). 15 // 4. adding read_end_info() and creating an end_info structure. 16 17 #include <stddef.h> 18 #include <stdint.h> 19 #include <string.h> 20 21 #include <vector> 22 23 #define PNG_INTERNAL 24 #include "png.h" 25 26 #define PNG_CLEANUP \ 27 if(png_handler.png_ptr) \ 28 { \ 29 if (png_handler.row_ptr) \ 30 png_free(png_handler.png_ptr, png_handler.row_ptr); \ 31 if (png_handler.end_info_ptr) \ 32 png_destroy_read_struct(&png_handler.png_ptr, &png_handler.info_ptr,\ 33 &png_handler.end_info_ptr); \ 34 else if (png_handler.info_ptr) \ 35 png_destroy_read_struct(&png_handler.png_ptr, &png_handler.info_ptr,\ 36 nullptr); \ 37 else \ 38 png_destroy_read_struct(&png_handler.png_ptr, nullptr, nullptr); \ 39 png_handler.png_ptr = nullptr; \ 40 png_handler.row_ptr = nullptr; \ 41 png_handler.info_ptr = nullptr; \ 42 png_handler.end_info_ptr = nullptr; \ 43 } 44 45 struct BufState { 46 const uint8_t* data; 47 size_t bytes_left; 48 }; 49 50 struct PngObjectHandler { 51 png_infop info_ptr = nullptr; 52 png_structp png_ptr = nullptr; 53 png_infop end_info_ptr = nullptr; 54 png_voidp row_ptr = nullptr; 55 BufState* buf_state = nullptr; 56 57 ~PngObjectHandler() { 58 if (row_ptr) 59 png_free(png_ptr, row_ptr); 60 if (end_info_ptr) 61 png_destroy_read_struct(&png_ptr, &info_ptr, &end_info_ptr); 62 else if (info_ptr) 63 png_destroy_read_struct(&png_ptr, &info_ptr, nullptr); 64 else 65 png_destroy_read_struct(&png_ptr, nullptr, nullptr); 66 delete buf_state; 67 } 68 }; 69 70 void user_read_data(png_structp png_ptr, png_bytep data, png_size_t length) { 71 BufState* buf_state = static_cast<BufState*>(png_get_io_ptr(png_ptr)); 72 if (length > buf_state->bytes_left) { 73 png_error(png_ptr, "read error"); 74 } 75 memcpy(data, buf_state->data, length); 76 buf_state->bytes_left -= length; 77 buf_state->data += length; 78 } 79 80 static const int kPngHeaderSize = 8; 81 82 // Entry point for LibFuzzer. 83 // Roughly follows the libpng book example: 84 // http://www.libpng.org/pub/png/book/chapter13.html 85 extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) { 86 if (size < kPngHeaderSize) { 87 return 0; 88 } 89 90 std::vector<unsigned char> v(data, data + size); 91 if (png_sig_cmp(v.data(), 0, kPngHeaderSize)) { 92 // not a PNG. 93 return 0; 94 } 95 96 PngObjectHandler png_handler; 97 png_handler.png_ptr = nullptr; 98 png_handler.row_ptr = nullptr; 99 png_handler.info_ptr = nullptr; 100 png_handler.end_info_ptr = nullptr; 101 102 png_handler.png_ptr = png_create_read_struct 103 (PNG_LIBPNG_VER_STRING, nullptr, nullptr, nullptr); 104 if (!png_handler.png_ptr) { 105 return 0; 106 } 107 108 png_handler.info_ptr = png_create_info_struct(png_handler.png_ptr); 109 if (!png_handler.info_ptr) { 110 PNG_CLEANUP 111 return 0; 112 } 113 114 png_handler.end_info_ptr = png_create_info_struct(png_handler.png_ptr); 115 if (!png_handler.end_info_ptr) { 116 PNG_CLEANUP 117 return 0; 118 } 119 120 png_set_crc_action(png_handler.png_ptr, PNG_CRC_QUIET_USE, PNG_CRC_QUIET_USE); 121 #ifdef PNG_IGNORE_ADLER32 122 png_set_option(png_handler.png_ptr, PNG_IGNORE_ADLER32, PNG_OPTION_ON); 123 #endif 124 125 // Setting up reading from buffer. 126 png_handler.buf_state = new BufState(); 127 png_handler.buf_state->data = data + kPngHeaderSize; 128 png_handler.buf_state->bytes_left = size - kPngHeaderSize; 129 png_set_read_fn(png_handler.png_ptr, png_handler.buf_state, user_read_data); 130 png_set_sig_bytes(png_handler.png_ptr, kPngHeaderSize); 131 132 if (setjmp(png_jmpbuf(png_handler.png_ptr))) { 133 PNG_CLEANUP 134 return 0; 135 } 136 137 // Reading. 138 png_read_info(png_handler.png_ptr, png_handler.info_ptr); 139 png_handler.row_ptr = png_malloc( 140 png_handler.png_ptr, png_get_rowbytes(png_handler.png_ptr, 141 png_handler.info_ptr)); 142 143 // reset error handler to put png_deleter into scope. 144 if (setjmp(png_jmpbuf(png_handler.png_ptr))) { 145 PNG_CLEANUP 146 return 0; 147 } 148 149 png_uint_32 width, height; 150 int bit_depth, color_type, interlace_type, compression_type; 151 int filter_type; 152 153 if (!png_get_IHDR(png_handler.png_ptr, png_handler.info_ptr, &width, 154 &height, &bit_depth, &color_type, &interlace_type, 155 &compression_type, &filter_type)) { 156 PNG_CLEANUP 157 return 0; 158 } 159 160 // This is going to be too slow. 161 if (width && height > 100000000 / width) { 162 PNG_CLEANUP 163 return 0; 164 } 165 166 int passes = png_set_interlace_handling(png_handler.png_ptr); 167 png_start_read_image(png_handler.png_ptr); 168 169 for (int pass = 0; pass < passes; ++pass) { 170 for (png_uint_32 y = 0; y < height; ++y) { 171 png_read_row(png_handler.png_ptr, 172 static_cast<png_bytep>(png_handler.row_ptr), nullptr); 173 } 174 } 175 176 png_read_end(png_handler.png_ptr, png_handler.end_info_ptr); 177 178 PNG_CLEANUP 179 return 0; 180 } 181