xref: /aosp_15_r20/external/libpng/contrib/oss-fuzz/libpng_read_fuzzer.cc (revision a67afe4df73cf47866eedc69947994b8ff839aba)
1*a67afe4dSAndroid Build Coastguard Worker 
2*a67afe4dSAndroid Build Coastguard Worker // libpng_read_fuzzer.cc
3*a67afe4dSAndroid Build Coastguard Worker // Copyright 2017-2018 Glenn Randers-Pehrson
4*a67afe4dSAndroid Build Coastguard Worker // Copyright 2015 The Chromium Authors. All rights reserved.
5*a67afe4dSAndroid Build Coastguard Worker // Use of this source code is governed by a BSD-style license that may
6*a67afe4dSAndroid Build Coastguard Worker // be found in the LICENSE file https://cs.chromium.org/chromium/src/LICENSE
7*a67afe4dSAndroid Build Coastguard Worker 
8*a67afe4dSAndroid Build Coastguard Worker // The modifications in 2017 by Glenn Randers-Pehrson include
9*a67afe4dSAndroid Build Coastguard Worker // 1. addition of a PNG_CLEANUP macro,
10*a67afe4dSAndroid Build Coastguard Worker // 2. setting the option to ignore ADLER32 checksums,
11*a67afe4dSAndroid Build Coastguard Worker // 3. adding "#include <string.h>" which is needed on some platforms
12*a67afe4dSAndroid Build Coastguard Worker //    to provide memcpy().
13*a67afe4dSAndroid Build Coastguard Worker // 4. adding read_end_info() and creating an end_info structure.
14*a67afe4dSAndroid Build Coastguard Worker // 5. adding calls to png_set_*() transforms commonly used by browsers.
15*a67afe4dSAndroid Build Coastguard Worker 
16*a67afe4dSAndroid Build Coastguard Worker #include <stddef.h>
17*a67afe4dSAndroid Build Coastguard Worker #include <stdint.h>
18*a67afe4dSAndroid Build Coastguard Worker #include <stdlib.h>
19*a67afe4dSAndroid Build Coastguard Worker #include <string.h>
20*a67afe4dSAndroid Build Coastguard Worker 
21*a67afe4dSAndroid Build Coastguard Worker #include <vector>
22*a67afe4dSAndroid Build Coastguard Worker 
23*a67afe4dSAndroid Build Coastguard Worker #define PNG_INTERNAL
24*a67afe4dSAndroid Build Coastguard Worker #include "png.h"
25*a67afe4dSAndroid Build Coastguard Worker 
26*a67afe4dSAndroid Build Coastguard Worker #define PNG_CLEANUP \
27*a67afe4dSAndroid Build Coastguard Worker   if(png_handler.png_ptr) \
28*a67afe4dSAndroid Build Coastguard Worker   { \
29*a67afe4dSAndroid Build Coastguard Worker     if (png_handler.row_ptr) \
30*a67afe4dSAndroid Build Coastguard Worker       png_free(png_handler.png_ptr, png_handler.row_ptr); \
31*a67afe4dSAndroid Build Coastguard Worker     if (png_handler.end_info_ptr) \
32*a67afe4dSAndroid Build Coastguard Worker       png_destroy_read_struct(&png_handler.png_ptr, &png_handler.info_ptr,\
33*a67afe4dSAndroid Build Coastguard Worker         &png_handler.end_info_ptr); \
34*a67afe4dSAndroid Build Coastguard Worker     else if (png_handler.info_ptr) \
35*a67afe4dSAndroid Build Coastguard Worker       png_destroy_read_struct(&png_handler.png_ptr, &png_handler.info_ptr,\
36*a67afe4dSAndroid Build Coastguard Worker         nullptr); \
37*a67afe4dSAndroid Build Coastguard Worker     else \
38*a67afe4dSAndroid Build Coastguard Worker       png_destroy_read_struct(&png_handler.png_ptr, nullptr, nullptr); \
39*a67afe4dSAndroid Build Coastguard Worker     png_handler.png_ptr = nullptr; \
40*a67afe4dSAndroid Build Coastguard Worker     png_handler.row_ptr = nullptr; \
41*a67afe4dSAndroid Build Coastguard Worker     png_handler.info_ptr = nullptr; \
42*a67afe4dSAndroid Build Coastguard Worker     png_handler.end_info_ptr = nullptr; \
43*a67afe4dSAndroid Build Coastguard Worker   }
44*a67afe4dSAndroid Build Coastguard Worker 
45*a67afe4dSAndroid Build Coastguard Worker struct BufState {
46*a67afe4dSAndroid Build Coastguard Worker   const uint8_t* data;
47*a67afe4dSAndroid Build Coastguard Worker   size_t bytes_left;
48*a67afe4dSAndroid Build Coastguard Worker };
49*a67afe4dSAndroid Build Coastguard Worker 
50*a67afe4dSAndroid Build Coastguard Worker struct PngObjectHandler {
51*a67afe4dSAndroid Build Coastguard Worker   png_infop info_ptr = nullptr;
52*a67afe4dSAndroid Build Coastguard Worker   png_structp png_ptr = nullptr;
53*a67afe4dSAndroid Build Coastguard Worker   png_infop end_info_ptr = nullptr;
54*a67afe4dSAndroid Build Coastguard Worker   png_voidp row_ptr = nullptr;
55*a67afe4dSAndroid Build Coastguard Worker   BufState* buf_state = nullptr;
56*a67afe4dSAndroid Build Coastguard Worker 
~PngObjectHandlerPngObjectHandler57*a67afe4dSAndroid Build Coastguard Worker   ~PngObjectHandler() {
58*a67afe4dSAndroid Build Coastguard Worker     if (row_ptr)
59*a67afe4dSAndroid Build Coastguard Worker       png_free(png_ptr, row_ptr);
60*a67afe4dSAndroid Build Coastguard Worker     if (end_info_ptr)
61*a67afe4dSAndroid Build Coastguard Worker       png_destroy_read_struct(&png_ptr, &info_ptr, &end_info_ptr);
62*a67afe4dSAndroid Build Coastguard Worker     else if (info_ptr)
63*a67afe4dSAndroid Build Coastguard Worker       png_destroy_read_struct(&png_ptr, &info_ptr, nullptr);
64*a67afe4dSAndroid Build Coastguard Worker     else
65*a67afe4dSAndroid Build Coastguard Worker       png_destroy_read_struct(&png_ptr, nullptr, nullptr);
66*a67afe4dSAndroid Build Coastguard Worker     delete buf_state;
67*a67afe4dSAndroid Build Coastguard Worker   }
68*a67afe4dSAndroid Build Coastguard Worker };
69*a67afe4dSAndroid Build Coastguard Worker 
user_read_data(png_structp png_ptr,png_bytep data,size_t length)70*a67afe4dSAndroid Build Coastguard Worker void user_read_data(png_structp png_ptr, png_bytep data, size_t length) {
71*a67afe4dSAndroid Build Coastguard Worker   BufState* buf_state = static_cast<BufState*>(png_get_io_ptr(png_ptr));
72*a67afe4dSAndroid Build Coastguard Worker   if (length > buf_state->bytes_left) {
73*a67afe4dSAndroid Build Coastguard Worker     png_error(png_ptr, "read error");
74*a67afe4dSAndroid Build Coastguard Worker   }
75*a67afe4dSAndroid Build Coastguard Worker   memcpy(data, buf_state->data, length);
76*a67afe4dSAndroid Build Coastguard Worker   buf_state->bytes_left -= length;
77*a67afe4dSAndroid Build Coastguard Worker   buf_state->data += length;
78*a67afe4dSAndroid Build Coastguard Worker }
79*a67afe4dSAndroid Build Coastguard Worker 
limited_malloc(png_structp,png_alloc_size_t size)80*a67afe4dSAndroid Build Coastguard Worker void* limited_malloc(png_structp, png_alloc_size_t size) {
81*a67afe4dSAndroid Build Coastguard Worker   // libpng may allocate large amounts of memory that the fuzzer reports as
82*a67afe4dSAndroid Build Coastguard Worker   // an error. In order to silence these errors, make libpng fail when trying
83*a67afe4dSAndroid Build Coastguard Worker   // to allocate a large amount. This allocator used to be in the Chromium
84*a67afe4dSAndroid Build Coastguard Worker   // version of this fuzzer.
85*a67afe4dSAndroid Build Coastguard Worker   // This number is chosen to match the default png_user_chunk_malloc_max.
86*a67afe4dSAndroid Build Coastguard Worker   if (size > 8000000)
87*a67afe4dSAndroid Build Coastguard Worker     return nullptr;
88*a67afe4dSAndroid Build Coastguard Worker 
89*a67afe4dSAndroid Build Coastguard Worker   return malloc(size);
90*a67afe4dSAndroid Build Coastguard Worker }
91*a67afe4dSAndroid Build Coastguard Worker 
default_free(png_structp,png_voidp ptr)92*a67afe4dSAndroid Build Coastguard Worker void default_free(png_structp, png_voidp ptr) {
93*a67afe4dSAndroid Build Coastguard Worker   return free(ptr);
94*a67afe4dSAndroid Build Coastguard Worker }
95*a67afe4dSAndroid Build Coastguard Worker 
96*a67afe4dSAndroid Build Coastguard Worker static const int kPngHeaderSize = 8;
97*a67afe4dSAndroid Build Coastguard Worker 
98*a67afe4dSAndroid Build Coastguard Worker // Entry point for LibFuzzer.
99*a67afe4dSAndroid Build Coastguard Worker // Roughly follows the libpng book example:
100*a67afe4dSAndroid Build Coastguard Worker // http://www.libpng.org/pub/png/book/chapter13.html
LLVMFuzzerTestOneInput(const uint8_t * data,size_t size)101*a67afe4dSAndroid Build Coastguard Worker extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) {
102*a67afe4dSAndroid Build Coastguard Worker   if (size < kPngHeaderSize) {
103*a67afe4dSAndroid Build Coastguard Worker     return 0;
104*a67afe4dSAndroid Build Coastguard Worker   }
105*a67afe4dSAndroid Build Coastguard Worker 
106*a67afe4dSAndroid Build Coastguard Worker   std::vector<unsigned char> v(data, data + size);
107*a67afe4dSAndroid Build Coastguard Worker   if (png_sig_cmp(v.data(), 0, kPngHeaderSize)) {
108*a67afe4dSAndroid Build Coastguard Worker     // not a PNG.
109*a67afe4dSAndroid Build Coastguard Worker     return 0;
110*a67afe4dSAndroid Build Coastguard Worker   }
111*a67afe4dSAndroid Build Coastguard Worker 
112*a67afe4dSAndroid Build Coastguard Worker   PngObjectHandler png_handler;
113*a67afe4dSAndroid Build Coastguard Worker   png_handler.png_ptr = nullptr;
114*a67afe4dSAndroid Build Coastguard Worker   png_handler.row_ptr = nullptr;
115*a67afe4dSAndroid Build Coastguard Worker   png_handler.info_ptr = nullptr;
116*a67afe4dSAndroid Build Coastguard Worker   png_handler.end_info_ptr = nullptr;
117*a67afe4dSAndroid Build Coastguard Worker 
118*a67afe4dSAndroid Build Coastguard Worker   png_handler.png_ptr = png_create_read_struct
119*a67afe4dSAndroid Build Coastguard Worker     (PNG_LIBPNG_VER_STRING, nullptr, nullptr, nullptr);
120*a67afe4dSAndroid Build Coastguard Worker   if (!png_handler.png_ptr) {
121*a67afe4dSAndroid Build Coastguard Worker     return 0;
122*a67afe4dSAndroid Build Coastguard Worker   }
123*a67afe4dSAndroid Build Coastguard Worker 
124*a67afe4dSAndroid Build Coastguard Worker   png_handler.info_ptr = png_create_info_struct(png_handler.png_ptr);
125*a67afe4dSAndroid Build Coastguard Worker   if (!png_handler.info_ptr) {
126*a67afe4dSAndroid Build Coastguard Worker     PNG_CLEANUP
127*a67afe4dSAndroid Build Coastguard Worker     return 0;
128*a67afe4dSAndroid Build Coastguard Worker   }
129*a67afe4dSAndroid Build Coastguard Worker 
130*a67afe4dSAndroid Build Coastguard Worker   png_handler.end_info_ptr = png_create_info_struct(png_handler.png_ptr);
131*a67afe4dSAndroid Build Coastguard Worker   if (!png_handler.end_info_ptr) {
132*a67afe4dSAndroid Build Coastguard Worker     PNG_CLEANUP
133*a67afe4dSAndroid Build Coastguard Worker     return 0;
134*a67afe4dSAndroid Build Coastguard Worker   }
135*a67afe4dSAndroid Build Coastguard Worker 
136*a67afe4dSAndroid Build Coastguard Worker   // Use a custom allocator that fails for large allocations to avoid OOM.
137*a67afe4dSAndroid Build Coastguard Worker   png_set_mem_fn(png_handler.png_ptr, nullptr, limited_malloc, default_free);
138*a67afe4dSAndroid Build Coastguard Worker 
139*a67afe4dSAndroid Build Coastguard Worker   png_set_crc_action(png_handler.png_ptr, PNG_CRC_QUIET_USE, PNG_CRC_QUIET_USE);
140*a67afe4dSAndroid Build Coastguard Worker #ifdef PNG_IGNORE_ADLER32
141*a67afe4dSAndroid Build Coastguard Worker   png_set_option(png_handler.png_ptr, PNG_IGNORE_ADLER32, PNG_OPTION_ON);
142*a67afe4dSAndroid Build Coastguard Worker #endif
143*a67afe4dSAndroid Build Coastguard Worker 
144*a67afe4dSAndroid Build Coastguard Worker   // Setting up reading from buffer.
145*a67afe4dSAndroid Build Coastguard Worker   png_handler.buf_state = new BufState();
146*a67afe4dSAndroid Build Coastguard Worker   png_handler.buf_state->data = data + kPngHeaderSize;
147*a67afe4dSAndroid Build Coastguard Worker   png_handler.buf_state->bytes_left = size - kPngHeaderSize;
148*a67afe4dSAndroid Build Coastguard Worker   png_set_read_fn(png_handler.png_ptr, png_handler.buf_state, user_read_data);
149*a67afe4dSAndroid Build Coastguard Worker   png_set_sig_bytes(png_handler.png_ptr, kPngHeaderSize);
150*a67afe4dSAndroid Build Coastguard Worker 
151*a67afe4dSAndroid Build Coastguard Worker   if (setjmp(png_jmpbuf(png_handler.png_ptr))) {
152*a67afe4dSAndroid Build Coastguard Worker     PNG_CLEANUP
153*a67afe4dSAndroid Build Coastguard Worker     return 0;
154*a67afe4dSAndroid Build Coastguard Worker   }
155*a67afe4dSAndroid Build Coastguard Worker 
156*a67afe4dSAndroid Build Coastguard Worker   // Reading.
157*a67afe4dSAndroid Build Coastguard Worker   png_read_info(png_handler.png_ptr, png_handler.info_ptr);
158*a67afe4dSAndroid Build Coastguard Worker 
159*a67afe4dSAndroid Build Coastguard Worker   // reset error handler to put png_deleter into scope.
160*a67afe4dSAndroid Build Coastguard Worker   if (setjmp(png_jmpbuf(png_handler.png_ptr))) {
161*a67afe4dSAndroid Build Coastguard Worker     PNG_CLEANUP
162*a67afe4dSAndroid Build Coastguard Worker     return 0;
163*a67afe4dSAndroid Build Coastguard Worker   }
164*a67afe4dSAndroid Build Coastguard Worker 
165*a67afe4dSAndroid Build Coastguard Worker   png_uint_32 width, height;
166*a67afe4dSAndroid Build Coastguard Worker   int bit_depth, color_type, interlace_type, compression_type;
167*a67afe4dSAndroid Build Coastguard Worker   int filter_type;
168*a67afe4dSAndroid Build Coastguard Worker 
169*a67afe4dSAndroid Build Coastguard Worker   if (!png_get_IHDR(png_handler.png_ptr, png_handler.info_ptr, &width,
170*a67afe4dSAndroid Build Coastguard Worker                     &height, &bit_depth, &color_type, &interlace_type,
171*a67afe4dSAndroid Build Coastguard Worker                     &compression_type, &filter_type)) {
172*a67afe4dSAndroid Build Coastguard Worker     PNG_CLEANUP
173*a67afe4dSAndroid Build Coastguard Worker     return 0;
174*a67afe4dSAndroid Build Coastguard Worker   }
175*a67afe4dSAndroid Build Coastguard Worker 
176*a67afe4dSAndroid Build Coastguard Worker   // This is going to be too slow.
177*a67afe4dSAndroid Build Coastguard Worker   if (width && height > 100000000 / width) {
178*a67afe4dSAndroid Build Coastguard Worker     PNG_CLEANUP
179*a67afe4dSAndroid Build Coastguard Worker     return 0;
180*a67afe4dSAndroid Build Coastguard Worker   }
181*a67afe4dSAndroid Build Coastguard Worker 
182*a67afe4dSAndroid Build Coastguard Worker   // Set several transforms that browsers typically use:
183*a67afe4dSAndroid Build Coastguard Worker   png_set_gray_to_rgb(png_handler.png_ptr);
184*a67afe4dSAndroid Build Coastguard Worker   png_set_expand(png_handler.png_ptr);
185*a67afe4dSAndroid Build Coastguard Worker   png_set_packing(png_handler.png_ptr);
186*a67afe4dSAndroid Build Coastguard Worker   png_set_scale_16(png_handler.png_ptr);
187*a67afe4dSAndroid Build Coastguard Worker   png_set_tRNS_to_alpha(png_handler.png_ptr);
188*a67afe4dSAndroid Build Coastguard Worker 
189*a67afe4dSAndroid Build Coastguard Worker   int passes = png_set_interlace_handling(png_handler.png_ptr);
190*a67afe4dSAndroid Build Coastguard Worker 
191*a67afe4dSAndroid Build Coastguard Worker   png_read_update_info(png_handler.png_ptr, png_handler.info_ptr);
192*a67afe4dSAndroid Build Coastguard Worker 
193*a67afe4dSAndroid Build Coastguard Worker   png_handler.row_ptr = png_malloc(
194*a67afe4dSAndroid Build Coastguard Worker       png_handler.png_ptr, png_get_rowbytes(png_handler.png_ptr,
195*a67afe4dSAndroid Build Coastguard Worker                                             png_handler.info_ptr));
196*a67afe4dSAndroid Build Coastguard Worker 
197*a67afe4dSAndroid Build Coastguard Worker   for (int pass = 0; pass < passes; ++pass) {
198*a67afe4dSAndroid Build Coastguard Worker     for (png_uint_32 y = 0; y < height; ++y) {
199*a67afe4dSAndroid Build Coastguard Worker       png_read_row(png_handler.png_ptr,
200*a67afe4dSAndroid Build Coastguard Worker                    static_cast<png_bytep>(png_handler.row_ptr), nullptr);
201*a67afe4dSAndroid Build Coastguard Worker     }
202*a67afe4dSAndroid Build Coastguard Worker   }
203*a67afe4dSAndroid Build Coastguard Worker 
204*a67afe4dSAndroid Build Coastguard Worker   png_read_end(png_handler.png_ptr, png_handler.end_info_ptr);
205*a67afe4dSAndroid Build Coastguard Worker 
206*a67afe4dSAndroid Build Coastguard Worker   PNG_CLEANUP
207*a67afe4dSAndroid Build Coastguard Worker 
208*a67afe4dSAndroid Build Coastguard Worker #ifdef PNG_SIMPLIFIED_READ_SUPPORTED
209*a67afe4dSAndroid Build Coastguard Worker   // Simplified READ API
210*a67afe4dSAndroid Build Coastguard Worker   png_image image;
211*a67afe4dSAndroid Build Coastguard Worker   memset(&image, 0, (sizeof image));
212*a67afe4dSAndroid Build Coastguard Worker   image.version = PNG_IMAGE_VERSION;
213*a67afe4dSAndroid Build Coastguard Worker 
214*a67afe4dSAndroid Build Coastguard Worker   if (!png_image_begin_read_from_memory(&image, data, size)) {
215*a67afe4dSAndroid Build Coastguard Worker     return 0;
216*a67afe4dSAndroid Build Coastguard Worker   }
217*a67afe4dSAndroid Build Coastguard Worker 
218*a67afe4dSAndroid Build Coastguard Worker   image.format = PNG_FORMAT_RGBA;
219*a67afe4dSAndroid Build Coastguard Worker   std::vector<png_byte> buffer(PNG_IMAGE_SIZE(image));
220*a67afe4dSAndroid Build Coastguard Worker   png_image_finish_read(&image, NULL, buffer.data(), 0, NULL);
221*a67afe4dSAndroid Build Coastguard Worker #endif
222*a67afe4dSAndroid Build Coastguard Worker 
223*a67afe4dSAndroid Build Coastguard Worker   return 0;
224*a67afe4dSAndroid Build Coastguard Worker }
225