1 // Copyright 2019 Google Inc. All Rights Reserved.
2 // Licensed under the Apache License, Version 2.0 (the "License");
3 #include <algorithm>
4 #include <iostream>
5 #include <sstream>
6 #include <string>
7 #include <vector>
8 #include <cstring>
9 #include <cassert>
10 #include <random>
11
12 #include <zlib.h>
13
14 // A simple class for parsing, serializing, and mutating an PNG file.
15 // https://en.wikipedia.org/wiki/Portable_Network_Graphics
16 // It is an example of a custom mutator for libFuzzer
17 // (https://llvm.org/docs/LibFuzzer.html) used for
18 // "structure-aware coverage-guided fuzzing".
19 //
20 // If you have a non structure-aware fuzz target for any API that handles
21 // PNG inputs, you can turn that fuzz target into a structure-aware one
22 // by defining PNG_MUTATOR_DEFINE_LIBFUZZER_CUSTOM_MUTATOR and then
23 // including this file.
24 class PngMutator {
25 using V = std::vector<uint8_t>;
26
27 public:
28
29 // Parse the input stream as a PNG file,
30 // put every chunk into its own vector,
31 // uncompress chunk data when needed,
32 // merge the IDAT chunks into one vector.
PngMutator(std::istream & in)33 PngMutator(std::istream &in) {
34 ihdr_.resize(13);
35 Read4(in);
36 Read4(in); // Skip the 8-byte magic value.
37 // read IHDR.
38 if (ReadInteger(in) != 13) return;
39 if (Read4(in) != Type("IHDR")) return;
40 // Read 13 values.
41 in.read((char*)ihdr_.data(), ihdr_.size());
42 Read4(in); // ignore CRC
43 ssize_t idat_idx = -1;
44
45 while (in) {
46 uint32_t len = ReadInteger(in);
47 uint32_t type = Read4(in);
48 if (type == Type("IEND")) break; // do nothing
49 char chunk_name[5];
50 memcpy(chunk_name, &type, 4);
51 chunk_name[4] = 0;
52 if (len > (1 << 20)) return;
53 V v(len);
54 in.read((char *)v.data(), len);
55 Read4(in); // ignore CRC
56
57 if (type == Type("IDAT")) {
58 if (idat_idx != -1)
59 Append(&chunks_[idat_idx].v, v);
60 else {
61 idat_idx = chunks_.size();
62 chunks_.push_back({type, v});
63 }
64 } else if (type == Type("iCCP")) {
65 auto it = v.begin();
66 while (it < v.end() && isprint(*it)) it++;
67 if (it < v.end() && !*it) it++;
68 if (it < v.end() && !*it) it++;
69 v = V(it, v.end());
70 auto uncompressed = Uncompress(v);
71 chunks_.push_back({type, uncompressed});
72 auto compressed = Compress(uncompressed);
73 } else {
74 chunks_.push_back({type, v});
75 }
76 // std::cerr << "CHUNK: " << chunk_name << std::endl;
77 }
78 if (idat_idx != -1)
79 chunks_[idat_idx].v = Uncompress(chunks_[idat_idx].v);
80 }
81
82 // Write back the PNG file.
Serialize(std::ostream & out)83 void Serialize(std::ostream &out) {
84 const unsigned char header[] = {0x89, 0x50, 0x4e, 0x47,
85 0x0d, 0x0a, 0x1a, 0x0a};
86 out.write((const char*)header, sizeof(header));
87 WriteChunk(out, "IHDR", ihdr_);
88 for (auto &ch : chunks_) {
89 if (ch.type == Type("iCCP")) {
90 V v;
91 v.push_back('x'); // assuming the iCCP name doesn't matter.
92 v.push_back(0);
93 v.push_back(0);
94 auto compressed = Compress(ch.v);
95 Append(&v, compressed);
96 WriteChunk(out, ch.type, v);
97 } else {
98 WriteChunk(out, ch.type, ch.v);
99 }
100 }
101
102 WriteChunk(out, "IEND", {});
103 }
104
105 // Raw byte array mutator, like that provided by libFuzzer.
106 using Mutator = size_t (*)(uint8_t *Data, size_t Size, size_t MaxSize);
107
108 // Mutate the in-memory representation of a PNG file.
109 // Given the same Seed, the same mutation is performed.
Mutate(Mutator m,unsigned int Seed)110 void Mutate(Mutator m, unsigned int Seed) {
111 std::minstd_rand rnd(Seed);
112 auto M = [&](V *v) {
113 if (v->empty())
114 v->resize(v->size() + 1 + rnd() % 256);
115 v->resize(m(v->data(), v->size(), v->size()));
116 };
117 switch (rnd() % 6) {
118 // Mutate IHDR.
119 case 0:
120 m(ihdr_.data(), ihdr_.size(), ihdr_.size());
121 break;
122 // Mutate some other chunk.
123 case 1:
124 if (!chunks_.empty()) M(&chunks_[rnd() % chunks_.size()].v);
125 break;
126 // Shuffle the chunks.
127 case 2:
128 std::shuffle(chunks_.begin(), chunks_.end(), rnd);
129 break;
130 // Delete a random chunk.
131 case 3:
132 if (!chunks_.empty())
133 chunks_.erase(chunks_.begin() + rnd() % chunks_.size());
134 break;
135 // Insert a random chunk with one of the known types, or a random type.
136 case 4: {
137 static const char *types[] = {
138 "IATx", "sTER", "hIST", "sPLT", "mkBF", "mkBS", "mkTS", "prVW",
139 "oFFs", "iDOT", "zTXt", "mkBT", "acTL", "iTXt", "sBIT", "tIME",
140 "iCCP", "vpAg", "tRNS", "cHRM", "PLTE", "bKGD", "gAMA", "sRGB",
141 "pHYs", "fdAT", "fcTL", "tEXt", "IDAT",
142 "pCAL", "sCAL", "eXIf",
143 "fUZz", // special chunk for extra fuzzing hints.
144 };
145 static const size_t n_types = sizeof(types) / sizeof(types[0]);
146 uint32_t type =
147 (rnd() % 10 <= 8) ? Type(types[rnd() % n_types]) : (uint32_t)rnd();
148 size_t len = rnd() % 256;
149 if (type == Type("fUZz"))
150 len = 16;
151 V v(len);
152 for (auto &b : v) b = rnd();
153 size_t pos = rnd() % (chunks_.size() + 1);
154 chunks_.insert(chunks_.begin() + pos, {type, v});
155 } break;
156 // Any more interesting mutations with a PNG file?
157 case 5: {
158 auto it = std::find_if(
159 chunks_.begin(), chunks_.end(),
160 [](const Chunk &ch) { return ch.type == Type("fUZz"); });
161 if (it != chunks_.end())
162 m(it->v.data(), it->v.size(), it->v.size());
163 }
164
165 }
166 }
167
168 // Takes a random chunk from p and inserts into *this.
CrossOver(const PngMutator & p,unsigned int Seed)169 void CrossOver(const PngMutator &p, unsigned int Seed) {
170 if (p.chunks_.empty()) return;
171 std::minstd_rand rnd(Seed);
172 size_t idx = rnd() % p.chunks_.size();
173 auto &ch = p.chunks_[idx];
174 size_t pos = rnd() % (chunks_.size() + 1);
175 chunks_.insert(chunks_.begin() + pos, ch);
176 }
177
178 private:
Append(V * to,const V & from)179 void Append(V *to, const V &from) {
180 to->insert(to->end(), from.begin(), from.end());
181 }
182
Read4(std::istream & in)183 uint32_t Read4(std::istream &in) {
184 uint32_t res = 0;
185 in.read((char *)&res, sizeof(res));
186 return res;
187 }
ReadInteger(std::istream & in)188 uint32_t ReadInteger(std::istream &in) {
189 return __builtin_bswap32(Read4(in));
190 }
Type(const char * tagname)191 static uint32_t Type(const char *tagname) {
192 uint32_t res;
193 assert(strlen(tagname) == 4);
194 memcpy(&res, tagname, 4);
195 return res;
196 }
197
WriteInt(std::ostream & out,uint32_t x)198 void WriteInt(std::ostream &out, uint32_t x) {
199 x = __builtin_bswap32(x);
200 out.write((char *)&x, sizeof(x));
201 }
202
203 // Chunk is written as:
204 // * 4-byte length
205 // * 4-byte type
206 // * the data itself
207 // * 4-byte crc (of type and data)
208 void WriteChunk(std::ostream &out, const char *type, const V &chunk,
209 bool compress = false) {
210 V compressed;
211 const V *v = &chunk;
212 if (compress) {
213 compressed = Compress(chunk);
214 v = &compressed;
215 }
216 uint32_t len = v->size();
217 uint32_t crc = crc32(0, (const unsigned char *)type, 4);
218 if (v->size())
219 crc = crc32(crc, (const unsigned char *)v->data(), v->size());
220 WriteInt(out, len);
221 out.write(type, 4);
222 out.write((const char*)v->data(), v->size());
223 WriteInt(out, crc);
224 }
225
WriteChunk(std::ostream & out,uint32_t type,const V & chunk)226 void WriteChunk(std::ostream &out, uint32_t type, const V &chunk) {
227 char type_s[5];
228 memcpy(type_s, &type, 4);
229 type_s[4] = 0;
230 WriteChunk(out, type_s, chunk);
231 }
232
Uncompress(const V & compressed)233 V Uncompress(const V &compressed) {
234 V v;
235 static const size_t kMaxBuffer = 1 << 28;
236 for (size_t sz = compressed.size() * 4; sz < kMaxBuffer; sz *= 2) {
237 v.resize(sz);
238 unsigned long len = sz;
239 auto res =
240 uncompress(v.data(), &len, compressed.data(), compressed.size());
241 if (res == Z_BUF_ERROR) continue;
242 if (res != Z_OK) return {};
243 v.resize(len);
244 break;
245 }
246 return v;
247 }
248
Compress(const V & uncompressed)249 V Compress(const V &uncompressed) {
250 V v;
251 static const size_t kMaxBuffer = 1 << 28;
252 for (size_t sz = uncompressed.size(); sz < kMaxBuffer; sz *= 2) {
253 v.resize(sz);
254 unsigned long len = sz;
255 auto res =
256 compress(v.data(), &len, uncompressed.data(), uncompressed.size());
257 if (res == Z_BUF_ERROR) continue;
258 if (res != Z_OK) return {};
259 v.resize(len);
260 break;
261 }
262 return v;
263 }
264
PrintHex(const V & v,size_t max_n)265 void PrintHex(const V &v, size_t max_n) {
266 for (size_t i = 0; i < max_n && i < v.size(); i++) {
267 std::cerr << "0x" << std::hex << (unsigned)v[i] << " " << std::dec;
268 }
269 std::cerr << std::endl;
270 }
271
272 V ihdr_;
273
274 struct Chunk {
275 uint32_t type;
276 V v;
277 };
278 std::vector<Chunk> chunks_;
279 };
280
281
282 #ifdef PNG_MUTATOR_DEFINE_LIBFUZZER_CUSTOM_MUTATOR
283
284 extern "C" size_t LLVMFuzzerMutate(uint8_t *Data, size_t Size, size_t MaxSize);
285
286 #if STANDALONE_TARGET
LLVMFuzzerMutate(uint8_t * Data,size_t Size,size_t MaxSize)287 size_t LLVMFuzzerMutate(uint8_t *Data, size_t Size, size_t MaxSize) {
288 assert(false && "LLVMFuzzerMutate should not be called from StandaloneFuzzTargetMain");
289 return 0;
290 }
291 #endif
292
LLVMFuzzerCustomMutator(uint8_t * Data,size_t Size,size_t MaxSize,unsigned int Seed)293 extern "C" size_t LLVMFuzzerCustomMutator(uint8_t *Data, size_t Size,
294 size_t MaxSize, unsigned int Seed) {
295 std::string s(reinterpret_cast<const char*>(Data), Size);
296 std::stringstream in(s);
297 std::stringstream out;
298 PngMutator p(in);
299 p.Mutate(LLVMFuzzerMutate, Seed);
300 p.Serialize(out);
301 const auto &str = out.str();
302 if (str.size() > MaxSize) return Size;
303 memcpy(Data, str.data(), str.size());
304 return str.size();
305 }
306
LLVMFuzzerCustomCrossOver(const uint8_t * Data1,size_t Size1,const uint8_t * Data2,size_t Size2,uint8_t * Out,size_t MaxOutSize,unsigned int Seed)307 extern "C" size_t LLVMFuzzerCustomCrossOver(const uint8_t *Data1, size_t Size1,
308 const uint8_t *Data2, size_t Size2,
309 uint8_t *Out, size_t MaxOutSize,
310 unsigned int Seed) {
311 std::stringstream in1(
312 std::string(reinterpret_cast<const char *>(Data1), Size1));
313 std::stringstream in2(
314 std::string(reinterpret_cast<const char *>(Data2), Size2));
315 PngMutator p1(in1);
316 PngMutator p2(in2);
317 p1.CrossOver(p2, Seed);
318 std::stringstream out;
319 p1.Serialize(out);
320 const auto &str = out.str();
321 if (str.size() > MaxOutSize) return 0;
322 memcpy(Out, str.data(), str.size());
323 return str.size();
324 }
325
326 #endif // PNG_MUTATOR_DEFINE_LIBFUZZER_CUSTOM_MUTATOR
327