• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 #include <algorithm>
2 #include <cassert>
3 #include <cstring>
4 #include <string>
5 #include <vector>
6 
7 #include "png.h"
8 
9 namespace {
10 
11 struct PngReader {
12   png_structp png_ptr = nullptr;
13   png_infop info_ptr = nullptr;
14   png_infop end_info = nullptr;
15 };
16 
17 struct PngArrayStream {
18   const uint8_t *data;
19   size_t size;
20   size_t pos;
21 };
22 
PngArrayStreamCallback(png_structp png_ptr,png_bytep data,png_size_t size)23 void PngArrayStreamCallback(png_structp png_ptr, png_bytep data,
24                             png_size_t size) {
25   PngArrayStream *stream =
26       static_cast<PngArrayStream *>(png_get_io_ptr(png_ptr));
27   if (stream->pos + size > stream->size) {
28     memset(data, 0, size);
29     stream->pos = size;
30   } else {
31     memcpy(data, &stream->data[stream->pos], size);
32     stream->pos += size;
33   }
34 }
35 
36 static bool PngVerboseWarnings = getenv("PNG_VERBOSE_WARNINGS") != nullptr;
37 
PngErrorHandler(png_structp png_ptr,png_const_charp error_message)38 void PngErrorHandler(png_structp png_ptr, png_const_charp error_message) {
39   if (PngVerboseWarnings) fprintf(stderr, "%s\n", error_message);
40   longjmp(png_jmpbuf(png_ptr), 1);
41 }
42 
PngWarningHandler(png_structp png_ptr,png_const_charp warning_message)43 void PngWarningHandler(png_structp png_ptr, png_const_charp warning_message) {
44   if (PngVerboseWarnings) fprintf(stderr, "%s\n", warning_message);
45   longjmp(png_jmpbuf(png_ptr), 1);
46 }
47 
48 }  // namespace
49 
LLVMFuzzerTestOneInput(const uint8_t * data,size_t size)50 extern "C" int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size) {
51   const size_t kPngSignatureSize = 8;
52   const size_t kIHDRSize = 4 + 4 + 13 + 4;
53   const size_t kMaxImageSize = 1 << 20;
54   const size_t kMaxHeight = 1 << 10;
55 
56   auto Read32 = [&](const uint8_t *p) {
57     uint32_t res;
58     assert(p >= data);
59     assert(p + sizeof(res) < data + size);
60     memcpy(&res, p, sizeof(res));
61     return res;
62   };
63 
64   if (size < kPngSignatureSize + kIHDRSize) return 0;
65   if (png_sig_cmp(data, 0, kPngSignatureSize)) return 0;
66   uint32_t width = __builtin_bswap32(Read32(data + kPngSignatureSize + 8));
67   uint32_t height = __builtin_bswap32(Read32(data + kPngSignatureSize + 12));
68   // Reject too large images because they will OOM.
69   // Also reject images with a too large height, because large height
70   // will cause too many mallocs.
71   // These two heuristics are far from optimal and may cause us to lose some
72   // coverage. But w/o them fuzzing is way too slow.
73   if ((uint64_t)width * height > kMaxImageSize) return 0;
74   if (height > kMaxHeight) return 0;
75 
76   // Find the fUZz chunk and it's contents.
77   const size_t fUZz_chunk_size = 16;
78   const uint8_t fUZz_signature[8] = {0,   0,   0,   fUZz_chunk_size,
79                                      'f', 'U', 'Z', 'z'};
80   const uint8_t *fUZz_beg =
81       std::search(data, data + size, fUZz_signature,
82                   fUZz_signature + sizeof(fUZz_signature));
83   if (fUZz_beg + sizeof(fUZz_signature) + fUZz_chunk_size < data + size)
84     fUZz_beg += sizeof(fUZz_signature);
85   else
86     fUZz_beg = nullptr;
87 
88   PngReader reader;
89   reader.png_ptr =
90       png_create_read_struct(PNG_LIBPNG_VER_STRING, NULL, NULL, NULL);
91   assert(reader.png_ptr);
92   reader.info_ptr = png_create_info_struct(reader.png_ptr);
93   assert(reader.info_ptr);
94   reader.end_info = png_create_info_struct(reader.png_ptr);
95   assert(reader.end_info);
96 
97   png_set_error_fn(reader.png_ptr, png_get_error_ptr(reader.png_ptr),
98                    PngErrorHandler, PngWarningHandler);
99 
100   PngArrayStream stream{data, size, 0};
101 
102   if (setjmp(png_jmpbuf(reader.png_ptr)) == 0) {
103     png_set_read_fn(reader.png_ptr, &stream, PngArrayStreamCallback);
104 
105     // Take transforms from the fUZz chunk. By default, enable all.
106     int transforms = fUZz_beg ? Read32(fUZz_beg) : ~0;
107     png_read_png(reader.png_ptr, reader.info_ptr, transforms, nullptr);
108   }
109   png_destroy_read_struct(&reader.png_ptr, &reader.info_ptr, &reader.end_info);
110 
111   // Run the same image through another libpng API.
112   // There is probably some redundancy here (I don't know what I am doing!)
113   png_image image;
114   memset(&image, 0, sizeof(image));
115   image.version = PNG_IMAGE_VERSION;
116   if (png_image_begin_read_from_memory(&image, data, size)) {
117     const size_t kMaxBufferSize = 64 << 20;
118     image.format = fUZz_beg ? Read32(fUZz_beg + 4) : PNG_FORMAT_RGBA;
119     size_t image_size = PNG_IMAGE_SIZE(image);
120     if (image_size <= kMaxBufferSize) {
121       png_bytep buffer = new png_byte[image_size];
122       const size_t kColorMapSize = 256 * 4;
123       // Do we need to take color & colormap from the fuzzed input?
124       png_color color = {1, 2, 3};
125       png_uint_16 colormap[256*4] = {0};
126       for (size_t i = 0; i < kColorMapSize; i++)
127         colormap[i] = i;
128       png_image_finish_read(&image, &color, buffer, 0, colormap);
129       delete[] buffer;
130     }
131   }
132   png_image_free(&image);
133   return 0;
134 }
135 
__asan_default_options()136 extern "C" const char *__asan_default_options() {
137   // TODO: remove this once
138   // https://bugs.chromium.org/p/oss-fuzz/issues/detail?id=12716
139   // is fixed.
140   return "detect_leaks=0";
141 }
142