• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 // Example fuzzer for PNG using protos.
2 #include <string>
3 #include <sstream>
4 #include <fstream>
5 #include <zlib.h>  // for crc32
6 
7 #include "libprotobuf-mutator/src/libfuzzer/libfuzzer_macro.h"
8 #include "png_fuzz_proto.pb.h"
9 
WriteInt(std::stringstream & out,uint32_t x)10 static void WriteInt(std::stringstream &out, uint32_t x) {
11   x = __builtin_bswap32(x);
12   out.write((char *)&x, sizeof(x));
13 }
14 
WriteByte(std::stringstream & out,uint8_t x)15 static void WriteByte(std::stringstream &out, uint8_t x) {
16   out.write((char *)&x, sizeof(x));
17 }
18 
Compress(const std::string & s)19 static std::string Compress(const std::string &s) {
20   std::string out(s.size() + 100, '\0');
21   size_t out_len = out.size();
22   compress((uint8_t *)&out[0], &out_len, (uint8_t *)s.data(), s.size());
23   out.resize(out_len);
24   return out;
25 }
26 
27 // Chunk is written as:
28 //  * 4-byte length
29 //  * 4-byte type
30 //  * the data itself
31 //  * 4-byte crc (of type and data)
WriteChunk(std::stringstream & out,const char * type,const std::string & chunk,bool compress=false)32 static void WriteChunk(std::stringstream &out, const char *type,
33                        const std::string &chunk, bool compress = false) {
34   std::string compressed;
35   const std::string *s = &chunk;
36   if (compress) {
37     compressed = Compress(chunk);
38     s = &compressed;
39   }
40   uint32_t len = s->size();
41   uint32_t crc = crc32(crc32(0, (const unsigned char *)type, 4),
42                        (const unsigned char *)s->data(), s->size());
43   WriteInt(out, len);
44   out.write(type, 4);
45   out.write(s->data(), s->size());
46   WriteInt(out, crc);
47 }
48 
ProtoToPng(const PngProto & png_proto)49 std::string ProtoToPng(const PngProto &png_proto) {
50   std::stringstream all;
51   const unsigned char header[] = {0x89, 0x50, 0x4e, 0x47, 0x0d, 0x0a, 0x1a, 0x0a};
52   all.write((const char*)header, sizeof(header));
53   std::stringstream ihdr_str;
54   auto &ihdr = png_proto.ihdr();
55   // Avoid large images.
56   // They may have interesting bugs, but OOMs are going to kill fuzzing.
57   uint32_t w = std::min(ihdr.width(), 4096U);
58   uint32_t h = std::min(ihdr.height(), 4096U);
59   WriteInt(ihdr_str, w);
60   WriteInt(ihdr_str, h);
61   WriteInt(ihdr_str, ihdr.other1());
62   WriteByte(ihdr_str, ihdr.other2());
63   WriteChunk(all, "IHDR", ihdr_str.str());
64 
65   for (size_t i = 0, n = png_proto.chunks_size(); i < n; i++) {
66     auto &chunk = png_proto.chunks(i);
67     if (chunk.has_plte()) {
68       WriteChunk(all, "PLTE", chunk.plte().data());
69     } else if (chunk.has_idat()) {
70       WriteChunk(all, "IDAT", chunk.idat().data(), true);
71     } else if (chunk.has_iccp()) {
72       std::stringstream iccp_str;
73       iccp_str << "xyz";  // don't fuzz iCCP name field.
74       WriteByte(iccp_str, 0);
75       WriteByte(iccp_str, 0);
76       auto compressed_data = Compress(chunk.iccp().data());
77       iccp_str.write(compressed_data.data(), compressed_data.size());
78       WriteChunk(all, "iCCP", iccp_str.str());
79     } else if (chunk.has_other_chunk()) {
80       auto &other_chunk = chunk.other_chunk();
81       char type[5] = {0};
82       if (other_chunk.has_known_type()) {
83         static const char * known_chunks[] = {
84             "bKGD", "cHRM", "dSIG", "eXIf", "gAMA", "hIST", "iCCP",
85             "iTXt", "pHYs", "sBIT", "sPLT", "sRGB", "sTER", "tEXt",
86             "tIME", "tRNS", "zTXt", "sCAL", "pCAL", "oFFs",
87         };
88         size_t known_chunks_size =
89             sizeof(known_chunks) / sizeof(known_chunks[0]);
90         size_t chunk_idx = other_chunk.known_type() % known_chunks_size;
91         memcpy(type, known_chunks[chunk_idx], 4);
92       } else if (other_chunk.has_unknown_type()) {
93         uint32_t unknown_type_int = other_chunk.unknown_type();
94         memcpy(type, &unknown_type_int, 4);
95       } else {
96         continue;
97       }
98       type[4] = 0;
99       WriteChunk(all, type, other_chunk.data());
100     }
101   }
102   WriteChunk(all, "IEND", "");
103 
104   std::string res = all.str();
105   if (const char *dump_path = getenv("PROTO_FUZZER_DUMP_PATH")) {
106     // With libFuzzer binary run this to generate a PNG file x.png:
107     // PROTO_FUZZER_DUMP_PATH=x.png ./a.out proto-input
108     std::ofstream of(dump_path);
109     of.write(res.data(), res.size());
110   }
111   return res;
112 }
113 
114 // The actual fuzz target that consumes the PNG data.
115 extern "C" int FuzzPNG(const uint8_t* data, size_t size);
116 
DEFINE_PROTO_FUZZER(const PngProto & png_proto)117 DEFINE_PROTO_FUZZER(const PngProto &png_proto) {
118   auto s = ProtoToPng(png_proto);
119   FuzzPNG((const uint8_t*)s.data(), s.size());
120 }
121