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