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 "SkImage.h"
14#include "SkImageEncoder.h"
15#include "SkMallocPixelRef.h"
16#include "SkOSFile.h"
17#include "SkOSPath.h"
18#include "SkPaint.h"
19#include "SkPath.h"
20#include "SkPicture.h"
21#include "SkPipe.h"
22#include "SkReadBuffer.h"
23#include "SkStream.h"
24#include "SkSurface.h"
25#include "SkTextBlob.h"
26
27#if SK_SUPPORT_GPU
28#include "SkSLCompiler.h"
29#endif
30
31#include <iostream>
32#include <signal.h>
33#include "sk_tool_utils.h"
34
35
36DEFINE_string2(bytes, b, "", "A path to a file or a directory. If a file, the "
37        "contents will be used as the fuzz bytes. If a directory, all files "
38        "in the directory will be used as fuzz bytes for the fuzzer, one at a "
39        "time.");
40DEFINE_string2(name, n, "", "If --type is 'api', fuzz the API with this name.");
41DEFINE_string2(dump, d, "", "If not empty, dump 'image*' or 'skp' types as a "
42        "PNG with this name.");
43DEFINE_bool2(verbose, v, false, "Print more information while fuzzing.");
44DEFINE_string2(type, t, "", "How to interpret --bytes, one of:\n"
45                            "animated_image_decode\n"
46                            "api\n"
47                            "color_deserialize\n"
48                            "filter_fuzz (equivalent to Chrome's filter_fuzz_stub)\n"
49                            "icc\n"
50                            "image_decode\n"
51                            "image_mode\n"
52                            "image_scale\n"
53                            "path_deserialize\n"
54                            "pipe\n"
55                            "region_deserialize\n"
56                            "region_set_path\n"
57                            "skp\n"
58                            "sksl2glsl\n"
59                            "textblob");
60
61static int fuzz_file(const char* path);
62static uint8_t calculate_option(SkData*);
63
64static void fuzz_api(sk_sp<SkData>);
65static void fuzz_color_deserialize(sk_sp<SkData>);
66static void fuzz_filter_fuzz(sk_sp<SkData>);
67static void fuzz_icc(sk_sp<SkData>);
68static void fuzz_img2(sk_sp<SkData>);
69static void fuzz_animated_img(sk_sp<SkData>);
70static void fuzz_img(sk_sp<SkData>, uint8_t, uint8_t);
71static void fuzz_path_deserialize(sk_sp<SkData>);
72static void fuzz_region_deserialize(sk_sp<SkData>);
73static void fuzz_region_set_path(sk_sp<SkData>);
74static void fuzz_skp(sk_sp<SkData>);
75static void fuzz_skpipe(sk_sp<SkData>);
76static void fuzz_textblob_deserialize(sk_sp<SkData>);
77
78#if SK_SUPPORT_GPU
79static void fuzz_sksl2glsl(sk_sp<SkData>);
80#endif
81
82int main(int argc, char** argv) {
83    SkCommandLineFlags::SetUsage("Usage: fuzz -t <type> -b <path/to/file> [-n api-to-fuzz]\n"
84                                 "--help lists the valid types\n");
85    SkCommandLineFlags::Parse(argc, argv);
86
87    const char* path = FLAGS_bytes.isEmpty() ? argv[0] : FLAGS_bytes[0];
88
89    if (!sk_isdir(path)) {
90        return fuzz_file(path);
91    }
92
93    SkOSFile::Iter it(path);
94    for (SkString file; it.next(&file); ) {
95        SkString p = SkOSPath::Join(path, file.c_str());
96        SkDebugf("Fuzzing %s\n", p.c_str());
97        int rv = fuzz_file(p.c_str());
98        if (rv != 0) {
99            return rv;
100        }
101    }
102    return 0;
103}
104
105static int fuzz_file(const char* path) {
106    sk_sp<SkData> bytes(SkData::MakeFromFileName(path));
107    if (!bytes) {
108        SkDebugf("Could not read %s\n", path);
109        return 1;
110    }
111
112    if (!FLAGS_type.isEmpty()) {
113        if (0 == strcmp("animated_image_decode", FLAGS_type[0])) {
114            fuzz_animated_img(bytes);
115            return 0;
116        }
117        if (0 == strcmp("api", FLAGS_type[0])) {
118            fuzz_api(bytes);
119            return 0;
120        }
121        if (0 == strcmp("color_deserialize", FLAGS_type[0])) {
122            fuzz_color_deserialize(bytes);
123            return 0;
124        }
125        if (0 == strcmp("icc", FLAGS_type[0])) {
126            fuzz_icc(bytes);
127            return 0;
128        }
129        if (0 == strcmp("image_decode", FLAGS_type[0])) {
130            fuzz_img2(bytes);
131            return 0;
132        }
133        if (0 == strcmp("image_scale", FLAGS_type[0])) {
134            uint8_t option = calculate_option(bytes.get());
135            fuzz_img(bytes, option, 0);
136            return 0;
137        }
138        if (0 == strcmp("image_mode", FLAGS_type[0])) {
139            uint8_t option = calculate_option(bytes.get());
140            fuzz_img(bytes, 0, option);
141            return 0;
142        }
143        if (0 == strcmp("path_deserialize", FLAGS_type[0])) {
144            fuzz_path_deserialize(bytes);
145            return 0;
146        }
147        if (0 == strcmp("region_deserialize", FLAGS_type[0])) {
148            fuzz_region_deserialize(bytes);
149            return 0;
150        }
151        if (0 == strcmp("region_set_path", FLAGS_type[0])) {
152            fuzz_region_set_path(bytes);
153            return 0;
154        }
155        if (0 == strcmp("pipe", FLAGS_type[0])) {
156            fuzz_skpipe(bytes);
157            return 0;
158        }
159        if (0 == strcmp("skp", FLAGS_type[0])) {
160            fuzz_skp(bytes);
161            return 0;
162        }
163        if (0 == strcmp("filter_fuzz", FLAGS_type[0])) {
164            fuzz_filter_fuzz(bytes);
165            return 0;
166        }
167        if (0 == strcmp("textblob", FLAGS_type[0])) {
168            fuzz_textblob_deserialize(bytes);
169            return 0;
170        }
171#if SK_SUPPORT_GPU
172        if (0 == strcmp("sksl2glsl", FLAGS_type[0])) {
173            fuzz_sksl2glsl(bytes);
174            return 0;
175        }
176#endif
177    }
178    SkCommandLineFlags::PrintUsage();
179    return 1;
180}
181
182// This adds up the first 1024 bytes and returns it as an 8 bit integer.  This allows afl-fuzz to
183// deterministically excercise different paths, or *options* (such as different scaling sizes or
184// different image modes) without needing to introduce a parameter.  This way we don't need a
185// image_scale1, image_scale2, image_scale4, etc fuzzer, we can just have a image_scale fuzzer.
186// Clients are expected to transform this number into a different range, e.g. with modulo (%).
187static uint8_t calculate_option(SkData* bytes) {
188    uint8_t total = 0;
189    const uint8_t* data = bytes->bytes();
190    for (size_t i = 0; i < 1024 && i < bytes->size(); i++) {
191        total += data[i];
192    }
193    return total;
194}
195
196static void fuzz_api(sk_sp<SkData> bytes) {
197    const char* name = FLAGS_name.isEmpty() ? "" : FLAGS_name[0];
198
199    for (auto r = sk_tools::Registry<Fuzzable>::Head(); r; r = r->next()) {
200        auto fuzzable = r->factory();
201        if (0 == strcmp(name, fuzzable.name)) {
202            SkDebugf("Fuzzing %s...\n", fuzzable.name);
203            Fuzz fuzz(std::move(bytes));
204            fuzzable.fn(&fuzz);
205            SkDebugf("[terminated] Success!\n");
206            return;
207        }
208    }
209
210    SkDebugf("When using --type api, please choose an API to fuzz with --name/-n:\n");
211    for (auto r = sk_tools::Registry<Fuzzable>::Head(); r; r = r->next()) {
212        auto fuzzable = r->factory();
213        SkDebugf("\t%s\n", fuzzable.name);
214    }
215}
216
217static void dump_png(SkBitmap bitmap) {
218    if (!FLAGS_dump.isEmpty()) {
219        sk_tool_utils::EncodeImageToFile(FLAGS_dump[0], bitmap, SkEncodedImageFormat::kPNG, 100);
220        SkDebugf("Dumped to %s\n", FLAGS_dump[0]);
221    }
222}
223
224void FuzzAnimatedImage(sk_sp<SkData> bytes);
225
226static void fuzz_animated_img(sk_sp<SkData> bytes) {
227    FuzzAnimatedImage(bytes);
228    SkDebugf("[terminated] Didn't crash while decoding/drawing animated image!\n");
229}
230
231void FuzzImage(sk_sp<SkData> bytes);
232
233static void fuzz_img2(sk_sp<SkData> bytes) {
234    FuzzImage(bytes);
235    SkDebugf("[terminated] Didn't crash while decoding/drawing image!\n");
236}
237
238static void fuzz_img(sk_sp<SkData> bytes, uint8_t scale, uint8_t mode) {
239    // We can scale 1x, 2x, 4x, 8x, 16x
240    scale = scale % 5;
241    float fscale = (float)pow(2.0f, scale);
242    SkDebugf("Scaling factor: %f\n", fscale);
243
244    // We have 5 different modes of decoding.
245    mode = mode % 5;
246    SkDebugf("Mode: %d\n", mode);
247
248    // This is mostly copied from DMSrcSink's CodecSrc::draw method.
249    SkDebugf("Decoding\n");
250    std::unique_ptr<SkCodec> codec(SkCodec::MakeFromData(bytes));
251    if (nullptr == codec.get()) {
252        SkDebugf("[terminated] Couldn't create codec.\n");
253        return;
254    }
255
256    SkImageInfo decodeInfo = codec->getInfo();
257    SkISize size = codec->getScaledDimensions(fscale);
258    decodeInfo = decodeInfo.makeWH(size.width(), size.height());
259
260    SkBitmap bitmap;
261    SkCodec::Options options;
262    options.fZeroInitialized = SkCodec::kYes_ZeroInitialized;
263
264    if (!bitmap.tryAllocPixelsFlags(decodeInfo, SkBitmap::kZeroPixels_AllocFlag)) {
265        SkDebugf("[terminated] Could not allocate memory.  Image might be too large (%d x %d)",
266                 decodeInfo.width(), decodeInfo.height());
267        return;
268    }
269
270    switch (mode) {
271        case 0: {//kCodecZeroInit_Mode, kCodec_Mode
272            switch (codec->getPixels(decodeInfo, bitmap.getPixels(), bitmap.rowBytes(), &options)) {
273                case SkCodec::kSuccess:
274                    SkDebugf("[terminated] Success!\n");
275                    break;
276                case SkCodec::kIncompleteInput:
277                    SkDebugf("[terminated] Partial Success\n");
278                    break;
279                case SkCodec::kErrorInInput:
280                    SkDebugf("[terminated] Partial Success with error\n");
281                    break;
282                case SkCodec::kInvalidConversion:
283                    SkDebugf("Incompatible colortype conversion\n");
284                    // Crash to allow afl-fuzz to know this was a bug.
285                    raise(SIGSEGV);
286                default:
287                    SkDebugf("[terminated] Couldn't getPixels.\n");
288                    return;
289            }
290            break;
291        }
292        case 1: {//kScanline_Mode
293            if (SkCodec::kSuccess != codec->startScanlineDecode(decodeInfo)) {
294                SkDebugf("[terminated] Could not start scanline decoder\n");
295                return;
296            }
297
298            void* dst = bitmap.getAddr(0, 0);
299            size_t rowBytes = bitmap.rowBytes();
300            uint32_t height = decodeInfo.height();
301            switch (codec->getScanlineOrder()) {
302                case SkCodec::kTopDown_SkScanlineOrder:
303                case SkCodec::kBottomUp_SkScanlineOrder:
304                    // We do not need to check the return value.  On an incomplete
305                    // image, memory will be filled with a default value.
306                    codec->getScanlines(dst, height, rowBytes);
307                    break;
308            }
309            SkDebugf("[terminated] Success!\n");
310            break;
311        }
312        case 2: { //kStripe_Mode
313            const int height = decodeInfo.height();
314            // This value is chosen arbitrarily.  We exercise more cases by choosing a value that
315            // does not align with image blocks.
316            const int stripeHeight = 37;
317            const int numStripes = (height + stripeHeight - 1) / stripeHeight;
318
319            // Decode odd stripes
320            if (SkCodec::kSuccess != codec->startScanlineDecode(decodeInfo)
321                    || SkCodec::kTopDown_SkScanlineOrder != codec->getScanlineOrder()) {
322                // This mode was designed to test the new skip scanlines API in libjpeg-turbo.
323                // Jpegs have kTopDown_SkScanlineOrder, and at this time, it is not interesting
324                // to run this test for image types that do not have this scanline ordering.
325                SkDebugf("[terminated] Could not start top-down scanline decoder\n");
326                return;
327            }
328
329            for (int i = 0; i < numStripes; i += 2) {
330                // Skip a stripe
331                const int linesToSkip = SkTMin(stripeHeight, height - i * stripeHeight);
332                codec->skipScanlines(linesToSkip);
333
334                // Read a stripe
335                const int startY = (i + 1) * stripeHeight;
336                const int linesToRead = SkTMin(stripeHeight, height - startY);
337                if (linesToRead > 0) {
338                    codec->getScanlines(bitmap.getAddr(0, startY), linesToRead, bitmap.rowBytes());
339                }
340            }
341
342            // Decode even stripes
343            const SkCodec::Result startResult = codec->startScanlineDecode(decodeInfo);
344            if (SkCodec::kSuccess != startResult) {
345                SkDebugf("[terminated] Failed to restart scanline decoder with same parameters.\n");
346                return;
347            }
348            for (int i = 0; i < numStripes; i += 2) {
349                // Read a stripe
350                const int startY = i * stripeHeight;
351                const int linesToRead = SkTMin(stripeHeight, height - startY);
352                codec->getScanlines(bitmap.getAddr(0, startY), linesToRead, bitmap.rowBytes());
353
354                // Skip a stripe
355                const int linesToSkip = SkTMin(stripeHeight, height - (i + 1) * stripeHeight);
356                if (linesToSkip > 0) {
357                    codec->skipScanlines(linesToSkip);
358                }
359            }
360            SkDebugf("[terminated] Success!\n");
361            break;
362        }
363        case 3: { //kSubset_Mode
364            // Arbitrarily choose a divisor.
365            int divisor = 2;
366            // Total width/height of the image.
367            const int W = codec->getInfo().width();
368            const int H = codec->getInfo().height();
369            if (divisor > W || divisor > H) {
370                SkDebugf("[terminated] Cannot codec subset: divisor %d is too big "
371                         "with dimensions (%d x %d)\n", divisor, W, H);
372                return;
373            }
374            // subset dimensions
375            // SkWebpCodec, the only one that supports subsets, requires even top/left boundaries.
376            const int w = SkAlign2(W / divisor);
377            const int h = SkAlign2(H / divisor);
378            SkIRect subset;
379            SkCodec::Options opts;
380            opts.fSubset = &subset;
381            SkBitmap subsetBm;
382            // We will reuse pixel memory from bitmap.
383            void* pixels = bitmap.getPixels();
384            // Keep track of left and top (for drawing subsetBm into canvas). We could use
385            // fscale * x and fscale * y, but we want integers such that the next subset will start
386            // where the last one ended. So we'll add decodeInfo.width() and height().
387            int left = 0;
388            for (int x = 0; x < W; x += w) {
389                int top = 0;
390                for (int y = 0; y < H; y+= h) {
391                    // Do not make the subset go off the edge of the image.
392                    const int preScaleW = SkTMin(w, W - x);
393                    const int preScaleH = SkTMin(h, H - y);
394                    subset.setXYWH(x, y, preScaleW, preScaleH);
395                    // And fscale
396                    // FIXME: Should we have a version of getScaledDimensions that takes a subset
397                    // into account?
398                    decodeInfo = decodeInfo.makeWH(
399                            SkTMax(1, SkScalarRoundToInt(preScaleW * fscale)),
400                            SkTMax(1, SkScalarRoundToInt(preScaleH * fscale)));
401                    size_t rowBytes = decodeInfo.minRowBytes();
402                    if (!subsetBm.installPixels(decodeInfo, pixels, rowBytes)) {
403                        SkDebugf("[terminated] Could not install pixels.\n");
404                        return;
405                    }
406                    const SkCodec::Result result = codec->getPixels(decodeInfo, pixels, rowBytes,
407                            &opts);
408                    switch (result) {
409                        case SkCodec::kSuccess:
410                        case SkCodec::kIncompleteInput:
411                        case SkCodec::kErrorInInput:
412                            SkDebugf("okay\n");
413                            break;
414                        case SkCodec::kInvalidConversion:
415                            if (0 == (x|y)) {
416                                // First subset is okay to return unimplemented.
417                                SkDebugf("[terminated] Incompatible colortype conversion\n");
418                                return;
419                            }
420                            // If the first subset succeeded, a later one should not fail.
421                            // fall through to failure
422                        case SkCodec::kUnimplemented:
423                            if (0 == (x|y)) {
424                                // First subset is okay to return unimplemented.
425                                SkDebugf("[terminated] subset codec not supported\n");
426                                return;
427                            }
428                            // If the first subset succeeded, why would a later one fail?
429                            // fall through to failure
430                        default:
431                            SkDebugf("[terminated] subset codec failed to decode (%d, %d, %d, %d) "
432                                                  "with dimensions (%d x %d)\t error %d\n",
433                                                  x, y, decodeInfo.width(), decodeInfo.height(),
434                                                  W, H, result);
435                            return;
436                    }
437                    // translate by the scaled height.
438                    top += decodeInfo.height();
439                }
440                // translate by the scaled width.
441                left += decodeInfo.width();
442            }
443            SkDebugf("[terminated] Success!\n");
444            break;
445        }
446        case 4: { //kAnimated_Mode
447            std::vector<SkCodec::FrameInfo> frameInfos = codec->getFrameInfo();
448            if (frameInfos.size() == 0) {
449                SkDebugf("[terminated] Not an animated image\n");
450                break;
451            }
452
453            for (size_t i = 0; i < frameInfos.size(); i++) {
454                options.fFrameIndex = i;
455                auto result = codec->startIncrementalDecode(decodeInfo, bitmap.getPixels(),
456                        bitmap.rowBytes(), &options);
457                if (SkCodec::kSuccess != result) {
458                    SkDebugf("[terminated] failed to start incremental decode "
459                             "in frame %d with error %d\n", i, result);
460                    return;
461                }
462
463                result = codec->incrementalDecode();
464                if (result == SkCodec::kIncompleteInput || result == SkCodec::kErrorInInput) {
465                    SkDebugf("okay\n");
466                    // Frames beyond this one will not decode.
467                    break;
468                }
469                if (result == SkCodec::kSuccess) {
470                    SkDebugf("okay - decoded frame %d\n", i);
471                } else {
472                    SkDebugf("[terminated] incremental decode failed with "
473                             "error %d\n", result);
474                    return;
475                }
476            }
477            SkDebugf("[terminated] Success!\n");
478            break;
479        }
480        default:
481            SkDebugf("[terminated] Mode not implemented yet\n");
482    }
483
484    dump_png(bitmap);
485}
486
487static void fuzz_skp(sk_sp<SkData> bytes) {
488    SkReadBuffer buf(bytes->data(), bytes->size());
489    SkDebugf("Decoding\n");
490    sk_sp<SkPicture> pic(SkPicture::MakeFromBuffer(buf));
491    if (!pic) {
492        SkDebugf("[terminated] Couldn't decode as a picture.\n");
493        return;
494    }
495    SkDebugf("Rendering\n");
496    SkBitmap bitmap;
497    if (!FLAGS_dump.isEmpty()) {
498        SkIRect size = pic->cullRect().roundOut();
499        bitmap.allocN32Pixels(size.width(), size.height());
500    }
501    SkCanvas canvas(bitmap);
502    canvas.drawPicture(pic);
503    SkDebugf("[terminated] Success! Decoded and rendered an SkPicture!\n");
504    dump_png(bitmap);
505}
506
507static void fuzz_skpipe(sk_sp<SkData> bytes) {
508    SkPipeDeserializer d;
509    SkDebugf("Decoding\n");
510    sk_sp<SkPicture> pic(d.readPicture(bytes.get()));
511    if (!pic) {
512        SkDebugf("[terminated] Couldn't decode picture via SkPipe.\n");
513        return;
514    }
515    SkDebugf("Rendering\n");
516    SkBitmap bitmap;
517    SkCanvas canvas(bitmap);
518    canvas.drawPicture(pic);
519    SkDebugf("[terminated] Success! Decoded and rendered an SkPicture from SkPipe!\n");
520}
521
522static void fuzz_icc(sk_sp<SkData> bytes) {
523    sk_sp<SkColorSpace> space(SkColorSpace::MakeICC(bytes->data(), bytes->size()));
524    if (!space) {
525        SkDebugf("[terminated] Couldn't decode ICC.\n");
526        return;
527    }
528    SkDebugf("[terminated] Success! Decoded ICC.\n");
529}
530
531static void fuzz_color_deserialize(sk_sp<SkData> bytes) {
532    sk_sp<SkColorSpace> space(SkColorSpace::Deserialize(bytes->data(), bytes->size()));
533    if (!space) {
534        SkDebugf("[terminated] Couldn't deserialize Colorspace.\n");
535        return;
536    }
537    SkDebugf("[terminated] Success! deserialized Colorspace.\n");
538}
539
540void FuzzPathDeserialize(SkReadBuffer& buf);
541
542static void fuzz_path_deserialize(sk_sp<SkData> bytes) {
543    SkReadBuffer buf(bytes->data(), bytes->size());
544    FuzzPathDeserialize(buf);
545    SkDebugf("[terminated] path_deserialize didn't crash!\n");
546}
547
548bool FuzzRegionDeserialize(sk_sp<SkData> bytes);
549
550static void fuzz_region_deserialize(sk_sp<SkData> bytes) {
551    if (!FuzzRegionDeserialize(bytes)) {
552        SkDebugf("[terminated] Couldn't initialize SkRegion.\n");
553        return;
554    }
555    SkDebugf("[terminated] Success! Initialized SkRegion.\n");
556}
557
558void FuzzTextBlobDeserialize(SkReadBuffer& buf);
559
560static void fuzz_textblob_deserialize(sk_sp<SkData> bytes) {
561    SkReadBuffer buf(bytes->data(), bytes->size());
562    FuzzTextBlobDeserialize(buf);
563    SkDebugf("[terminated] textblob didn't crash!\n");
564}
565
566void FuzzRegionSetPath(Fuzz* fuzz);
567
568static void fuzz_region_set_path(sk_sp<SkData> bytes) {
569    Fuzz fuzz(bytes);
570    FuzzRegionSetPath(&fuzz);
571    SkDebugf("[terminated] region_set_path didn't crash!\n");
572}
573
574void FuzzImageFilterDeserialize(sk_sp<SkData> bytes);
575
576static void fuzz_filter_fuzz(sk_sp<SkData> bytes) {
577    FuzzImageFilterDeserialize(bytes);
578    SkDebugf("[terminated] filter_fuzz didn't crash!\n");
579}
580
581#if SK_SUPPORT_GPU
582static void fuzz_sksl2glsl(sk_sp<SkData> bytes) {
583    SkSL::Compiler compiler;
584    SkSL::String output;
585    SkSL::Program::Settings settings;
586    sk_sp<GrShaderCaps> caps = SkSL::ShaderCapsFactory::Default();
587    settings.fCaps = caps.get();
588    std::unique_ptr<SkSL::Program> program = compiler.convertProgram(SkSL::Program::kFragment_Kind,
589                                                          SkSL::String((const char*) bytes->data()),
590                                                          settings);
591    if (!program || !compiler.toGLSL(*program, &output)) {
592        SkDebugf("[terminated] Couldn't compile input.\n");
593        return;
594    }
595    SkDebugf("[terminated] Success! Compiled input.\n");
596}
597#endif
598