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