/src/fuzzer-test-suite/libpng-1.2.56/png_mutator.h
Line | Count | Source (jump to first uncovered line) |
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. |
33 | 0 | PngMutator(std::istream &in) { |
34 | 0 | ihdr_.resize(13); |
35 | 0 | Read4(in); |
36 | 0 | Read4(in); // Skip the 8-byte magic value. |
37 | | // read IHDR. |
38 | 0 | if (ReadInteger(in) != 13) return; |
39 | 0 | if (Read4(in) != Type("IHDR")) return; |
40 | | // Read 13 values. |
41 | 0 | in.read((char*)ihdr_.data(), ihdr_.size()); |
42 | 0 | Read4(in); // ignore CRC |
43 | 0 | ssize_t idat_idx = -1; |
44 | |
|
45 | 0 | while (in) { |
46 | 0 | uint32_t len = ReadInteger(in); |
47 | 0 | uint32_t type = Read4(in); |
48 | 0 | if (type == Type("IEND")) break; // do nothing |
49 | 0 | char chunk_name[5]; |
50 | 0 | memcpy(chunk_name, &type, 4); |
51 | 0 | chunk_name[4] = 0; |
52 | 0 | if (len > (1 << 20)) return; |
53 | 0 | V v(len); |
54 | 0 | in.read((char *)v.data(), len); |
55 | 0 | Read4(in); // ignore CRC |
56 | |
|
57 | 0 | if (type == Type("IDAT")) { |
58 | 0 | if (idat_idx != -1) |
59 | 0 | Append(&chunks_[idat_idx].v, v); |
60 | 0 | else { |
61 | 0 | idat_idx = chunks_.size(); |
62 | 0 | chunks_.push_back({type, v}); |
63 | 0 | } |
64 | 0 | } else if (type == Type("iCCP")) { |
65 | 0 | auto it = v.begin(); |
66 | 0 | while (it < v.end() && isprint(*it)) it++; |
67 | 0 | if (it < v.end() && !*it) it++; |
68 | 0 | if (it < v.end() && !*it) it++; |
69 | 0 | v = V(it, v.end()); |
70 | 0 | auto uncompressed = Uncompress(v); |
71 | 0 | chunks_.push_back({type, uncompressed}); |
72 | 0 | auto compressed = Compress(uncompressed); |
73 | 0 | } else { |
74 | 0 | chunks_.push_back({type, v}); |
75 | 0 | } |
76 | | // std::cerr << "CHUNK: " << chunk_name << std::endl; |
77 | 0 | } |
78 | 0 | if (idat_idx != -1) |
79 | 0 | chunks_[idat_idx].v = Uncompress(chunks_[idat_idx].v); |
80 | 0 | } |
81 | | |
82 | | // Write back the PNG file. |
83 | 0 | void Serialize(std::ostream &out) { |
84 | 0 | const unsigned char header[] = {0x89, 0x50, 0x4e, 0x47, |
85 | 0 | 0x0d, 0x0a, 0x1a, 0x0a}; |
86 | 0 | out.write((const char*)header, sizeof(header)); |
87 | 0 | WriteChunk(out, "IHDR", ihdr_); |
88 | 0 | for (auto &ch : chunks_) { |
89 | 0 | if (ch.type == Type("iCCP")) { |
90 | 0 | V v; |
91 | 0 | v.push_back('x'); // assuming the iCCP name doesn't matter. |
92 | 0 | v.push_back(0); |
93 | 0 | v.push_back(0); |
94 | 0 | auto compressed = Compress(ch.v); |
95 | 0 | Append(&v, compressed); |
96 | 0 | WriteChunk(out, ch.type, v); |
97 | 0 | } else { |
98 | 0 | WriteChunk(out, ch.type, ch.v); |
99 | 0 | } |
100 | 0 | } |
101 | |
|
102 | 0 | WriteChunk(out, "IEND", {}); |
103 | 0 | } |
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. |
110 | 0 | void Mutate(Mutator m, unsigned int Seed) { |
111 | 0 | std::minstd_rand rnd(Seed); |
112 | 0 | auto M = [&](V *v) { |
113 | 0 | if (v->empty()) |
114 | 0 | v->resize(v->size() + 1 + rnd() % 256); |
115 | 0 | v->resize(m(v->data(), v->size(), v->size())); |
116 | 0 | }; |
117 | 0 | switch (rnd() % 6) { |
118 | | // Mutate IHDR. |
119 | 0 | case 0: |
120 | 0 | m(ihdr_.data(), ihdr_.size(), ihdr_.size()); |
121 | 0 | break; |
122 | | // Mutate some other chunk. |
123 | 0 | case 1: |
124 | 0 | if (!chunks_.empty()) M(&chunks_[rnd() % chunks_.size()].v); |
125 | 0 | break; |
126 | | // Shuffle the chunks. |
127 | 0 | case 2: |
128 | 0 | std::shuffle(chunks_.begin(), chunks_.end(), rnd); |
129 | 0 | break; |
130 | | // Delete a random chunk. |
131 | 0 | case 3: |
132 | 0 | if (!chunks_.empty()) |
133 | 0 | chunks_.erase(chunks_.begin() + rnd() % chunks_.size()); |
134 | 0 | break; |
135 | | // Insert a random chunk with one of the known types, or a random type. |
136 | 0 | case 4: { |
137 | 0 | static const char *types[] = { |
138 | 0 | "IATx", "sTER", "hIST", "sPLT", "mkBF", "mkBS", "mkTS", "prVW", |
139 | 0 | "oFFs", "iDOT", "zTXt", "mkBT", "acTL", "iTXt", "sBIT", "tIME", |
140 | 0 | "iCCP", "vpAg", "tRNS", "cHRM", "PLTE", "bKGD", "gAMA", "sRGB", |
141 | 0 | "pHYs", "fdAT", "fcTL", "tEXt", "IDAT", |
142 | 0 | "pCAL", "sCAL", "eXIf", |
143 | 0 | "fUZz", // special chunk for extra fuzzing hints. |
144 | 0 | }; |
145 | 0 | static const size_t n_types = sizeof(types) / sizeof(types[0]); |
146 | 0 | uint32_t type = |
147 | 0 | (rnd() % 10 <= 8) ? Type(types[rnd() % n_types]) : (uint32_t)rnd(); |
148 | 0 | size_t len = rnd() % 256; |
149 | 0 | if (type == Type("fUZz")) |
150 | 0 | len = 16; |
151 | 0 | V v(len); |
152 | 0 | for (auto &b : v) b = rnd(); |
153 | 0 | size_t pos = rnd() % (chunks_.size() + 1); |
154 | 0 | chunks_.insert(chunks_.begin() + pos, {type, v}); |
155 | 0 | } break; |
156 | | // Any more interesting mutations with a PNG file? |
157 | 0 | case 5: { |
158 | 0 | auto it = std::find_if( |
159 | 0 | chunks_.begin(), chunks_.end(), |
160 | 0 | [](const Chunk &ch) { return ch.type == Type("fUZz"); }); |
161 | 0 | if (it != chunks_.end()) |
162 | 0 | m(it->v.data(), it->v.size(), it->v.size()); |
163 | 0 | } |
164 | |
|
165 | 0 | } |
166 | 0 | } |
167 | | |
168 | | // Takes a random chunk from p and inserts into *this. |
169 | 0 | void CrossOver(const PngMutator &p, unsigned int Seed) { |
170 | 0 | if (p.chunks_.empty()) return; |
171 | 0 | std::minstd_rand rnd(Seed); |
172 | 0 | size_t idx = rnd() % p.chunks_.size(); |
173 | 0 | auto &ch = p.chunks_[idx]; |
174 | 0 | size_t pos = rnd() % (chunks_.size() + 1); |
175 | 0 | chunks_.insert(chunks_.begin() + pos, ch); |
176 | 0 | } |
177 | | |
178 | | private: |
179 | 0 | void Append(V *to, const V &from) { |
180 | 0 | to->insert(to->end(), from.begin(), from.end()); |
181 | 0 | } |
182 | | |
183 | 0 | uint32_t Read4(std::istream &in) { |
184 | 0 | uint32_t res = 0; |
185 | 0 | in.read((char *)&res, sizeof(res)); |
186 | 0 | return res; |
187 | 0 | } |
188 | 0 | uint32_t ReadInteger(std::istream &in) { |
189 | 0 | return __builtin_bswap32(Read4(in)); |
190 | 0 | } |
191 | 0 | static uint32_t Type(const char *tagname) { |
192 | 0 | uint32_t res; |
193 | 0 | assert(strlen(tagname) == 4); |
194 | 0 | memcpy(&res, tagname, 4); |
195 | 0 | return res; |
196 | 0 | } |
197 | | |
198 | 0 | void WriteInt(std::ostream &out, uint32_t x) { |
199 | 0 | x = __builtin_bswap32(x); |
200 | 0 | out.write((char *)&x, sizeof(x)); |
201 | 0 | } |
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 | 0 | bool compress = false) { |
210 | 0 | V compressed; |
211 | 0 | const V *v = &chunk; |
212 | 0 | if (compress) { |
213 | 0 | compressed = Compress(chunk); |
214 | 0 | v = &compressed; |
215 | 0 | } |
216 | 0 | uint32_t len = v->size(); |
217 | 0 | uint32_t crc = crc32(0, (const unsigned char *)type, 4); |
218 | 0 | if (v->size()) |
219 | 0 | crc = crc32(crc, (const unsigned char *)v->data(), v->size()); |
220 | 0 | WriteInt(out, len); |
221 | 0 | out.write(type, 4); |
222 | 0 | out.write((const char*)v->data(), v->size()); |
223 | 0 | WriteInt(out, crc); |
224 | 0 | } |
225 | | |
226 | 0 | void WriteChunk(std::ostream &out, uint32_t type, const V &chunk) { |
227 | 0 | char type_s[5]; |
228 | 0 | memcpy(type_s, &type, 4); |
229 | 0 | type_s[4] = 0; |
230 | 0 | WriteChunk(out, type_s, chunk); |
231 | 0 | } |
232 | | |
233 | 0 | V Uncompress(const V &compressed) { |
234 | 0 | V v; |
235 | 0 | static const size_t kMaxBuffer = 1 << 28; |
236 | 0 | for (size_t sz = compressed.size() * 4; sz < kMaxBuffer; sz *= 2) { |
237 | 0 | v.resize(sz); |
238 | 0 | unsigned long len = sz; |
239 | 0 | auto res = |
240 | 0 | uncompress(v.data(), &len, compressed.data(), compressed.size()); |
241 | 0 | if (res == Z_BUF_ERROR) continue; |
242 | 0 | if (res != Z_OK) return {}; |
243 | 0 | v.resize(len); |
244 | 0 | break; |
245 | 0 | } |
246 | 0 | return v; |
247 | 0 | } |
248 | | |
249 | 0 | V Compress(const V &uncompressed) { |
250 | 0 | V v; |
251 | 0 | static const size_t kMaxBuffer = 1 << 28; |
252 | 0 | for (size_t sz = uncompressed.size(); sz < kMaxBuffer; sz *= 2) { |
253 | 0 | v.resize(sz); |
254 | 0 | unsigned long len = sz; |
255 | 0 | auto res = |
256 | 0 | compress(v.data(), &len, uncompressed.data(), uncompressed.size()); |
257 | 0 | if (res == Z_BUF_ERROR) continue; |
258 | 0 | if (res != Z_OK) return {}; |
259 | 0 | v.resize(len); |
260 | 0 | break; |
261 | 0 | } |
262 | 0 | return v; |
263 | 0 | } |
264 | | |
265 | 0 | void PrintHex(const V &v, size_t max_n) { |
266 | 0 | for (size_t i = 0; i < max_n && i < v.size(); i++) { |
267 | 0 | std::cerr << "0x" << std::hex << (unsigned)v[i] << " " << std::dec; |
268 | 0 | } |
269 | 0 | std::cerr << std::endl; |
270 | 0 | } |
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 |
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 | | |
293 | | extern "C" size_t LLVMFuzzerCustomMutator(uint8_t *Data, size_t Size, |
294 | 0 | size_t MaxSize, unsigned int Seed) { |
295 | 0 | std::string s(reinterpret_cast<const char*>(Data), Size); |
296 | 0 | std::stringstream in(s); |
297 | 0 | std::stringstream out; |
298 | 0 | PngMutator p(in); |
299 | 0 | p.Mutate(LLVMFuzzerMutate, Seed); |
300 | 0 | p.Serialize(out); |
301 | 0 | const auto &str = out.str(); |
302 | 0 | if (str.size() > MaxSize) return Size; |
303 | 0 | memcpy(Data, str.data(), str.size()); |
304 | 0 | return str.size(); |
305 | 0 | } |
306 | | |
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 | 0 | unsigned int Seed) { |
311 | 0 | std::stringstream in1( |
312 | 0 | std::string(reinterpret_cast<const char *>(Data1), Size1)); |
313 | 0 | std::stringstream in2( |
314 | 0 | std::string(reinterpret_cast<const char *>(Data2), Size2)); |
315 | 0 | PngMutator p1(in1); |
316 | 0 | PngMutator p2(in2); |
317 | 0 | p1.CrossOver(p2, Seed); |
318 | 0 | std::stringstream out; |
319 | 0 | p1.Serialize(out); |
320 | 0 | const auto &str = out.str(); |
321 | 0 | if (str.size() > MaxOutSize) return 0; |
322 | 0 | memcpy(Out, str.data(), str.size()); |
323 | 0 | return str.size(); |
324 | 0 | } |
325 | | |
326 | | #endif // PNG_MUTATOR_DEFINE_LIBFUZZER_CUSTOM_MUTATOR |