• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright 2016 Google Inc.
3  *
4  * Use of this source code is governed by a BSD-style license that can be
5  * found in the LICENSE file.
6  */
7 
8 #include "Fuzz.h"
9 #include "SkCanvas.h"
10 #include "SkCodec.h"
11 #include "SkCommandLineFlags.h"
12 #include "SkData.h"
13 #include "SkForceLinking.h"
14 #include "SkImage.h"
15 #include "SkImageEncoder.h"
16 #include "SkMallocPixelRef.h"
17 #include "SkPicture.h"
18 #include "SkStream.h"
19 
20 #include <cmath>
21 #include <signal.h>
22 #include <stdlib.h>
23 
24 // TODO(kjlubick): Remove once http://crrev.com/1671193002 lands
25 __SK_FORCE_IMAGE_DECODER_LINKING;
26 
27 DEFINE_string2(bytes, b, "", "A path to a file.  This can be the fuzz bytes or a binary to parse.");
28 DEFINE_string2(name, n, "", "If --type is 'api', fuzz the API with this name.");
29 
30 DEFINE_string2(type, t, "api", "How to interpret --bytes, either 'image_scale', 'image_mode', 'skp', or 'api'.");
31 DEFINE_string2(dump, d, "", "If not empty, dump 'image*' or 'skp' types as a PNG with this name.");
32 
printUsage(const char * name)33 static int printUsage(const char* name) {
34     SkDebugf("Usage: %s -t <type> -b <path/to/file> [-n api-to-fuzz]\n", name);
35     return 1;
36 }
37 static uint8_t calculate_option(SkData*);
38 
39 static int fuzz_api(SkData*);
40 static int fuzz_img(SkData*, uint8_t, uint8_t);
41 static int fuzz_skp(SkData*);
42 
main(int argc,char ** argv)43 int main(int argc, char** argv) {
44     SkCommandLineFlags::Parse(argc, argv);
45 
46     const char* path = FLAGS_bytes.isEmpty() ? argv[0] : FLAGS_bytes[0];
47     SkAutoTUnref<SkData> bytes(SkData::NewFromFileName(path));
48     if (!bytes) {
49         SkDebugf("Could not read %s\n", path);
50         return 2;
51     }
52 
53     uint8_t option = calculate_option(bytes);
54 
55     if (!FLAGS_type.isEmpty()) {
56         switch (FLAGS_type[0][0]) {
57             case 'a': return fuzz_api(bytes);
58 
59             case 'i':
60                 // We only allow one degree of freedom to avoid a search space explosion for afl-fuzz.
61                 if (FLAGS_type[0][6] == 's') { // image_scale
62                     return fuzz_img(bytes, option, 0);
63                 }
64                 // image_mode
65                 return fuzz_img(bytes, 0, option);
66             case 's': return fuzz_skp(bytes);
67         }
68     }
69     return printUsage(argv[0]);
70 }
71 
72 // This adds up the first 1024 bytes and returns it as an 8 bit integer.  This allows afl-fuzz to
73 // deterministically excercise different paths, or *options* (such as different scaling sizes or
74 // different image modes) without needing to introduce a parameter.  This way we don't need a
75 // image_scale1, image_scale2, image_scale4, etc fuzzer, we can just have a image_scale fuzzer.
76 // Clients are expected to transform this number into a different range, e.g. with modulo (%).
calculate_option(SkData * bytes)77 static uint8_t calculate_option(SkData* bytes) {
78     uint8_t total = 0;
79     const uint8_t* data = bytes->bytes();
80     for (size_t i = 0; i < 1024 && i < bytes->size(); i++) {
81         total += data[i];
82     }
83     return total;
84 }
85 
fuzz_api(SkData * bytes)86 int fuzz_api(SkData* bytes) {
87     const char* name = FLAGS_name.isEmpty() ? "" : FLAGS_name[0];
88 
89     for (auto r = SkTRegistry<Fuzzable>::Head(); r; r = r->next()) {
90         auto fuzzable = r->factory();
91         if (0 == strcmp(name, fuzzable.name)) {
92             SkDebugf("Fuzzing %s...\n", fuzzable.name);
93             Fuzz fuzz(bytes);
94             fuzzable.fn(&fuzz);
95             SkDebugf("[terminated] Success!\n");
96             return 0;
97         }
98     }
99 
100     SkDebugf("When using --type api, please choose an API to fuzz with --name/-n:\n");
101     for (auto r = SkTRegistry<Fuzzable>::Head(); r; r = r->next()) {
102         auto fuzzable = r->factory();
103         SkDebugf("\t%s\n", fuzzable.name);
104     }
105     return 1;
106 }
107 
dump_png(SkBitmap bitmap)108 static void dump_png(SkBitmap bitmap) {
109     if (!FLAGS_dump.isEmpty()) {
110         SkImageEncoder::EncodeFile(FLAGS_dump[0], bitmap, SkImageEncoder::kPNG_Type, 100);
111         SkDebugf("Dumped to %s\n", FLAGS_dump[0]);
112     }
113 }
114 
fuzz_img(SkData * bytes,uint8_t scale,uint8_t mode)115 int fuzz_img(SkData* bytes, uint8_t scale, uint8_t mode) {
116     // We can scale 1x, 2x, 4x, 8x, 16x
117     scale = scale % 5;
118     float fscale = (float)pow(2.0f, scale);
119     SkDebugf("Scaling factor: %f\n", fscale);
120 
121     // We have 4 different modes of decoding, just like DM.
122     mode = mode % 4;
123     SkDebugf("Mode: %d\n", mode);
124 
125     // This is mostly copied from DMSrcSink's CodecSrc::draw method.
126     SkDebugf("Decoding\n");
127     SkAutoTDelete<SkCodec> codec(SkCodec::NewFromData(bytes));
128     if (nullptr == codec.get()) {
129         SkDebugf("[terminated] Couldn't create codec.\n");
130         return 3;
131     }
132 
133     SkImageInfo decodeInfo = codec->getInfo();
134 
135     SkISize size = codec->getScaledDimensions(fscale);
136     decodeInfo = decodeInfo.makeWH(size.width(), size.height());
137 
138     // Construct a color table for the decode if necessary
139     SkAutoTUnref<SkColorTable> colorTable(nullptr);
140     SkPMColor* colorPtr = nullptr;
141     int* colorCountPtr = nullptr;
142     int maxColors = 256;
143     if (kIndex_8_SkColorType == decodeInfo.colorType()) {
144         SkPMColor colors[256];
145         colorTable.reset(new SkColorTable(colors, maxColors));
146         colorPtr = const_cast<SkPMColor*>(colorTable->readColors());
147         colorCountPtr = &maxColors;
148     }
149 
150     SkBitmap bitmap;
151     SkMallocPixelRef::ZeroedPRFactory zeroFactory;
152     SkCodec::Options options;
153     options.fZeroInitialized = SkCodec::kYes_ZeroInitialized;
154 
155     if (!bitmap.tryAllocPixels(decodeInfo, &zeroFactory, colorTable.get())) {
156         SkDebugf("[terminated] Could not allocate memory.  Image might be too large (%d x %d)",
157                  decodeInfo.width(), decodeInfo.height());
158         return 4;
159     }
160 
161     switch (mode) {
162         case 0: {//kCodecZeroInit_Mode, kCodec_Mode
163             switch (codec->getPixels(decodeInfo, bitmap.getPixels(), bitmap.rowBytes(), &options,
164                                      colorPtr, colorCountPtr)) {
165                 case SkCodec::kSuccess:
166                     SkDebugf("[terminated] Success!\n");
167                     break;
168                 case SkCodec::kIncompleteInput:
169                     SkDebugf("[terminated] Partial Success\n");
170                     break;
171                 case SkCodec::kInvalidConversion:
172                     SkDebugf("Incompatible colortype conversion\n");
173                     // Crash to allow afl-fuzz to know this was a bug.
174                     raise(SIGSEGV);
175                 default:
176                     SkDebugf("[terminated] Couldn't getPixels.\n");
177                     return 6;
178             }
179             break;
180         }
181         case 1: {//kScanline_Mode
182             if (SkCodec::kSuccess != codec->startScanlineDecode(decodeInfo, NULL, colorPtr,
183                                                                 colorCountPtr)) {
184                     SkDebugf("[terminated] Could not start scanline decoder\n");
185                     return 7;
186                 }
187 
188             void* dst = bitmap.getAddr(0, 0);
189             size_t rowBytes = bitmap.rowBytes();
190             uint32_t height = decodeInfo.height();
191             switch (codec->getScanlineOrder()) {
192                 case SkCodec::kTopDown_SkScanlineOrder:
193                 case SkCodec::kBottomUp_SkScanlineOrder:
194                 case SkCodec::kNone_SkScanlineOrder:
195                     // We do not need to check the return value.  On an incomplete
196                     // image, memory will be filled with a default value.
197                     codec->getScanlines(dst, height, rowBytes);
198                     break;
199                 case SkCodec::kOutOfOrder_SkScanlineOrder: {
200                     for (int y = 0; y < decodeInfo.height(); y++) {
201                         int dstY = codec->outputScanline(y);
202                         void* dstPtr = bitmap.getAddr(0, dstY);
203                         // We complete the loop, even if this call begins to fail
204                         // due to an incomplete image.  This ensures any uninitialized
205                         // memory will be filled with the proper value.
206                         codec->getScanlines(dstPtr, 1, bitmap.rowBytes());
207                     }
208                     break;
209                 }
210             }
211             SkDebugf("[terminated] Success!\n");
212             break;
213         }
214         case 2: { //kStripe_Mode
215             const int height = decodeInfo.height();
216             // This value is chosen arbitrarily.  We exercise more cases by choosing a value that
217             // does not align with image blocks.
218             const int stripeHeight = 37;
219             const int numStripes = (height + stripeHeight - 1) / stripeHeight;
220 
221             // Decode odd stripes
222             if (SkCodec::kSuccess != codec->startScanlineDecode(decodeInfo, NULL, colorPtr,
223                                                                 colorCountPtr)
224                     || SkCodec::kTopDown_SkScanlineOrder != codec->getScanlineOrder()) {
225                 // This mode was designed to test the new skip scanlines API in libjpeg-turbo.
226                 // Jpegs have kTopDown_SkScanlineOrder, and at this time, it is not interesting
227                 // to run this test for image types that do not have this scanline ordering.
228                 SkDebugf("[terminated] Could not start top-down scanline decoder\n");
229                 return 8;
230             }
231 
232             for (int i = 0; i < numStripes; i += 2) {
233                 // Skip a stripe
234                 const int linesToSkip = SkTMin(stripeHeight, height - i * stripeHeight);
235                 codec->skipScanlines(linesToSkip);
236 
237                 // Read a stripe
238                 const int startY = (i + 1) * stripeHeight;
239                 const int linesToRead = SkTMin(stripeHeight, height - startY);
240                 if (linesToRead > 0) {
241                     codec->getScanlines(bitmap.getAddr(0, startY), linesToRead, bitmap.rowBytes());
242                 }
243             }
244 
245             // Decode even stripes
246             const SkCodec::Result startResult = codec->startScanlineDecode(decodeInfo, nullptr,
247                     colorPtr, colorCountPtr);
248             if (SkCodec::kSuccess != startResult) {
249                 SkDebugf("[terminated] Failed to restart scanline decoder with same parameters.\n");
250                 return 9;
251             }
252             for (int i = 0; i < numStripes; i += 2) {
253                 // Read a stripe
254                 const int startY = i * stripeHeight;
255                 const int linesToRead = SkTMin(stripeHeight, height - startY);
256                 codec->getScanlines(bitmap.getAddr(0, startY), linesToRead, bitmap.rowBytes());
257 
258                 // Skip a stripe
259                 const int linesToSkip = SkTMin(stripeHeight, height - (i + 1) * stripeHeight);
260                 if (linesToSkip > 0) {
261                     codec->skipScanlines(linesToSkip);
262                 }
263             }
264             SkDebugf("[terminated] Success!\n");
265             break;
266         }
267         case 3: { //kSubset_Mode
268             // Arbitrarily choose a divisor.
269             int divisor = 2;
270             // Total width/height of the image.
271             const int W = codec->getInfo().width();
272             const int H = codec->getInfo().height();
273             if (divisor > W || divisor > H) {
274                 SkDebugf("[terminated] Cannot codec subset: divisor %d is too big "
275                          "with dimensions (%d x %d)\n", divisor, W, H);
276                 return 10;
277             }
278             // subset dimensions
279             // SkWebpCodec, the only one that supports subsets, requires even top/left boundaries.
280             const int w = SkAlign2(W / divisor);
281             const int h = SkAlign2(H / divisor);
282             SkIRect subset;
283             SkCodec::Options opts;
284             opts.fSubset = &subset;
285             SkBitmap subsetBm;
286             // We will reuse pixel memory from bitmap.
287             void* pixels = bitmap.getPixels();
288             // Keep track of left and top (for drawing subsetBm into canvas). We could use
289             // fscale * x and fscale * y, but we want integers such that the next subset will start
290             // where the last one ended. So we'll add decodeInfo.width() and height().
291             int left = 0;
292             for (int x = 0; x < W; x += w) {
293                 int top = 0;
294                 for (int y = 0; y < H; y+= h) {
295                     // Do not make the subset go off the edge of the image.
296                     const int preScaleW = SkTMin(w, W - x);
297                     const int preScaleH = SkTMin(h, H - y);
298                     subset.setXYWH(x, y, preScaleW, preScaleH);
299                     // And fscale
300                     // FIXME: Should we have a version of getScaledDimensions that takes a subset
301                     // into account?
302                     decodeInfo = decodeInfo.makeWH(
303                             SkTMax(1, SkScalarRoundToInt(preScaleW * fscale)),
304                             SkTMax(1, SkScalarRoundToInt(preScaleH * fscale)));
305                     size_t rowBytes = decodeInfo.minRowBytes();
306                     if (!subsetBm.installPixels(decodeInfo, pixels, rowBytes, colorTable.get(),
307                                                 nullptr, nullptr)) {
308                         SkDebugf("[terminated] Could not install pixels.\n");
309                         return 11;
310                     }
311                     const SkCodec::Result result = codec->getPixels(decodeInfo, pixels, rowBytes,
312                             &opts, colorPtr, colorCountPtr);
313                     switch (result) {
314                         case SkCodec::kSuccess:
315                         case SkCodec::kIncompleteInput:
316                             SkDebugf("okay\n");
317                             break;
318                         case SkCodec::kInvalidConversion:
319                             if (0 == (x|y)) {
320                                 // First subset is okay to return unimplemented.
321                                 SkDebugf("[terminated] Incompatible colortype conversion\n");
322                                 return 12;
323                             }
324                             // If the first subset succeeded, a later one should not fail.
325                             // fall through to failure
326                         case SkCodec::kUnimplemented:
327                             if (0 == (x|y)) {
328                                 // First subset is okay to return unimplemented.
329                                 SkDebugf("[terminated] subset codec not supported\n");
330                                 return 13;
331                             }
332                             // If the first subset succeeded, why would a later one fail?
333                             // fall through to failure
334                         default:
335                             SkDebugf("[terminated] subset codec failed to decode (%d, %d, %d, %d) "
336                                                   "with dimensions (%d x %d)\t error %d\n",
337                                                   x, y, decodeInfo.width(), decodeInfo.height(),
338                                                   W, H, result);
339                             return 14;
340                     }
341                     // translate by the scaled height.
342                     top += decodeInfo.height();
343                 }
344                 // translate by the scaled width.
345                 left += decodeInfo.width();
346             }
347             SkDebugf("[terminated] Success!\n");
348             break;
349         }
350         default:
351             SkDebugf("[terminated] Mode not implemented yet\n");
352     }
353 
354     dump_png(bitmap);
355     return 0;
356 }
357 
fuzz_skp(SkData * bytes)358 int fuzz_skp(SkData* bytes) {
359     SkMemoryStream stream(bytes);
360     SkDebugf("Decoding\n");
361     SkAutoTUnref<SkPicture> pic(SkPicture::CreateFromStream(&stream));
362     if (!pic) {
363         SkDebugf("[terminated] Couldn't decode as a picture.\n");
364         return 3;
365     }
366     SkDebugf("Rendering\n");
367     SkBitmap bitmap;
368     if (!FLAGS_dump.isEmpty()) {
369         SkIRect size = pic->cullRect().roundOut();
370         bitmap.allocN32Pixels(size.width(), size.height());
371     }
372     SkCanvas canvas(bitmap);
373     canvas.drawPicture(pic);
374     SkDebugf("[terminated] Success! Decoded and rendered an SkPicture!\n");
375     dump_png(bitmap);
376     return 0;
377 }
378 
Fuzz(SkData * bytes)379 Fuzz::Fuzz(SkData* bytes) : fBytes(SkSafeRef(bytes)), fNextByte(0) {}
380 
signalBug()381 void Fuzz::signalBug   () { raise(SIGSEGV); }
signalBoring()382 void Fuzz::signalBoring() { exit(0); }
383 
384 template <typename T>
nextT()385 T Fuzz::nextT() {
386     if (fNextByte + sizeof(T) > fBytes->size()) {
387         this->signalBoring();
388     }
389 
390     T val;
391     memcpy(&val, fBytes->bytes() + fNextByte, sizeof(T));
392     fNextByte += sizeof(T);
393     return val;
394 }
395 
nextB()396 uint8_t  Fuzz::nextB() { return this->nextT<uint8_t >(); }
nextBool()397 bool  Fuzz::nextBool() { return nextB()&1; }
nextU()398 uint32_t Fuzz::nextU() { return this->nextT<uint32_t>(); }
nextF()399 float    Fuzz::nextF() { return this->nextT<float   >(); }
400 
401 
nextRangeU(uint32_t min,uint32_t max)402 uint32_t Fuzz::nextRangeU(uint32_t min, uint32_t max) {
403     if (min > max) {
404         SkDebugf("Check mins and maxes (%d, %d)\n", min, max);
405         this->signalBoring();
406     }
407     uint32_t range = max - min + 1;
408     if (0 == range) {
409         return this->nextU();
410     } else {
411         return min + this->nextU() % range;
412     }
413 }
nextRangeF(float min,float max)414 float Fuzz::nextRangeF(float min, float max) {
415     if (min > max) {
416         SkDebugf("Check mins and maxes (%f, %f)\n", min, max);
417         this->signalBoring();
418     }
419     float f = std::abs(this->nextF());
420     if (!std::isnormal(f) && f != 0.0) {
421         this->signalBoring();
422     }
423     return min + fmod(f, (max - min + 1));
424 }
425