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
45struct BufState {
46  const uint8_t* data;
47  size_t bytes_left;
48};
49
50struct 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
70void 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
80static 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
85extern "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