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/Fuzz.h"
9 #include "include/codec/SkCodec.h"
10 #include "include/core/SkCanvas.h"
11 #include "include/core/SkData.h"
12 #include "include/core/SkImage.h"
13 #include "include/core/SkImageEncoder.h"
14 #include "include/core/SkMallocPixelRef.h"
15 #include "include/core/SkPaint.h"
16 #include "include/core/SkPath.h"
17 #include "include/core/SkStream.h"
18 #include "include/core/SkSurface.h"
19 #include "include/core/SkTextBlob.h"
20 #include "src/core/SkFontMgrPriv.h"
21 #include "src/core/SkOSFile.h"
22 #include "src/core/SkReadBuffer.h"
23 #include "src/utils/SkOSPath.h"
24 #include "tools/ToolUtils.h"
25 #include "tools/flags/CommandLineFlags.h"
26 #include "tools/fonts/TestFontMgr.h"
27
28 #include <iostream>
29 #include <map>
30 #include <regex>
31 #include <signal.h>
32
33 static DEFINE_string2(bytes, b, "", "A path to a file or a directory. If a file, the "
34 "contents will be used as the fuzz bytes. If a directory, all files "
35 "in the directory will be used as fuzz bytes for the fuzzer, one at a "
36 "time.");
37 static DEFINE_string2(name, n, "", "If --type is 'api', fuzz the API with this name.");
38 static DEFINE_string2(dump, d, "", "If not empty, dump 'image*' or 'skp' types as a "
39 "PNG with this name.");
40 static DEFINE_int(loops, 1, "Run the fuzzer on each input this many times.");
41 DEFINE_bool2(verbose, v, false, "Print more information while fuzzing.");
42
43 // This cannot be inlined in DEFINE_string2 due to interleaved ifdefs
44 static constexpr char g_type_message[] = "How to interpret --bytes, one of:\n"
45 "android_codec\n"
46 "animated_image_decode\n"
47 "api\n"
48 "color_deserialize\n"
49 "filter_fuzz (equivalent to Chrome's filter_fuzz_stub)\n"
50 "image_decode\n"
51 "image_decode_incremental\n"
52 "image_mode\n"
53 "image_scale\n"
54 "json\n"
55 "path_deserialize\n"
56 "region_deserialize\n"
57 "region_set_path\n"
58 "skdescriptor_deserialize\n"
59 "skp\n"
60 "skruntimeeffect\n"
61 "sksl2glsl\n"
62 "svg_dom\n"
63 "sksl2metal\n"
64 "sksl2pipeline\n"
65 "sksl2spirv\n"
66 #if defined(SK_ENABLE_SKOTTIE)
67 "skottie_json\n"
68 #endif
69 "textblob";
70
71 static DEFINE_string2(type, t, "", g_type_message);
72
73 static int fuzz_file(SkString path, SkString type);
74 static uint8_t calculate_option(SkData*);
75 static SkString try_auto_detect(SkString path, SkString* name);
76
77 static void fuzz_android_codec(sk_sp<SkData>);
78 static void fuzz_animated_img(sk_sp<SkData>);
79 static void fuzz_api(sk_sp<SkData> bytes, SkString name);
80 static void fuzz_color_deserialize(sk_sp<SkData>);
81 static void fuzz_filter_fuzz(sk_sp<SkData>);
82 static void fuzz_image_decode(sk_sp<SkData>);
83 static void fuzz_image_decode_incremental(sk_sp<SkData>);
84 static void fuzz_img(sk_sp<SkData>, uint8_t, uint8_t);
85 static void fuzz_json(sk_sp<SkData>);
86 static void fuzz_path_deserialize(sk_sp<SkData>);
87 static void fuzz_region_deserialize(sk_sp<SkData>);
88 static void fuzz_region_set_path(sk_sp<SkData>);
89 static void fuzz_skdescriptor_deserialize(sk_sp<SkData>);
90 static void fuzz_skp(sk_sp<SkData>);
91 static void fuzz_skruntimeeffect(sk_sp<SkData>);
92 static void fuzz_sksl2glsl(sk_sp<SkData>);
93 static void fuzz_sksl2metal(sk_sp<SkData>);
94 static void fuzz_sksl2pipeline(sk_sp<SkData>);
95 static void fuzz_sksl2spirv(sk_sp<SkData>);
96 static void fuzz_svg_dom(sk_sp<SkData>);
97 static void fuzz_textblob_deserialize(sk_sp<SkData>);
98
99 static void print_api_names();
100
101 #if defined(SK_ENABLE_SKOTTIE)
102 static void fuzz_skottie_json(sk_sp<SkData>);
103 #endif
104
main(int argc,char ** argv)105 int main(int argc, char** argv) {
106 CommandLineFlags::SetUsage(
107 "Usage: fuzz -t <type> -b <path/to/file> [-n api-to-fuzz]\n"
108 " fuzz -b <path/to/file>\n"
109 "--help lists the valid types. If type is not specified,\n"
110 "fuzz will make a guess based on the name of the file.\n");
111 CommandLineFlags::Parse(argc, argv);
112 gSkFontMgr_DefaultFactory = &ToolUtils::MakePortableFontMgr;
113
114 SkString path = SkString(FLAGS_bytes.isEmpty() ? argv[0] : FLAGS_bytes[0]);
115 SkString type = SkString(FLAGS_type.isEmpty() ? "" : FLAGS_type[0]);
116
117 int loopCount = std::max(FLAGS_loops, 1);
118
119 if (!sk_isdir(path.c_str())) {
120 for (int i = 0; i < loopCount; ++i) {
121 int rv = fuzz_file(path, type);
122 if (rv != 0) {
123 return rv;
124 }
125 }
126 return 0;
127 }
128
129 SkOSFile::Iter it(path.c_str());
130 for (SkString file; it.next(&file); ) {
131 SkString p = SkOSPath::Join(path.c_str(), file.c_str());
132 SkDebugf("Fuzzing %s\n", p.c_str());
133 for (int i = 0; i < loopCount; ++i) {
134 int rv = fuzz_file(p, type);
135 if (rv != 0) {
136 return rv;
137 }
138 }
139 }
140 return 0;
141 }
142
fuzz_file(SkString path,SkString type)143 static int fuzz_file(SkString path, SkString type) {
144 sk_sp<SkData> bytes(SkData::MakeFromFileName(path.c_str()));
145 if (!bytes) {
146 SkDebugf("Could not read %s\n", path.c_str());
147 return 1;
148 }
149
150 SkString name = SkString(FLAGS_name.isEmpty() ? "" : FLAGS_name[0]);
151
152 if (type.isEmpty()) {
153 type = try_auto_detect(path, &name);
154 }
155
156 if (type.isEmpty()) {
157 SkDebugf("Could not autodetect type of %s\n", path.c_str());
158 return 1;
159 }
160 if (type.equals("android_codec")) {
161 fuzz_android_codec(bytes);
162 return 0;
163 }
164 if (type.equals("animated_image_decode")) {
165 fuzz_animated_img(bytes);
166 return 0;
167 }
168 if (type.equals("api")) {
169 fuzz_api(bytes, name);
170 return 0;
171 }
172 if (type.equals("color_deserialize")) {
173 fuzz_color_deserialize(bytes);
174 return 0;
175 }
176 if (type.equals("filter_fuzz")) {
177 fuzz_filter_fuzz(bytes);
178 return 0;
179 }
180 if (type.equals("image_decode")) {
181 fuzz_image_decode(bytes);
182 return 0;
183 }
184 if (type.equals("image_decode_incremental")) {
185 fuzz_image_decode_incremental(bytes);
186 return 0;
187 }
188 if (type.equals("image_scale")) {
189 uint8_t option = calculate_option(bytes.get());
190 fuzz_img(bytes, option, 0);
191 return 0;
192 }
193 if (type.equals("image_mode")) {
194 uint8_t option = calculate_option(bytes.get());
195 fuzz_img(bytes, 0, option);
196 return 0;
197 }
198 if (type.equals("json")) {
199 fuzz_json(bytes);
200 return 0;
201 }
202 if (type.equals("path_deserialize")) {
203 fuzz_path_deserialize(bytes);
204 return 0;
205 }
206 if (type.equals("region_deserialize")) {
207 fuzz_region_deserialize(bytes);
208 return 0;
209 }
210 if (type.equals("region_set_path")) {
211 fuzz_region_set_path(bytes);
212 return 0;
213 }
214 if (type.equals("pipe")) {
215 SkDebugf("I would prefer not to.\n");
216 return 0;
217 }
218 if (type.equals("skdescriptor_deserialize")) {
219 fuzz_skdescriptor_deserialize(bytes);
220 return 0;
221 }
222 #if defined(SK_ENABLE_SKOTTIE)
223 if (type.equals("skottie_json")) {
224 fuzz_skottie_json(bytes);
225 return 0;
226 }
227 #endif
228 if (type.equals("skp")) {
229 fuzz_skp(bytes);
230 return 0;
231 }
232 if (type.equals("skruntimeeffect")) {
233 fuzz_skruntimeeffect(bytes);
234 return 0;
235 }
236 if (type.equals("sksl2glsl")) {
237 fuzz_sksl2glsl(bytes);
238 return 0;
239 }
240 if (type.equals("sksl2metal")) {
241 fuzz_sksl2metal(bytes);
242 return 0;
243 }
244 if (type.equals("sksl2spirv")) {
245 fuzz_sksl2spirv(bytes);
246 return 0;
247 }
248 if (type.equals("sksl2pipeline")) {
249 fuzz_sksl2pipeline(bytes);
250 return 0;
251 }
252 if (type.equals("svg_dom")) {
253 fuzz_svg_dom(bytes);
254 return 0;
255 }
256 if (type.equals("textblob")) {
257 fuzz_textblob_deserialize(bytes);
258 return 0;
259 }
260 SkDebugf("Unknown type %s\n", type.c_str());
261 CommandLineFlags::PrintUsage();
262 return 1;
263 }
264
265 static std::map<std::string, std::string> cf_api_map = {
266 {"api_create_ddl", "CreateDDL"},
267 {"api_draw_functions", "DrawFunctions"},
268 {"api_ddl_threading", "DDLThreadingGL"},
269 {"api_gradients", "Gradients"},
270 {"api_image_filter", "ImageFilter"},
271 {"api_mock_gpu_canvas", "MockGPUCanvas"},
272 {"api_null_canvas", "NullCanvas"},
273 {"api_path_measure", "PathMeasure"},
274 {"api_pathop", "Pathop"},
275 {"api_polyutils", "PolyUtils"},
276 {"api_raster_n32_canvas", "RasterN32Canvas"},
277 {"api_skparagraph", "SkParagraph"},
278 {"api_svg_canvas", "SVGCanvas"},
279 {"jpeg_encoder", "JPEGEncoder"},
280 {"png_encoder", "PNGEncoder"},
281 {"skia_pathop_fuzzer", "LegacyChromiumPathop"},
282 {"webp_encoder", "WEBPEncoder"}
283 };
284
285 // maps clusterfuzz/oss-fuzz -> Skia's name
286 static std::map<std::string, std::string> cf_map = {
287 {"android_codec", "android_codec"},
288 {"animated_image_decode", "animated_image_decode"},
289 {"image_decode", "image_decode"},
290 {"image_decode_incremental", "image_decode_incremental"},
291 {"image_filter_deserialize", "filter_fuzz"},
292 {"image_filter_deserialize_width", "filter_fuzz"},
293 {"path_deserialize", "path_deserialize"},
294 {"region_deserialize", "region_deserialize"},
295 {"region_set_path", "region_set_path"},
296 {"skdescriptor_deserialize", "skdescriptor_deserialize"},
297 {"skjson", "json"},
298 {"skp", "skp"},
299 {"skruntimeeffect", "skruntimeeffect"},
300 {"sksl2glsl", "sksl2glsl"},
301 {"sksl2metal", "sksl2metal"},
302 {"sksl2spirv", "sksl2spirv"},
303 {"sksl2pipeline", "sksl2pipeline"},
304 #if defined(SK_ENABLE_SKOTTIE)
305 {"skottie_json", "skottie_json"},
306 #endif
307 {"svg_dom", "svg_dom"},
308 {"textblob_deserialize", "textblob"}
309 };
310
try_auto_detect(SkString path,SkString * name)311 static SkString try_auto_detect(SkString path, SkString* name) {
312 std::cmatch m;
313 std::regex clusterfuzz("clusterfuzz-testcase(-minimized)?-([a-z0-9_]+)-[\\d]+");
314 std::regex skiafuzzer("(api-)?(\\w+)-[a-f0-9]+");
315
316 if (std::regex_search(path.c_str(), m, clusterfuzz)) {
317 std::string type = m.str(2);
318
319 if (cf_api_map.find(type) != cf_api_map.end()) {
320 *name = SkString(cf_api_map[type].c_str());
321 return SkString("api");
322 } else {
323 if (cf_map.find(type) != cf_map.end()) {
324 return SkString(cf_map[type].c_str());
325 }
326 }
327 } else if (std::regex_search(path.c_str(), m, skiafuzzer)) {
328 std::string a1 = m.str(1);
329 std::string typeOrName = m.str(2);
330 if (a1.length() > 0) {
331 // it's an api fuzzer
332 *name = SkString(typeOrName.c_str());
333 return SkString("api");
334 } else {
335 return SkString(typeOrName.c_str());
336 }
337 }
338
339 return SkString("");
340 }
341
342 void FuzzJSON(sk_sp<SkData> bytes);
343
fuzz_json(sk_sp<SkData> bytes)344 static void fuzz_json(sk_sp<SkData> bytes){
345 FuzzJSON(bytes);
346 SkDebugf("[terminated] Done parsing!\n");
347 }
348
349 #if defined(SK_ENABLE_SKOTTIE)
350 void FuzzSkottieJSON(sk_sp<SkData> bytes);
351
fuzz_skottie_json(sk_sp<SkData> bytes)352 static void fuzz_skottie_json(sk_sp<SkData> bytes){
353 FuzzSkottieJSON(bytes);
354 SkDebugf("[terminated] Done animating!\n");
355 }
356 #endif
357
358 void FuzzSVG(sk_sp<SkData> bytes);
fuzz_svg_dom(sk_sp<SkData> bytes)359 static void fuzz_svg_dom(sk_sp<SkData> bytes){
360 FuzzSVG(bytes);
361 SkDebugf("[terminated] Done DOM!\n");
362 }
363
364 // This adds up the first 1024 bytes and returns it as an 8 bit integer. This allows afl-fuzz to
365 // deterministically excercise different paths, or *options* (such as different scaling sizes or
366 // different image modes) without needing to introduce a parameter. This way we don't need a
367 // image_scale1, image_scale2, image_scale4, etc fuzzer, we can just have a image_scale fuzzer.
368 // Clients are expected to transform this number into a different range, e.g. with modulo (%).
calculate_option(SkData * bytes)369 static uint8_t calculate_option(SkData* bytes) {
370 uint8_t total = 0;
371 const uint8_t* data = bytes->bytes();
372 for (size_t i = 0; i < 1024 && i < bytes->size(); i++) {
373 total += data[i];
374 }
375 return total;
376 }
377
print_api_names()378 static void print_api_names(){
379 SkDebugf("When using --type api, please choose an API to fuzz with --name/-n:\n");
380 for (const Fuzzable& fuzzable : sk_tools::Registry<Fuzzable>::Range()) {
381 SkDebugf("\t%s\n", fuzzable.name);
382 }
383 }
384
fuzz_api(sk_sp<SkData> bytes,SkString name)385 static void fuzz_api(sk_sp<SkData> bytes, SkString name) {
386 for (const Fuzzable& fuzzable : sk_tools::Registry<Fuzzable>::Range()) {
387 if (name.equals(fuzzable.name)) {
388 SkDebugf("Fuzzing %s...\n", fuzzable.name);
389 Fuzz fuzz(std::move(bytes));
390 fuzzable.fn(&fuzz);
391 SkDebugf("[terminated] Success!\n");
392 return;
393 }
394 }
395
396 print_api_names();
397 }
398
dump_png(SkBitmap bitmap)399 static void dump_png(SkBitmap bitmap) {
400 if (!FLAGS_dump.isEmpty()) {
401 ToolUtils::EncodeImageToFile(FLAGS_dump[0], bitmap, SkEncodedImageFormat::kPNG, 100);
402 SkDebugf("Dumped to %s\n", FLAGS_dump[0]);
403 }
404 }
405
406 bool FuzzAnimatedImage(sk_sp<SkData> bytes);
407
fuzz_animated_img(sk_sp<SkData> bytes)408 static void fuzz_animated_img(sk_sp<SkData> bytes) {
409 if (FuzzAnimatedImage(bytes)) {
410 SkDebugf("[terminated] Success from decoding/drawing animated image!\n");
411 return;
412 }
413 SkDebugf("[terminated] Could not decode or draw animated image.\n");
414 }
415
416 bool FuzzImageDecode(sk_sp<SkData> bytes);
417
fuzz_image_decode(sk_sp<SkData> bytes)418 static void fuzz_image_decode(sk_sp<SkData> bytes) {
419 if (FuzzImageDecode(bytes)) {
420 SkDebugf("[terminated] Success from decoding/drawing image!\n");
421 return;
422 }
423 SkDebugf("[terminated] Could not decode or draw image.\n");
424 }
425
426 bool FuzzIncrementalImageDecode(sk_sp<SkData> bytes);
427
fuzz_image_decode_incremental(sk_sp<SkData> bytes)428 static void fuzz_image_decode_incremental(sk_sp<SkData> bytes) {
429 if (FuzzIncrementalImageDecode(bytes)) {
430 SkDebugf("[terminated] Success using incremental decode!\n");
431 return;
432 }
433 SkDebugf("[terminated] Could not incrementally decode and image.\n");
434 }
435
436 bool FuzzAndroidCodec(sk_sp<SkData> bytes, uint8_t sampleSize);
437
fuzz_android_codec(sk_sp<SkData> bytes)438 static void fuzz_android_codec(sk_sp<SkData> bytes) {
439 Fuzz fuzz(bytes);
440 uint8_t sampleSize;
441 fuzz.nextRange(&sampleSize, 1, 64);
442 bytes = SkData::MakeSubset(bytes.get(), 1, bytes->size() - 1);
443 if (FuzzAndroidCodec(bytes, sampleSize)) {
444 SkDebugf("[terminated] Success on Android Codec sampleSize=%u!\n", sampleSize);
445 return;
446 }
447 SkDebugf("[terminated] Could not use Android Codec sampleSize=%u!\n", sampleSize);
448 }
449
450 // This is a "legacy" fuzzer that likely does too much. It was based off of how
451 // DM reads in images. image_decode, image_decode_incremental and android_codec
452 // are more targeted fuzzers that do a subset of what this one does.
fuzz_img(sk_sp<SkData> bytes,uint8_t scale,uint8_t mode)453 static void fuzz_img(sk_sp<SkData> bytes, uint8_t scale, uint8_t mode) {
454 // We can scale 1x, 2x, 4x, 8x, 16x
455 scale = scale % 5;
456 float fscale = (float)pow(2.0f, scale);
457 SkDebugf("Scaling factor: %f\n", fscale);
458
459 // We have 5 different modes of decoding.
460 mode = mode % 5;
461 SkDebugf("Mode: %d\n", mode);
462
463 // This is mostly copied from DMSrcSink's CodecSrc::draw method.
464 SkDebugf("Decoding\n");
465 std::unique_ptr<SkCodec> codec(SkCodec::MakeFromData(bytes));
466 if (nullptr == codec) {
467 SkDebugf("[terminated] Couldn't create codec.\n");
468 return;
469 }
470
471 SkImageInfo decodeInfo = codec->getInfo();
472 SkISize size = codec->getScaledDimensions(fscale);
473 decodeInfo = decodeInfo.makeDimensions(size);
474
475 SkBitmap bitmap;
476 SkCodec::Options options;
477 options.fZeroInitialized = SkCodec::kYes_ZeroInitialized;
478
479 if (!bitmap.tryAllocPixelsFlags(decodeInfo, SkBitmap::kZeroPixels_AllocFlag)) {
480 SkDebugf("[terminated] Could not allocate memory. Image might be too large (%d x %d)",
481 decodeInfo.width(), decodeInfo.height());
482 return;
483 }
484
485 switch (mode) {
486 case 0: {//kCodecZeroInit_Mode, kCodec_Mode
487 switch (codec->getPixels(decodeInfo, bitmap.getPixels(), bitmap.rowBytes(), &options)) {
488 case SkCodec::kSuccess:
489 SkDebugf("[terminated] Success!\n");
490 break;
491 case SkCodec::kIncompleteInput:
492 SkDebugf("[terminated] Partial Success\n");
493 break;
494 case SkCodec::kErrorInInput:
495 SkDebugf("[terminated] Partial Success with error\n");
496 break;
497 case SkCodec::kInvalidConversion:
498 SkDebugf("Incompatible colortype conversion\n");
499 // Crash to allow afl-fuzz to know this was a bug.
500 raise(SIGSEGV);
501 break;
502 default:
503 SkDebugf("[terminated] Couldn't getPixels.\n");
504 return;
505 }
506 break;
507 }
508 case 1: {//kScanline_Mode
509 if (SkCodec::kSuccess != codec->startScanlineDecode(decodeInfo)) {
510 SkDebugf("[terminated] Could not start scanline decoder\n");
511 return;
512 }
513
514 void* dst = bitmap.getAddr(0, 0);
515 size_t rowBytes = bitmap.rowBytes();
516 uint32_t height = decodeInfo.height();
517 // We do not need to check the return value. On an incomplete
518 // image, memory will be filled with a default value.
519 codec->getScanlines(dst, height, rowBytes);
520 SkDebugf("[terminated] Success!\n");
521 break;
522 }
523 case 2: { //kStripe_Mode
524 const int height = decodeInfo.height();
525 // This value is chosen arbitrarily. We exercise more cases by choosing a value that
526 // does not align with image blocks.
527 const int stripeHeight = 37;
528 const int numStripes = (height + stripeHeight - 1) / stripeHeight;
529
530 // Decode odd stripes
531 if (SkCodec::kSuccess != codec->startScanlineDecode(decodeInfo)
532 || SkCodec::kTopDown_SkScanlineOrder != codec->getScanlineOrder()) {
533 // This mode was designed to test the new skip scanlines API in libjpeg-turbo.
534 // Jpegs have kTopDown_SkScanlineOrder, and at this time, it is not interesting
535 // to run this test for image types that do not have this scanline ordering.
536 SkDebugf("[terminated] Could not start top-down scanline decoder\n");
537 return;
538 }
539
540 for (int i = 0; i < numStripes; i += 2) {
541 // Skip a stripe
542 const int linesToSkip = std::min(stripeHeight, height - i * stripeHeight);
543 codec->skipScanlines(linesToSkip);
544
545 // Read a stripe
546 const int startY = (i + 1) * stripeHeight;
547 const int linesToRead = std::min(stripeHeight, height - startY);
548 if (linesToRead > 0) {
549 codec->getScanlines(bitmap.getAddr(0, startY), linesToRead, bitmap.rowBytes());
550 }
551 }
552
553 // Decode even stripes
554 const SkCodec::Result startResult = codec->startScanlineDecode(decodeInfo);
555 if (SkCodec::kSuccess != startResult) {
556 SkDebugf("[terminated] Failed to restart scanline decoder with same parameters.\n");
557 return;
558 }
559 for (int i = 0; i < numStripes; i += 2) {
560 // Read a stripe
561 const int startY = i * stripeHeight;
562 const int linesToRead = std::min(stripeHeight, height - startY);
563 codec->getScanlines(bitmap.getAddr(0, startY), linesToRead, bitmap.rowBytes());
564
565 // Skip a stripe
566 const int linesToSkip = std::min(stripeHeight, height - (i + 1) * stripeHeight);
567 if (linesToSkip > 0) {
568 codec->skipScanlines(linesToSkip);
569 }
570 }
571 SkDebugf("[terminated] Success!\n");
572 break;
573 }
574 case 3: { //kSubset_Mode
575 // Arbitrarily choose a divisor.
576 int divisor = 2;
577 // Total width/height of the image.
578 const int W = codec->getInfo().width();
579 const int H = codec->getInfo().height();
580 if (divisor > W || divisor > H) {
581 SkDebugf("[terminated] Cannot codec subset: divisor %d is too big "
582 "with dimensions (%d x %d)\n", divisor, W, H);
583 return;
584 }
585 // subset dimensions
586 // SkWebpCodec, the only one that supports subsets, requires even top/left boundaries.
587 const int w = SkAlign2(W / divisor);
588 const int h = SkAlign2(H / divisor);
589 SkIRect subset;
590 SkCodec::Options opts;
591 opts.fSubset = ⊂
592 SkBitmap subsetBm;
593 // We will reuse pixel memory from bitmap.
594 void* pixels = bitmap.getPixels();
595 // Keep track of left and top (for drawing subsetBm into canvas). We could use
596 // fscale * x and fscale * y, but we want integers such that the next subset will start
597 // where the last one ended. So we'll add decodeInfo.width() and height().
598 int left = 0;
599 for (int x = 0; x < W; x += w) {
600 int top = 0;
601 for (int y = 0; y < H; y+= h) {
602 // Do not make the subset go off the edge of the image.
603 const int preScaleW = std::min(w, W - x);
604 const int preScaleH = std::min(h, H - y);
605 subset.setXYWH(x, y, preScaleW, preScaleH);
606 // And fscale
607 // FIXME: Should we have a version of getScaledDimensions that takes a subset
608 // into account?
609 decodeInfo = decodeInfo.makeWH(
610 std::max(1, SkScalarRoundToInt(preScaleW * fscale)),
611 std::max(1, SkScalarRoundToInt(preScaleH * fscale)));
612 size_t rowBytes = decodeInfo.minRowBytes();
613 if (!subsetBm.installPixels(decodeInfo, pixels, rowBytes)) {
614 SkDebugf("[terminated] Could not install pixels.\n");
615 return;
616 }
617 const SkCodec::Result result = codec->getPixels(decodeInfo, pixels, rowBytes,
618 &opts);
619 switch (result) {
620 case SkCodec::kSuccess:
621 case SkCodec::kIncompleteInput:
622 case SkCodec::kErrorInInput:
623 SkDebugf("okay\n");
624 break;
625 case SkCodec::kInvalidConversion:
626 if (0 == (x|y)) {
627 // First subset is okay to return unimplemented.
628 SkDebugf("[terminated] Incompatible colortype conversion\n");
629 return;
630 }
631 // If the first subset succeeded, a later one should not fail.
632 [[fallthrough]];
633 case SkCodec::kUnimplemented:
634 if (0 == (x|y)) {
635 // First subset is okay to return unimplemented.
636 SkDebugf("[terminated] subset codec not supported\n");
637 return;
638 }
639 // If the first subset succeeded, why would a later one fail?
640 [[fallthrough]];
641 default:
642 SkDebugf("[terminated] subset codec failed to decode (%d, %d, %d, %d) "
643 "with dimensions (%d x %d)\t error %d\n",
644 x, y, decodeInfo.width(), decodeInfo.height(),
645 W, H, result);
646 return;
647 }
648 // translate by the scaled height.
649 top += decodeInfo.height();
650 }
651 // translate by the scaled width.
652 left += decodeInfo.width();
653 }
654 SkDebugf("[terminated] Success!\n");
655 break;
656 }
657 case 4: { //kAnimated_Mode
658 std::vector<SkCodec::FrameInfo> frameInfos = codec->getFrameInfo();
659 if (frameInfos.size() == 0) {
660 SkDebugf("[terminated] Not an animated image\n");
661 break;
662 }
663
664 for (size_t i = 0; i < frameInfos.size(); i++) {
665 options.fFrameIndex = i;
666 auto result = codec->startIncrementalDecode(decodeInfo, bitmap.getPixels(),
667 bitmap.rowBytes(), &options);
668 if (SkCodec::kSuccess != result) {
669 SkDebugf("[terminated] failed to start incremental decode "
670 "in frame %d with error %d\n", i, result);
671 return;
672 }
673
674 result = codec->incrementalDecode();
675 if (result == SkCodec::kIncompleteInput || result == SkCodec::kErrorInInput) {
676 SkDebugf("okay\n");
677 // Frames beyond this one will not decode.
678 break;
679 }
680 if (result == SkCodec::kSuccess) {
681 SkDebugf("okay - decoded frame %d\n", i);
682 } else {
683 SkDebugf("[terminated] incremental decode failed with "
684 "error %d\n", result);
685 return;
686 }
687 }
688 SkDebugf("[terminated] Success!\n");
689 break;
690 }
691 default:
692 SkDebugf("[terminated] Mode not implemented yet\n");
693 }
694
695 dump_png(bitmap);
696 }
697
698 void FuzzSKP(sk_sp<SkData> bytes);
fuzz_skp(sk_sp<SkData> bytes)699 static void fuzz_skp(sk_sp<SkData> bytes) {
700 FuzzSKP(bytes);
701 SkDebugf("[terminated] Finished SKP\n");
702 }
703
fuzz_color_deserialize(sk_sp<SkData> bytes)704 static void fuzz_color_deserialize(sk_sp<SkData> bytes) {
705 sk_sp<SkColorSpace> space(SkColorSpace::Deserialize(bytes->data(), bytes->size()));
706 if (!space) {
707 SkDebugf("[terminated] Couldn't deserialize Colorspace.\n");
708 return;
709 }
710 SkDebugf("[terminated] Success! deserialized Colorspace.\n");
711 }
712
713 void FuzzPathDeserialize(SkReadBuffer& buf);
714
fuzz_path_deserialize(sk_sp<SkData> bytes)715 static void fuzz_path_deserialize(sk_sp<SkData> bytes) {
716 SkReadBuffer buf(bytes->data(), bytes->size());
717 FuzzPathDeserialize(buf);
718 SkDebugf("[terminated] path_deserialize didn't crash!\n");
719 }
720
721 bool FuzzRegionDeserialize(sk_sp<SkData> bytes);
722
fuzz_region_deserialize(sk_sp<SkData> bytes)723 static void fuzz_region_deserialize(sk_sp<SkData> bytes) {
724 if (!FuzzRegionDeserialize(bytes)) {
725 SkDebugf("[terminated] Couldn't initialize SkRegion.\n");
726 return;
727 }
728 SkDebugf("[terminated] Success! Initialized SkRegion.\n");
729 }
730
731 void FuzzTextBlobDeserialize(SkReadBuffer& buf);
732
fuzz_textblob_deserialize(sk_sp<SkData> bytes)733 static void fuzz_textblob_deserialize(sk_sp<SkData> bytes) {
734 SkReadBuffer buf(bytes->data(), bytes->size());
735 FuzzTextBlobDeserialize(buf);
736 SkDebugf("[terminated] textblob didn't crash!\n");
737 }
738
739 void FuzzRegionSetPath(Fuzz* fuzz);
740
fuzz_region_set_path(sk_sp<SkData> bytes)741 static void fuzz_region_set_path(sk_sp<SkData> bytes) {
742 Fuzz fuzz(bytes);
743 FuzzRegionSetPath(&fuzz);
744 SkDebugf("[terminated] region_set_path didn't crash!\n");
745 }
746
747 void FuzzImageFilterDeserialize(sk_sp<SkData> bytes);
748
fuzz_filter_fuzz(sk_sp<SkData> bytes)749 static void fuzz_filter_fuzz(sk_sp<SkData> bytes) {
750 FuzzImageFilterDeserialize(bytes);
751 SkDebugf("[terminated] filter_fuzz didn't crash!\n");
752 }
753
754 bool FuzzSkRuntimeEffect(sk_sp<SkData> bytes);
755
fuzz_skruntimeeffect(sk_sp<SkData> bytes)756 static void fuzz_skruntimeeffect(sk_sp<SkData> bytes) {
757 if (FuzzSkRuntimeEffect(bytes)) {
758 SkDebugf("[terminated] Success! Compiled and Executed sksl code.\n");
759 } else {
760 SkDebugf("[terminated] Could not Compile or Execute sksl code.\n");
761 }
762 }
763
764 bool FuzzSKSL2GLSL(sk_sp<SkData> bytes);
765
fuzz_sksl2glsl(sk_sp<SkData> bytes)766 static void fuzz_sksl2glsl(sk_sp<SkData> bytes) {
767 if (FuzzSKSL2GLSL(bytes)) {
768 SkDebugf("[terminated] Success! Compiled input to GLSL.\n");
769 } else {
770 SkDebugf("[terminated] Could not compile input to GLSL.\n");
771 }
772 }
773
774 bool FuzzSKSL2SPIRV(sk_sp<SkData> bytes);
775
fuzz_sksl2spirv(sk_sp<SkData> bytes)776 static void fuzz_sksl2spirv(sk_sp<SkData> bytes) {
777 if (FuzzSKSL2SPIRV(bytes)) {
778 SkDebugf("[terminated] Success! Compiled input to SPIRV.\n");
779 } else {
780 SkDebugf("[terminated] Could not compile input to SPIRV.\n");
781 }
782 }
783
784 bool FuzzSKSL2Metal(sk_sp<SkData> bytes);
785
fuzz_sksl2metal(sk_sp<SkData> bytes)786 static void fuzz_sksl2metal(sk_sp<SkData> bytes) {
787 if (FuzzSKSL2Metal(bytes)) {
788 SkDebugf("[terminated] Success! Compiled input to Metal.\n");
789 } else {
790 SkDebugf("[terminated] Could not compile input to Metal.\n");
791 }
792 }
793
794 bool FuzzSKSL2Pipeline(sk_sp<SkData> bytes);
795
fuzz_sksl2pipeline(sk_sp<SkData> bytes)796 static void fuzz_sksl2pipeline(sk_sp<SkData> bytes) {
797 if (FuzzSKSL2Pipeline(bytes)) {
798 SkDebugf("[terminated] Success! Compiled input to pipeline stage.\n");
799 } else {
800 SkDebugf("[terminated] Could not compile input to pipeline stage.\n");
801 }
802 }
803
804 void FuzzSkDescriptorDeserialize(sk_sp<SkData> bytes);
805
fuzz_skdescriptor_deserialize(sk_sp<SkData> bytes)806 static void fuzz_skdescriptor_deserialize(sk_sp<SkData> bytes) {
807 FuzzSkDescriptorDeserialize(bytes);
808 SkDebugf("[terminated] Did not crash while deserializing an SkDescriptor.\n");
809 }
810
811