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 = ⊂ 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