2016-01-13 20:57:57 +00:00
|
|
|
/*
|
|
|
|
* Copyright 2016 Google Inc.
|
|
|
|
*
|
|
|
|
* Use of this source code is governed by a BSD-style license that can be
|
|
|
|
* found in the LICENSE file.
|
|
|
|
*/
|
|
|
|
|
2019-04-23 17:05:21 +00:00
|
|
|
#include "fuzz/Fuzz.h"
|
|
|
|
#include "include/codec/SkCodec.h"
|
|
|
|
#include "include/core/SkCanvas.h"
|
|
|
|
#include "include/core/SkData.h"
|
|
|
|
#include "include/core/SkImage.h"
|
|
|
|
#include "include/core/SkImageEncoder.h"
|
|
|
|
#include "include/core/SkMallocPixelRef.h"
|
|
|
|
#include "include/core/SkPaint.h"
|
|
|
|
#include "include/core/SkPath.h"
|
|
|
|
#include "include/core/SkStream.h"
|
|
|
|
#include "include/core/SkSurface.h"
|
|
|
|
#include "include/core/SkTextBlob.h"
|
2019-06-03 15:59:23 +00:00
|
|
|
#include "src/core/SkFontMgrPriv.h"
|
2019-04-23 17:05:21 +00:00
|
|
|
#include "src/core/SkOSFile.h"
|
|
|
|
#include "src/core/SkReadBuffer.h"
|
|
|
|
#include "src/utils/SkOSPath.h"
|
|
|
|
#include "tools/ToolUtils.h"
|
2019-06-03 15:59:23 +00:00
|
|
|
#include "tools/flags/CommandLineFlags.h"
|
|
|
|
#include "tools/fonts/TestFontMgr.h"
|
2018-09-19 15:31:27 +00:00
|
|
|
|
2017-02-27 21:42:03 +00:00
|
|
|
#include <iostream>
|
2018-04-24 17:58:16 +00:00
|
|
|
#include <map>
|
2018-04-23 20:44:55 +00:00
|
|
|
#include <regex>
|
2016-01-15 13:46:54 +00:00
|
|
|
#include <signal.h>
|
2018-01-11 15:27:14 +00:00
|
|
|
|
2019-03-21 16:31:36 +00:00
|
|
|
static DEFINE_string2(bytes, b, "", "A path to a file or a directory. If a file, the "
|
|
|
|
"contents will be used as the fuzz bytes. If a directory, all files "
|
|
|
|
"in the directory will be used as fuzz bytes for the fuzzer, one at a "
|
|
|
|
"time.");
|
|
|
|
static DEFINE_string2(name, n, "", "If --type is 'api', fuzz the API with this name.");
|
|
|
|
static DEFINE_string2(dump, d, "", "If not empty, dump 'image*' or 'skp' types as a "
|
|
|
|
"PNG with this name.");
|
2020-07-17 15:37:14 +00:00
|
|
|
static DEFINE_int(loops, 1, "Run the fuzzer on each input this many times.");
|
2018-01-09 17:47:33 +00:00
|
|
|
DEFINE_bool2(verbose, v, false, "Print more information while fuzzing.");
|
2018-06-08 16:25:38 +00:00
|
|
|
|
|
|
|
// This cannot be inlined in DEFINE_string2 due to interleaved ifdefs
|
|
|
|
static constexpr char g_type_message[] = "How to interpret --bytes, one of:\n"
|
2018-10-17 14:24:44 +00:00
|
|
|
"android_codec\n"
|
2018-06-08 16:25:38 +00:00
|
|
|
"animated_image_decode\n"
|
|
|
|
"api\n"
|
|
|
|
"color_deserialize\n"
|
|
|
|
"filter_fuzz (equivalent to Chrome's filter_fuzz_stub)\n"
|
|
|
|
"image_decode\n"
|
2018-10-17 14:24:44 +00:00
|
|
|
"image_decode_incremental\n"
|
2018-06-08 16:25:38 +00:00
|
|
|
"image_mode\n"
|
|
|
|
"image_scale\n"
|
2018-06-19 15:27:20 +00:00
|
|
|
"json\n"
|
2018-06-08 16:25:38 +00:00
|
|
|
"path_deserialize\n"
|
|
|
|
"region_deserialize\n"
|
|
|
|
"region_set_path\n"
|
2019-10-21 17:44:48 +00:00
|
|
|
"skdescriptor_deserialize\n"
|
2018-06-08 16:25:38 +00:00
|
|
|
"skp\n"
|
Add SkRuntimeEffect Fuzzer
The major improvement is that now the fuzzer is able to execute
the sksl code (before it just compiled it). The fuzzer will
reserve 256 bytes for providing uniforms to the shader;
meanwhile, the fuzzer will read the remaining bytes as sksl code
to create SkRuntimeEffect. It then creates a shader and executes
it by painting the shader on a canvas.
The code was tested locally with afl-fuzz, and the execution
speed was around 700/sec.
An alternative implementation would have been using Fuzz.h to
read bytes; I decided to go with sk_sp<SkData> since it has a
comparable format to other binary fuzzer and meets all the
functionality in this fuzzer.
For future changes, there are 2 important improvements to the
implementation:
1) Current shader does not have children shaders; thus,
makeShader() will fail if the SkSL ever tries to use an 'in shader'.
As pointed out in patchset 11, after creating the runtime effect,
effect->children().count() will tell you how many children it's
expecting (how many 'in shader' variables were declared). When you
call makeShader(), the second and third arguments are a
(C-style) array of shader pointers, and
a count (which must match children().count()).
Some helpful examples can be SkRTShader::CreateProc in
SkRuntimeEffect.cpp, make_fuzz_shader in FuzzCanvas.cpp.
2)
In this fuzzer, after creating the paint from a shader, the paint
can be drawn on either GPU canvas or CPU, so a possible way is to
use SkSurface::MakeRenderTarget to create GPU canvas and use a byte
to determine which canvas it will be drawn on.
Change-Id: Ib0385edd0f5ec2f23744aa517135a6955c53ba38
Reviewed-on: https://skia-review.googlesource.com/c/skia/+/300618
Commit-Queue: Zepeng Hu <zepenghu@google.com>
Reviewed-by: Brian Osman <brianosman@google.com>
Reviewed-by: Kevin Lubick <kjlubick@google.com>
2020-07-10 13:36:20 +00:00
|
|
|
"skruntimeeffect\n"
|
2018-06-08 16:25:38 +00:00
|
|
|
"sksl2glsl\n"
|
2020-06-12 12:20:59 +00:00
|
|
|
"svg_dom\n"
|
2019-03-11 15:09:40 +00:00
|
|
|
"sksl2metal\n"
|
2019-03-18 20:20:55 +00:00
|
|
|
"sksl2pipeline\n"
|
2019-03-11 15:09:40 +00:00
|
|
|
"sksl2spirv\n"
|
2018-06-08 16:25:38 +00:00
|
|
|
#if defined(SK_ENABLE_SKOTTIE)
|
|
|
|
"skottie_json\n"
|
|
|
|
#endif
|
|
|
|
"textblob";
|
|
|
|
|
2019-03-21 16:31:36 +00:00
|
|
|
static DEFINE_string2(type, t, "", g_type_message);
|
2016-01-21 13:03:28 +00:00
|
|
|
|
2018-04-23 20:44:55 +00:00
|
|
|
static int fuzz_file(SkString path, SkString type);
|
2016-02-17 00:14:23 +00:00
|
|
|
static uint8_t calculate_option(SkData*);
|
2018-04-23 20:44:55 +00:00
|
|
|
static SkString try_auto_detect(SkString path, SkString* name);
|
2016-01-21 13:03:28 +00:00
|
|
|
|
2018-10-17 14:24:44 +00:00
|
|
|
static void fuzz_android_codec(sk_sp<SkData>);
|
|
|
|
static void fuzz_animated_img(sk_sp<SkData>);
|
2018-04-23 20:44:55 +00:00
|
|
|
static void fuzz_api(sk_sp<SkData> bytes, SkString name);
|
2017-01-06 13:26:56 +00:00
|
|
|
static void fuzz_color_deserialize(sk_sp<SkData>);
|
2017-11-21 22:13:35 +00:00
|
|
|
static void fuzz_filter_fuzz(sk_sp<SkData>);
|
2018-10-17 14:24:44 +00:00
|
|
|
static void fuzz_image_decode(sk_sp<SkData>);
|
|
|
|
static void fuzz_image_decode_incremental(sk_sp<SkData>);
|
2017-02-14 18:12:37 +00:00
|
|
|
static void fuzz_img(sk_sp<SkData>, uint8_t, uint8_t);
|
2018-06-19 15:27:20 +00:00
|
|
|
static void fuzz_json(sk_sp<SkData>);
|
2017-01-06 18:48:19 +00:00
|
|
|
static void fuzz_path_deserialize(sk_sp<SkData>);
|
2017-02-20 22:47:18 +00:00
|
|
|
static void fuzz_region_deserialize(sk_sp<SkData>);
|
2018-01-11 15:27:14 +00:00
|
|
|
static void fuzz_region_set_path(sk_sp<SkData>);
|
2019-10-21 17:44:48 +00:00
|
|
|
static void fuzz_skdescriptor_deserialize(sk_sp<SkData>);
|
2017-02-14 18:12:37 +00:00
|
|
|
static void fuzz_skp(sk_sp<SkData>);
|
Add SkRuntimeEffect Fuzzer
The major improvement is that now the fuzzer is able to execute
the sksl code (before it just compiled it). The fuzzer will
reserve 256 bytes for providing uniforms to the shader;
meanwhile, the fuzzer will read the remaining bytes as sksl code
to create SkRuntimeEffect. It then creates a shader and executes
it by painting the shader on a canvas.
The code was tested locally with afl-fuzz, and the execution
speed was around 700/sec.
An alternative implementation would have been using Fuzz.h to
read bytes; I decided to go with sk_sp<SkData> since it has a
comparable format to other binary fuzzer and meets all the
functionality in this fuzzer.
For future changes, there are 2 important improvements to the
implementation:
1) Current shader does not have children shaders; thus,
makeShader() will fail if the SkSL ever tries to use an 'in shader'.
As pointed out in patchset 11, after creating the runtime effect,
effect->children().count() will tell you how many children it's
expecting (how many 'in shader' variables were declared). When you
call makeShader(), the second and third arguments are a
(C-style) array of shader pointers, and
a count (which must match children().count()).
Some helpful examples can be SkRTShader::CreateProc in
SkRuntimeEffect.cpp, make_fuzz_shader in FuzzCanvas.cpp.
2)
In this fuzzer, after creating the paint from a shader, the paint
can be drawn on either GPU canvas or CPU, so a possible way is to
use SkSurface::MakeRenderTarget to create GPU canvas and use a byte
to determine which canvas it will be drawn on.
Change-Id: Ib0385edd0f5ec2f23744aa517135a6955c53ba38
Reviewed-on: https://skia-review.googlesource.com/c/skia/+/300618
Commit-Queue: Zepeng Hu <zepenghu@google.com>
Reviewed-by: Brian Osman <brianosman@google.com>
Reviewed-by: Kevin Lubick <kjlubick@google.com>
2020-07-10 13:36:20 +00:00
|
|
|
static void fuzz_skruntimeeffect(sk_sp<SkData>);
|
2019-03-11 15:09:40 +00:00
|
|
|
static void fuzz_sksl2glsl(sk_sp<SkData>);
|
|
|
|
static void fuzz_sksl2metal(sk_sp<SkData>);
|
2019-03-18 20:20:55 +00:00
|
|
|
static void fuzz_sksl2pipeline(sk_sp<SkData>);
|
2019-10-21 17:44:48 +00:00
|
|
|
static void fuzz_sksl2spirv(sk_sp<SkData>);
|
2020-06-12 12:20:59 +00:00
|
|
|
static void fuzz_svg_dom(sk_sp<SkData>);
|
2017-11-21 22:13:35 +00:00
|
|
|
static void fuzz_textblob_deserialize(sk_sp<SkData>);
|
2017-03-16 16:30:43 +00:00
|
|
|
|
2018-04-23 20:44:55 +00:00
|
|
|
static void print_api_names();
|
|
|
|
|
2018-05-04 19:01:03 +00:00
|
|
|
#if defined(SK_ENABLE_SKOTTIE)
|
|
|
|
static void fuzz_skottie_json(sk_sp<SkData>);
|
|
|
|
#endif
|
|
|
|
|
2016-01-13 20:57:57 +00:00
|
|
|
int main(int argc, char** argv) {
|
2019-03-20 15:50:33 +00:00
|
|
|
CommandLineFlags::SetUsage(
|
|
|
|
"Usage: fuzz -t <type> -b <path/to/file> [-n api-to-fuzz]\n"
|
|
|
|
" fuzz -b <path/to/file>\n"
|
|
|
|
"--help lists the valid types. If type is not specified,\n"
|
|
|
|
"fuzz will make a guess based on the name of the file.\n");
|
|
|
|
CommandLineFlags::Parse(argc, argv);
|
2019-06-03 15:59:23 +00:00
|
|
|
gSkFontMgr_DefaultFactory = &ToolUtils::MakePortableFontMgr;
|
2016-01-15 14:19:53 +00:00
|
|
|
|
2018-04-23 20:44:55 +00:00
|
|
|
SkString path = SkString(FLAGS_bytes.isEmpty() ? argv[0] : FLAGS_bytes[0]);
|
|
|
|
SkString type = SkString(FLAGS_type.isEmpty() ? "" : FLAGS_type[0]);
|
2017-01-06 13:26:56 +00:00
|
|
|
|
2020-07-17 15:37:14 +00:00
|
|
|
int loopCount = std::max(FLAGS_loops, 1);
|
|
|
|
|
2018-04-23 20:44:55 +00:00
|
|
|
if (!sk_isdir(path.c_str())) {
|
2020-07-17 15:37:14 +00:00
|
|
|
for (int i = 0; i < loopCount; ++i) {
|
|
|
|
int rv = fuzz_file(path, type);
|
|
|
|
if (rv != 0) {
|
|
|
|
return rv;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return 0;
|
2017-01-06 13:26:56 +00:00
|
|
|
}
|
|
|
|
|
2018-04-23 20:44:55 +00:00
|
|
|
SkOSFile::Iter it(path.c_str());
|
2017-01-06 13:26:56 +00:00
|
|
|
for (SkString file; it.next(&file); ) {
|
2018-04-23 20:44:55 +00:00
|
|
|
SkString p = SkOSPath::Join(path.c_str(), file.c_str());
|
2017-01-06 13:26:56 +00:00
|
|
|
SkDebugf("Fuzzing %s\n", p.c_str());
|
2020-07-17 15:37:14 +00:00
|
|
|
for (int i = 0; i < loopCount; ++i) {
|
|
|
|
int rv = fuzz_file(p, type);
|
|
|
|
if (rv != 0) {
|
|
|
|
return rv;
|
|
|
|
}
|
2017-01-06 13:26:56 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2018-04-23 20:44:55 +00:00
|
|
|
static int fuzz_file(SkString path, SkString type) {
|
|
|
|
sk_sp<SkData> bytes(SkData::MakeFromFileName(path.c_str()));
|
2016-01-21 13:03:28 +00:00
|
|
|
if (!bytes) {
|
2018-04-23 20:44:55 +00:00
|
|
|
SkDebugf("Could not read %s\n", path.c_str());
|
2017-01-06 13:26:56 +00:00
|
|
|
return 1;
|
2016-01-21 13:03:28 +00:00
|
|
|
}
|
2016-01-13 20:57:57 +00:00
|
|
|
|
2018-04-23 20:44:55 +00:00
|
|
|
SkString name = SkString(FLAGS_name.isEmpty() ? "" : FLAGS_name[0]);
|
|
|
|
|
|
|
|
if (type.isEmpty()) {
|
|
|
|
type = try_auto_detect(path, &name);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (type.isEmpty()) {
|
|
|
|
SkDebugf("Could not autodetect type of %s\n", path.c_str());
|
|
|
|
return 1;
|
|
|
|
}
|
2018-10-17 14:24:44 +00:00
|
|
|
if (type.equals("android_codec")) {
|
|
|
|
fuzz_android_codec(bytes);
|
|
|
|
return 0;
|
|
|
|
}
|
2018-04-23 20:44:55 +00:00
|
|
|
if (type.equals("animated_image_decode")) {
|
|
|
|
fuzz_animated_img(bytes);
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
if (type.equals("api")) {
|
|
|
|
fuzz_api(bytes, name);
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
if (type.equals("color_deserialize")) {
|
|
|
|
fuzz_color_deserialize(bytes);
|
|
|
|
return 0;
|
|
|
|
}
|
2018-06-19 15:27:20 +00:00
|
|
|
if (type.equals("filter_fuzz")) {
|
|
|
|
fuzz_filter_fuzz(bytes);
|
|
|
|
return 0;
|
|
|
|
}
|
2018-04-23 20:44:55 +00:00
|
|
|
if (type.equals("image_decode")) {
|
2018-10-17 14:24:44 +00:00
|
|
|
fuzz_image_decode(bytes);
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
if (type.equals("image_decode_incremental")) {
|
|
|
|
fuzz_image_decode_incremental(bytes);
|
2018-04-23 20:44:55 +00:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
if (type.equals("image_scale")) {
|
|
|
|
uint8_t option = calculate_option(bytes.get());
|
|
|
|
fuzz_img(bytes, option, 0);
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
if (type.equals("image_mode")) {
|
|
|
|
uint8_t option = calculate_option(bytes.get());
|
|
|
|
fuzz_img(bytes, 0, option);
|
|
|
|
return 0;
|
|
|
|
}
|
2018-06-19 15:27:20 +00:00
|
|
|
if (type.equals("json")) {
|
|
|
|
fuzz_json(bytes);
|
2018-05-03 20:26:10 +00:00
|
|
|
return 0;
|
|
|
|
}
|
2018-04-23 20:44:55 +00:00
|
|
|
if (type.equals("path_deserialize")) {
|
|
|
|
fuzz_path_deserialize(bytes);
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
if (type.equals("region_deserialize")) {
|
|
|
|
fuzz_region_deserialize(bytes);
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
if (type.equals("region_set_path")) {
|
|
|
|
fuzz_region_set_path(bytes);
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
if (type.equals("pipe")) {
|
2018-09-21 15:19:45 +00:00
|
|
|
SkDebugf("I would prefer not to.\n");
|
2018-04-23 20:44:55 +00:00
|
|
|
return 0;
|
|
|
|
}
|
2019-10-21 17:44:48 +00:00
|
|
|
if (type.equals("skdescriptor_deserialize")) {
|
|
|
|
fuzz_skdescriptor_deserialize(bytes);
|
|
|
|
return 0;
|
|
|
|
}
|
2018-05-04 19:01:03 +00:00
|
|
|
#if defined(SK_ENABLE_SKOTTIE)
|
2018-05-03 20:26:10 +00:00
|
|
|
if (type.equals("skottie_json")) {
|
|
|
|
fuzz_skottie_json(bytes);
|
2018-04-23 20:44:55 +00:00
|
|
|
return 0;
|
|
|
|
}
|
2018-05-04 19:01:03 +00:00
|
|
|
#endif
|
2018-05-03 20:26:10 +00:00
|
|
|
if (type.equals("skp")) {
|
|
|
|
fuzz_skp(bytes);
|
2018-04-23 20:44:55 +00:00
|
|
|
return 0;
|
|
|
|
}
|
Add SkRuntimeEffect Fuzzer
The major improvement is that now the fuzzer is able to execute
the sksl code (before it just compiled it). The fuzzer will
reserve 256 bytes for providing uniforms to the shader;
meanwhile, the fuzzer will read the remaining bytes as sksl code
to create SkRuntimeEffect. It then creates a shader and executes
it by painting the shader on a canvas.
The code was tested locally with afl-fuzz, and the execution
speed was around 700/sec.
An alternative implementation would have been using Fuzz.h to
read bytes; I decided to go with sk_sp<SkData> since it has a
comparable format to other binary fuzzer and meets all the
functionality in this fuzzer.
For future changes, there are 2 important improvements to the
implementation:
1) Current shader does not have children shaders; thus,
makeShader() will fail if the SkSL ever tries to use an 'in shader'.
As pointed out in patchset 11, after creating the runtime effect,
effect->children().count() will tell you how many children it's
expecting (how many 'in shader' variables were declared). When you
call makeShader(), the second and third arguments are a
(C-style) array of shader pointers, and
a count (which must match children().count()).
Some helpful examples can be SkRTShader::CreateProc in
SkRuntimeEffect.cpp, make_fuzz_shader in FuzzCanvas.cpp.
2)
In this fuzzer, after creating the paint from a shader, the paint
can be drawn on either GPU canvas or CPU, so a possible way is to
use SkSurface::MakeRenderTarget to create GPU canvas and use a byte
to determine which canvas it will be drawn on.
Change-Id: Ib0385edd0f5ec2f23744aa517135a6955c53ba38
Reviewed-on: https://skia-review.googlesource.com/c/skia/+/300618
Commit-Queue: Zepeng Hu <zepenghu@google.com>
Reviewed-by: Brian Osman <brianosman@google.com>
Reviewed-by: Kevin Lubick <kjlubick@google.com>
2020-07-10 13:36:20 +00:00
|
|
|
if (type.equals("skruntimeeffect")) {
|
|
|
|
fuzz_skruntimeeffect(bytes);
|
|
|
|
return 0;
|
|
|
|
}
|
2018-04-23 20:44:55 +00:00
|
|
|
if (type.equals("sksl2glsl")) {
|
|
|
|
fuzz_sksl2glsl(bytes);
|
|
|
|
return 0;
|
2016-01-21 13:03:28 +00:00
|
|
|
}
|
2019-03-11 15:09:40 +00:00
|
|
|
if (type.equals("sksl2metal")) {
|
|
|
|
fuzz_sksl2metal(bytes);
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
if (type.equals("sksl2spirv")) {
|
|
|
|
fuzz_sksl2spirv(bytes);
|
|
|
|
return 0;
|
|
|
|
}
|
2019-03-18 20:20:55 +00:00
|
|
|
if (type.equals("sksl2pipeline")) {
|
|
|
|
fuzz_sksl2pipeline(bytes);
|
|
|
|
return 0;
|
|
|
|
}
|
2020-06-12 12:20:59 +00:00
|
|
|
if (type.equals("svg_dom")) {
|
|
|
|
fuzz_svg_dom(bytes);
|
|
|
|
return 0;
|
|
|
|
}
|
2019-03-11 15:09:40 +00:00
|
|
|
if (type.equals("textblob")) {
|
|
|
|
fuzz_textblob_deserialize(bytes);
|
|
|
|
return 0;
|
|
|
|
}
|
2018-05-29 13:22:06 +00:00
|
|
|
SkDebugf("Unknown type %s\n", type.c_str());
|
2019-03-20 15:50:33 +00:00
|
|
|
CommandLineFlags::PrintUsage();
|
2018-01-09 17:47:33 +00:00
|
|
|
return 1;
|
2016-01-21 13:03:28 +00:00
|
|
|
}
|
|
|
|
|
2018-04-23 20:44:55 +00:00
|
|
|
static std::map<std::string, std::string> cf_api_map = {
|
2020-07-22 14:37:46 +00:00
|
|
|
{"api_create_ddl", "CreateDDL"},
|
2018-04-23 20:44:55 +00:00
|
|
|
{"api_draw_functions", "DrawFunctions"},
|
|
|
|
{"api_gradients", "Gradients"},
|
|
|
|
{"api_image_filter", "ImageFilter"},
|
|
|
|
{"api_mock_gpu_canvas", "MockGPUCanvas"},
|
|
|
|
{"api_null_canvas", "NullCanvas"},
|
|
|
|
{"api_path_measure", "PathMeasure"},
|
2018-09-24 12:48:19 +00:00
|
|
|
{"api_pathop", "Pathop"},
|
2018-10-11 13:35:15 +00:00
|
|
|
{"api_polyutils", "PolyUtils"},
|
2018-04-23 20:44:55 +00:00
|
|
|
{"api_raster_n32_canvas", "RasterN32Canvas"},
|
2021-02-04 16:09:59 +00:00
|
|
|
{"api_skparagraph", "SkParagraph"},
|
2020-07-01 13:21:03 +00:00
|
|
|
{"api_svg_canvas", "SVGCanvas"},
|
2018-04-23 20:44:55 +00:00
|
|
|
{"jpeg_encoder", "JPEGEncoder"},
|
|
|
|
{"png_encoder", "PNGEncoder"},
|
2018-10-23 13:28:48 +00:00
|
|
|
{"skia_pathop_fuzzer", "LegacyChromiumPathop"},
|
2018-04-23 20:44:55 +00:00
|
|
|
{"webp_encoder", "WEBPEncoder"}
|
|
|
|
};
|
|
|
|
|
2018-06-20 13:29:23 +00:00
|
|
|
// maps clusterfuzz/oss-fuzz -> Skia's name
|
2018-04-23 20:44:55 +00:00
|
|
|
static std::map<std::string, std::string> cf_map = {
|
2018-10-17 14:24:44 +00:00
|
|
|
{"android_codec", "android_codec"},
|
2018-04-23 20:44:55 +00:00
|
|
|
{"animated_image_decode", "animated_image_decode"},
|
|
|
|
{"image_decode", "image_decode"},
|
2018-10-17 14:24:44 +00:00
|
|
|
{"image_decode_incremental", "image_decode_incremental"},
|
2018-04-23 20:44:55 +00:00
|
|
|
{"image_filter_deserialize", "filter_fuzz"},
|
|
|
|
{"image_filter_deserialize_width", "filter_fuzz"},
|
|
|
|
{"path_deserialize", "path_deserialize"},
|
|
|
|
{"region_deserialize", "region_deserialize"},
|
|
|
|
{"region_set_path", "region_set_path"},
|
2019-10-21 17:44:48 +00:00
|
|
|
{"skdescriptor_deserialize", "skdescriptor_deserialize"},
|
2018-06-20 13:29:23 +00:00
|
|
|
{"skjson", "json"},
|
2020-07-31 17:21:29 +00:00
|
|
|
{"skp", "skp"},
|
Add SkRuntimeEffect Fuzzer
The major improvement is that now the fuzzer is able to execute
the sksl code (before it just compiled it). The fuzzer will
reserve 256 bytes for providing uniforms to the shader;
meanwhile, the fuzzer will read the remaining bytes as sksl code
to create SkRuntimeEffect. It then creates a shader and executes
it by painting the shader on a canvas.
The code was tested locally with afl-fuzz, and the execution
speed was around 700/sec.
An alternative implementation would have been using Fuzz.h to
read bytes; I decided to go with sk_sp<SkData> since it has a
comparable format to other binary fuzzer and meets all the
functionality in this fuzzer.
For future changes, there are 2 important improvements to the
implementation:
1) Current shader does not have children shaders; thus,
makeShader() will fail if the SkSL ever tries to use an 'in shader'.
As pointed out in patchset 11, after creating the runtime effect,
effect->children().count() will tell you how many children it's
expecting (how many 'in shader' variables were declared). When you
call makeShader(), the second and third arguments are a
(C-style) array of shader pointers, and
a count (which must match children().count()).
Some helpful examples can be SkRTShader::CreateProc in
SkRuntimeEffect.cpp, make_fuzz_shader in FuzzCanvas.cpp.
2)
In this fuzzer, after creating the paint from a shader, the paint
can be drawn on either GPU canvas or CPU, so a possible way is to
use SkSurface::MakeRenderTarget to create GPU canvas and use a byte
to determine which canvas it will be drawn on.
Change-Id: Ib0385edd0f5ec2f23744aa517135a6955c53ba38
Reviewed-on: https://skia-review.googlesource.com/c/skia/+/300618
Commit-Queue: Zepeng Hu <zepenghu@google.com>
Reviewed-by: Brian Osman <brianosman@google.com>
Reviewed-by: Kevin Lubick <kjlubick@google.com>
2020-07-10 13:36:20 +00:00
|
|
|
{"skruntimeeffect", "skruntimeeffect"},
|
2019-03-11 15:09:40 +00:00
|
|
|
{"sksl2glsl", "sksl2glsl"},
|
|
|
|
{"sksl2metal", "sksl2metal"},
|
|
|
|
{"sksl2spirv", "sksl2spirv"},
|
2019-03-18 20:20:55 +00:00
|
|
|
{"sksl2pipeline", "sksl2pipeline"},
|
2018-09-24 12:48:19 +00:00
|
|
|
#if defined(SK_ENABLE_SKOTTIE)
|
|
|
|
{"skottie_json", "skottie_json"},
|
|
|
|
#endif
|
2020-06-12 12:20:59 +00:00
|
|
|
{"svg_dom", "svg_dom"},
|
2018-04-23 20:44:55 +00:00
|
|
|
{"textblob_deserialize", "textblob"}
|
|
|
|
};
|
|
|
|
|
|
|
|
static SkString try_auto_detect(SkString path, SkString* name) {
|
|
|
|
std::cmatch m;
|
|
|
|
std::regex clusterfuzz("clusterfuzz-testcase(-minimized)?-([a-z0-9_]+)-[\\d]+");
|
|
|
|
std::regex skiafuzzer("(api-)?(\\w+)-[a-f0-9]+");
|
|
|
|
|
|
|
|
if (std::regex_search(path.c_str(), m, clusterfuzz)) {
|
|
|
|
std::string type = m.str(2);
|
2018-06-20 13:29:23 +00:00
|
|
|
|
|
|
|
if (cf_api_map.find(type) != cf_api_map.end()) {
|
|
|
|
*name = SkString(cf_api_map[type].c_str());
|
|
|
|
return SkString("api");
|
2018-04-23 20:44:55 +00:00
|
|
|
} else {
|
|
|
|
if (cf_map.find(type) != cf_map.end()) {
|
|
|
|
return SkString(cf_map[type].c_str());
|
|
|
|
}
|
|
|
|
}
|
|
|
|
} else if (std::regex_search(path.c_str(), m, skiafuzzer)) {
|
|
|
|
std::string a1 = m.str(1);
|
|
|
|
std::string typeOrName = m.str(2);
|
|
|
|
if (a1.length() > 0) {
|
|
|
|
// it's an api fuzzer
|
|
|
|
*name = SkString(typeOrName.c_str());
|
|
|
|
return SkString("api");
|
|
|
|
} else {
|
|
|
|
return SkString(typeOrName.c_str());
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return SkString("");
|
|
|
|
}
|
|
|
|
|
2018-06-19 15:27:20 +00:00
|
|
|
void FuzzJSON(sk_sp<SkData> bytes);
|
2018-06-08 16:25:38 +00:00
|
|
|
|
2018-06-19 15:27:20 +00:00
|
|
|
static void fuzz_json(sk_sp<SkData> bytes){
|
|
|
|
FuzzJSON(bytes);
|
2018-06-08 16:25:38 +00:00
|
|
|
SkDebugf("[terminated] Done parsing!\n");
|
|
|
|
}
|
|
|
|
|
2018-05-04 19:01:03 +00:00
|
|
|
#if defined(SK_ENABLE_SKOTTIE)
|
2018-05-03 20:26:10 +00:00
|
|
|
void FuzzSkottieJSON(sk_sp<SkData> bytes);
|
|
|
|
|
|
|
|
static void fuzz_skottie_json(sk_sp<SkData> bytes){
|
|
|
|
FuzzSkottieJSON(bytes);
|
|
|
|
SkDebugf("[terminated] Done animating!\n");
|
|
|
|
}
|
2018-05-04 19:01:03 +00:00
|
|
|
#endif
|
2018-05-03 20:26:10 +00:00
|
|
|
|
2020-06-12 12:20:59 +00:00
|
|
|
void FuzzSVG(sk_sp<SkData> bytes);
|
|
|
|
static void fuzz_svg_dom(sk_sp<SkData> bytes){
|
|
|
|
FuzzSVG(bytes);
|
|
|
|
SkDebugf("[terminated] Done DOM!\n");
|
|
|
|
}
|
|
|
|
|
2016-02-17 00:14:23 +00:00
|
|
|
// This adds up the first 1024 bytes and returns it as an 8 bit integer. This allows afl-fuzz to
|
|
|
|
// deterministically excercise different paths, or *options* (such as different scaling sizes or
|
|
|
|
// different image modes) without needing to introduce a parameter. This way we don't need a
|
|
|
|
// image_scale1, image_scale2, image_scale4, etc fuzzer, we can just have a image_scale fuzzer.
|
|
|
|
// Clients are expected to transform this number into a different range, e.g. with modulo (%).
|
|
|
|
static uint8_t calculate_option(SkData* bytes) {
|
|
|
|
uint8_t total = 0;
|
|
|
|
const uint8_t* data = bytes->bytes();
|
|
|
|
for (size_t i = 0; i < 1024 && i < bytes->size(); i++) {
|
|
|
|
total += data[i];
|
|
|
|
}
|
|
|
|
return total;
|
|
|
|
}
|
|
|
|
|
2018-04-23 20:44:55 +00:00
|
|
|
static void print_api_names(){
|
|
|
|
SkDebugf("When using --type api, please choose an API to fuzz with --name/-n:\n");
|
2018-07-30 21:07:07 +00:00
|
|
|
for (const Fuzzable& fuzzable : sk_tools::Registry<Fuzzable>::Range()) {
|
2018-04-23 20:44:55 +00:00
|
|
|
SkDebugf("\t%s\n", fuzzable.name);
|
|
|
|
}
|
|
|
|
}
|
2016-01-21 14:13:52 +00:00
|
|
|
|
2018-04-23 20:44:55 +00:00
|
|
|
static void fuzz_api(sk_sp<SkData> bytes, SkString name) {
|
2018-07-30 21:07:07 +00:00
|
|
|
for (const Fuzzable& fuzzable : sk_tools::Registry<Fuzzable>::Range()) {
|
2018-04-23 20:44:55 +00:00
|
|
|
if (name.equals(fuzzable.name)) {
|
2016-01-15 14:19:53 +00:00
|
|
|
SkDebugf("Fuzzing %s...\n", fuzzable.name);
|
2017-03-01 15:42:45 +00:00
|
|
|
Fuzz fuzz(std::move(bytes));
|
2016-01-13 20:57:57 +00:00
|
|
|
fuzzable.fn(&fuzz);
|
2016-02-01 16:23:50 +00:00
|
|
|
SkDebugf("[terminated] Success!\n");
|
2017-01-06 13:26:56 +00:00
|
|
|
return;
|
2016-01-13 20:57:57 +00:00
|
|
|
}
|
|
|
|
}
|
2016-01-21 14:13:52 +00:00
|
|
|
|
2018-04-23 20:44:55 +00:00
|
|
|
print_api_names();
|
2016-01-21 13:03:28 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
static void dump_png(SkBitmap bitmap) {
|
|
|
|
if (!FLAGS_dump.isEmpty()) {
|
2019-03-20 16:12:10 +00:00
|
|
|
ToolUtils::EncodeImageToFile(FLAGS_dump[0], bitmap, SkEncodedImageFormat::kPNG, 100);
|
2016-01-21 13:03:28 +00:00
|
|
|
SkDebugf("Dumped to %s\n", FLAGS_dump[0]);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2018-10-17 14:24:44 +00:00
|
|
|
bool FuzzAnimatedImage(sk_sp<SkData> bytes);
|
2018-02-12 13:26:39 +00:00
|
|
|
|
|
|
|
static void fuzz_animated_img(sk_sp<SkData> bytes) {
|
2018-10-17 14:24:44 +00:00
|
|
|
if (FuzzAnimatedImage(bytes)) {
|
|
|
|
SkDebugf("[terminated] Success from decoding/drawing animated image!\n");
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
SkDebugf("[terminated] Could not decode or draw animated image.\n");
|
|
|
|
}
|
|
|
|
|
|
|
|
bool FuzzImageDecode(sk_sp<SkData> bytes);
|
|
|
|
|
|
|
|
static void fuzz_image_decode(sk_sp<SkData> bytes) {
|
|
|
|
if (FuzzImageDecode(bytes)) {
|
|
|
|
SkDebugf("[terminated] Success from decoding/drawing image!\n");
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
SkDebugf("[terminated] Could not decode or draw image.\n");
|
|
|
|
}
|
|
|
|
|
|
|
|
bool FuzzIncrementalImageDecode(sk_sp<SkData> bytes);
|
|
|
|
|
|
|
|
static void fuzz_image_decode_incremental(sk_sp<SkData> bytes) {
|
|
|
|
if (FuzzIncrementalImageDecode(bytes)) {
|
|
|
|
SkDebugf("[terminated] Success using incremental decode!\n");
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
SkDebugf("[terminated] Could not incrementally decode and image.\n");
|
2018-02-12 13:26:39 +00:00
|
|
|
}
|
|
|
|
|
2018-10-17 14:24:44 +00:00
|
|
|
bool FuzzAndroidCodec(sk_sp<SkData> bytes, uint8_t sampleSize);
|
2018-02-12 13:26:39 +00:00
|
|
|
|
2018-10-17 14:24:44 +00:00
|
|
|
static void fuzz_android_codec(sk_sp<SkData> bytes) {
|
|
|
|
Fuzz fuzz(bytes);
|
|
|
|
uint8_t sampleSize;
|
|
|
|
fuzz.nextRange(&sampleSize, 1, 64);
|
|
|
|
bytes = SkData::MakeSubset(bytes.get(), 1, bytes->size() - 1);
|
|
|
|
if (FuzzAndroidCodec(bytes, sampleSize)) {
|
|
|
|
SkDebugf("[terminated] Success on Android Codec sampleSize=%u!\n", sampleSize);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
SkDebugf("[terminated] Could not use Android Codec sampleSize=%u!\n", sampleSize);
|
2018-02-12 13:26:39 +00:00
|
|
|
}
|
|
|
|
|
2018-10-17 14:24:44 +00:00
|
|
|
// This is a "legacy" fuzzer that likely does too much. It was based off of how
|
|
|
|
// DM reads in images. image_decode, image_decode_incremental and android_codec
|
|
|
|
// are more targeted fuzzers that do a subset of what this one does.
|
2017-01-06 13:26:56 +00:00
|
|
|
static void fuzz_img(sk_sp<SkData> bytes, uint8_t scale, uint8_t mode) {
|
2016-02-17 00:14:23 +00:00
|
|
|
// We can scale 1x, 2x, 4x, 8x, 16x
|
|
|
|
scale = scale % 5;
|
2016-02-18 14:27:38 +00:00
|
|
|
float fscale = (float)pow(2.0f, scale);
|
|
|
|
SkDebugf("Scaling factor: %f\n", fscale);
|
2016-02-17 00:14:23 +00:00
|
|
|
|
2016-12-08 14:07:56 +00:00
|
|
|
// We have 5 different modes of decoding.
|
|
|
|
mode = mode % 5;
|
2016-02-17 00:14:23 +00:00
|
|
|
SkDebugf("Mode: %d\n", mode);
|
|
|
|
|
|
|
|
// This is mostly copied from DMSrcSink's CodecSrc::draw method.
|
2016-02-01 16:23:50 +00:00
|
|
|
SkDebugf("Decoding\n");
|
2017-07-23 19:30:02 +00:00
|
|
|
std::unique_ptr<SkCodec> codec(SkCodec::MakeFromData(bytes));
|
2020-08-16 12:48:02 +00:00
|
|
|
if (nullptr == codec) {
|
2016-02-01 16:23:50 +00:00
|
|
|
SkDebugf("[terminated] Couldn't create codec.\n");
|
2017-01-06 13:26:56 +00:00
|
|
|
return;
|
2016-01-21 13:03:28 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
SkImageInfo decodeInfo = codec->getInfo();
|
2016-02-17 00:14:23 +00:00
|
|
|
SkISize size = codec->getScaledDimensions(fscale);
|
2019-10-03 17:26:54 +00:00
|
|
|
decodeInfo = decodeInfo.makeDimensions(size);
|
2016-02-17 00:14:23 +00:00
|
|
|
|
2016-01-21 13:03:28 +00:00
|
|
|
SkBitmap bitmap;
|
|
|
|
SkCodec::Options options;
|
|
|
|
options.fZeroInitialized = SkCodec::kYes_ZeroInitialized;
|
|
|
|
|
2017-07-18 14:53:11 +00:00
|
|
|
if (!bitmap.tryAllocPixelsFlags(decodeInfo, SkBitmap::kZeroPixels_AllocFlag)) {
|
2016-02-01 16:23:50 +00:00
|
|
|
SkDebugf("[terminated] Could not allocate memory. Image might be too large (%d x %d)",
|
2016-02-17 00:14:23 +00:00
|
|
|
decodeInfo.width(), decodeInfo.height());
|
2017-01-06 13:26:56 +00:00
|
|
|
return;
|
2016-01-21 13:03:28 +00:00
|
|
|
}
|
|
|
|
|
2016-02-17 00:14:23 +00:00
|
|
|
switch (mode) {
|
|
|
|
case 0: {//kCodecZeroInit_Mode, kCodec_Mode
|
2017-07-11 17:35:31 +00:00
|
|
|
switch (codec->getPixels(decodeInfo, bitmap.getPixels(), bitmap.rowBytes(), &options)) {
|
2016-02-17 00:14:23 +00:00
|
|
|
case SkCodec::kSuccess:
|
|
|
|
SkDebugf("[terminated] Success!\n");
|
|
|
|
break;
|
|
|
|
case SkCodec::kIncompleteInput:
|
|
|
|
SkDebugf("[terminated] Partial Success\n");
|
|
|
|
break;
|
2017-07-06 16:26:09 +00:00
|
|
|
case SkCodec::kErrorInInput:
|
|
|
|
SkDebugf("[terminated] Partial Success with error\n");
|
|
|
|
break;
|
2016-02-17 00:14:23 +00:00
|
|
|
case SkCodec::kInvalidConversion:
|
|
|
|
SkDebugf("Incompatible colortype conversion\n");
|
|
|
|
// Crash to allow afl-fuzz to know this was a bug.
|
|
|
|
raise(SIGSEGV);
|
2020-06-11 21:55:07 +00:00
|
|
|
break;
|
2016-02-17 00:14:23 +00:00
|
|
|
default:
|
|
|
|
SkDebugf("[terminated] Couldn't getPixels.\n");
|
2017-01-06 13:26:56 +00:00
|
|
|
return;
|
2016-02-17 00:14:23 +00:00
|
|
|
}
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
case 1: {//kScanline_Mode
|
2017-07-11 17:35:31 +00:00
|
|
|
if (SkCodec::kSuccess != codec->startScanlineDecode(decodeInfo)) {
|
|
|
|
SkDebugf("[terminated] Could not start scanline decoder\n");
|
|
|
|
return;
|
|
|
|
}
|
2016-02-17 00:14:23 +00:00
|
|
|
|
|
|
|
void* dst = bitmap.getAddr(0, 0);
|
|
|
|
size_t rowBytes = bitmap.rowBytes();
|
|
|
|
uint32_t height = decodeInfo.height();
|
2018-04-06 19:28:23 +00:00
|
|
|
// We do not need to check the return value. On an incomplete
|
|
|
|
// image, memory will be filled with a default value.
|
|
|
|
codec->getScanlines(dst, height, rowBytes);
|
2016-02-01 16:23:50 +00:00
|
|
|
SkDebugf("[terminated] Success!\n");
|
2016-01-21 13:03:28 +00:00
|
|
|
break;
|
2016-02-17 00:14:23 +00:00
|
|
|
}
|
|
|
|
case 2: { //kStripe_Mode
|
|
|
|
const int height = decodeInfo.height();
|
|
|
|
// This value is chosen arbitrarily. We exercise more cases by choosing a value that
|
|
|
|
// does not align with image blocks.
|
|
|
|
const int stripeHeight = 37;
|
|
|
|
const int numStripes = (height + stripeHeight - 1) / stripeHeight;
|
|
|
|
|
|
|
|
// Decode odd stripes
|
2017-07-11 17:35:31 +00:00
|
|
|
if (SkCodec::kSuccess != codec->startScanlineDecode(decodeInfo)
|
2016-02-17 00:14:23 +00:00
|
|
|
|| SkCodec::kTopDown_SkScanlineOrder != codec->getScanlineOrder()) {
|
|
|
|
// This mode was designed to test the new skip scanlines API in libjpeg-turbo.
|
|
|
|
// Jpegs have kTopDown_SkScanlineOrder, and at this time, it is not interesting
|
|
|
|
// to run this test for image types that do not have this scanline ordering.
|
|
|
|
SkDebugf("[terminated] Could not start top-down scanline decoder\n");
|
2017-01-06 13:26:56 +00:00
|
|
|
return;
|
2016-02-17 00:14:23 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
for (int i = 0; i < numStripes; i += 2) {
|
|
|
|
// Skip a stripe
|
2020-02-07 15:36:46 +00:00
|
|
|
const int linesToSkip = std::min(stripeHeight, height - i * stripeHeight);
|
2016-02-17 00:14:23 +00:00
|
|
|
codec->skipScanlines(linesToSkip);
|
|
|
|
|
|
|
|
// Read a stripe
|
|
|
|
const int startY = (i + 1) * stripeHeight;
|
2020-02-07 15:36:46 +00:00
|
|
|
const int linesToRead = std::min(stripeHeight, height - startY);
|
2016-02-17 00:14:23 +00:00
|
|
|
if (linesToRead > 0) {
|
|
|
|
codec->getScanlines(bitmap.getAddr(0, startY), linesToRead, bitmap.rowBytes());
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// Decode even stripes
|
2017-07-11 17:35:31 +00:00
|
|
|
const SkCodec::Result startResult = codec->startScanlineDecode(decodeInfo);
|
2016-02-17 00:14:23 +00:00
|
|
|
if (SkCodec::kSuccess != startResult) {
|
|
|
|
SkDebugf("[terminated] Failed to restart scanline decoder with same parameters.\n");
|
2017-01-06 13:26:56 +00:00
|
|
|
return;
|
2016-02-17 00:14:23 +00:00
|
|
|
}
|
|
|
|
for (int i = 0; i < numStripes; i += 2) {
|
|
|
|
// Read a stripe
|
|
|
|
const int startY = i * stripeHeight;
|
2020-02-07 15:36:46 +00:00
|
|
|
const int linesToRead = std::min(stripeHeight, height - startY);
|
2016-02-17 00:14:23 +00:00
|
|
|
codec->getScanlines(bitmap.getAddr(0, startY), linesToRead, bitmap.rowBytes());
|
|
|
|
|
|
|
|
// Skip a stripe
|
2020-02-07 15:36:46 +00:00
|
|
|
const int linesToSkip = std::min(stripeHeight, height - (i + 1) * stripeHeight);
|
2016-02-17 00:14:23 +00:00
|
|
|
if (linesToSkip > 0) {
|
|
|
|
codec->skipScanlines(linesToSkip);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
SkDebugf("[terminated] Success!\n");
|
2016-01-21 13:03:28 +00:00
|
|
|
break;
|
2016-02-17 00:14:23 +00:00
|
|
|
}
|
|
|
|
case 3: { //kSubset_Mode
|
|
|
|
// Arbitrarily choose a divisor.
|
|
|
|
int divisor = 2;
|
|
|
|
// Total width/height of the image.
|
|
|
|
const int W = codec->getInfo().width();
|
|
|
|
const int H = codec->getInfo().height();
|
|
|
|
if (divisor > W || divisor > H) {
|
|
|
|
SkDebugf("[terminated] Cannot codec subset: divisor %d is too big "
|
|
|
|
"with dimensions (%d x %d)\n", divisor, W, H);
|
2017-01-06 13:26:56 +00:00
|
|
|
return;
|
2016-02-17 00:14:23 +00:00
|
|
|
}
|
|
|
|
// subset dimensions
|
|
|
|
// SkWebpCodec, the only one that supports subsets, requires even top/left boundaries.
|
|
|
|
const int w = SkAlign2(W / divisor);
|
|
|
|
const int h = SkAlign2(H / divisor);
|
|
|
|
SkIRect subset;
|
|
|
|
SkCodec::Options opts;
|
|
|
|
opts.fSubset = ⊂
|
|
|
|
SkBitmap subsetBm;
|
|
|
|
// We will reuse pixel memory from bitmap.
|
|
|
|
void* pixels = bitmap.getPixels();
|
|
|
|
// Keep track of left and top (for drawing subsetBm into canvas). We could use
|
|
|
|
// fscale * x and fscale * y, but we want integers such that the next subset will start
|
|
|
|
// where the last one ended. So we'll add decodeInfo.width() and height().
|
|
|
|
int left = 0;
|
|
|
|
for (int x = 0; x < W; x += w) {
|
|
|
|
int top = 0;
|
|
|
|
for (int y = 0; y < H; y+= h) {
|
|
|
|
// Do not make the subset go off the edge of the image.
|
2020-02-07 15:36:46 +00:00
|
|
|
const int preScaleW = std::min(w, W - x);
|
|
|
|
const int preScaleH = std::min(h, H - y);
|
2016-02-17 00:14:23 +00:00
|
|
|
subset.setXYWH(x, y, preScaleW, preScaleH);
|
|
|
|
// And fscale
|
|
|
|
// FIXME: Should we have a version of getScaledDimensions that takes a subset
|
|
|
|
// into account?
|
|
|
|
decodeInfo = decodeInfo.makeWH(
|
2020-02-07 15:36:46 +00:00
|
|
|
std::max(1, SkScalarRoundToInt(preScaleW * fscale)),
|
|
|
|
std::max(1, SkScalarRoundToInt(preScaleH * fscale)));
|
2016-02-17 00:14:23 +00:00
|
|
|
size_t rowBytes = decodeInfo.minRowBytes();
|
2017-07-11 17:35:31 +00:00
|
|
|
if (!subsetBm.installPixels(decodeInfo, pixels, rowBytes)) {
|
2016-02-17 00:14:23 +00:00
|
|
|
SkDebugf("[terminated] Could not install pixels.\n");
|
2017-01-06 13:26:56 +00:00
|
|
|
return;
|
2016-02-17 00:14:23 +00:00
|
|
|
}
|
|
|
|
const SkCodec::Result result = codec->getPixels(decodeInfo, pixels, rowBytes,
|
2017-07-11 17:35:31 +00:00
|
|
|
&opts);
|
2016-02-17 00:14:23 +00:00
|
|
|
switch (result) {
|
|
|
|
case SkCodec::kSuccess:
|
|
|
|
case SkCodec::kIncompleteInput:
|
2017-07-06 16:26:09 +00:00
|
|
|
case SkCodec::kErrorInInput:
|
2016-02-17 00:14:23 +00:00
|
|
|
SkDebugf("okay\n");
|
|
|
|
break;
|
|
|
|
case SkCodec::kInvalidConversion:
|
|
|
|
if (0 == (x|y)) {
|
|
|
|
// First subset is okay to return unimplemented.
|
|
|
|
SkDebugf("[terminated] Incompatible colortype conversion\n");
|
2017-01-06 13:26:56 +00:00
|
|
|
return;
|
2016-02-17 00:14:23 +00:00
|
|
|
}
|
|
|
|
// If the first subset succeeded, a later one should not fail.
|
2020-06-11 21:55:07 +00:00
|
|
|
[[fallthrough]];
|
2016-02-17 00:14:23 +00:00
|
|
|
case SkCodec::kUnimplemented:
|
|
|
|
if (0 == (x|y)) {
|
|
|
|
// First subset is okay to return unimplemented.
|
|
|
|
SkDebugf("[terminated] subset codec not supported\n");
|
2017-01-06 13:26:56 +00:00
|
|
|
return;
|
2016-02-17 00:14:23 +00:00
|
|
|
}
|
|
|
|
// If the first subset succeeded, why would a later one fail?
|
2020-06-11 21:55:07 +00:00
|
|
|
[[fallthrough]];
|
2016-02-17 00:14:23 +00:00
|
|
|
default:
|
|
|
|
SkDebugf("[terminated] subset codec failed to decode (%d, %d, %d, %d) "
|
|
|
|
"with dimensions (%d x %d)\t error %d\n",
|
|
|
|
x, y, decodeInfo.width(), decodeInfo.height(),
|
|
|
|
W, H, result);
|
2017-01-06 13:26:56 +00:00
|
|
|
return;
|
2016-02-17 00:14:23 +00:00
|
|
|
}
|
|
|
|
// translate by the scaled height.
|
|
|
|
top += decodeInfo.height();
|
|
|
|
}
|
|
|
|
// translate by the scaled width.
|
|
|
|
left += decodeInfo.width();
|
|
|
|
}
|
|
|
|
SkDebugf("[terminated] Success!\n");
|
|
|
|
break;
|
|
|
|
}
|
2016-12-08 14:07:56 +00:00
|
|
|
case 4: { //kAnimated_Mode
|
|
|
|
std::vector<SkCodec::FrameInfo> frameInfos = codec->getFrameInfo();
|
|
|
|
if (frameInfos.size() == 0) {
|
|
|
|
SkDebugf("[terminated] Not an animated image\n");
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
for (size_t i = 0; i < frameInfos.size(); i++) {
|
|
|
|
options.fFrameIndex = i;
|
|
|
|
auto result = codec->startIncrementalDecode(decodeInfo, bitmap.getPixels(),
|
|
|
|
bitmap.rowBytes(), &options);
|
|
|
|
if (SkCodec::kSuccess != result) {
|
|
|
|
SkDebugf("[terminated] failed to start incremental decode "
|
|
|
|
"in frame %d with error %d\n", i, result);
|
2017-01-06 13:26:56 +00:00
|
|
|
return;
|
2016-12-08 14:07:56 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
result = codec->incrementalDecode();
|
2017-07-06 16:26:09 +00:00
|
|
|
if (result == SkCodec::kIncompleteInput || result == SkCodec::kErrorInInput) {
|
2016-12-08 14:07:56 +00:00
|
|
|
SkDebugf("okay\n");
|
|
|
|
// Frames beyond this one will not decode.
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
if (result == SkCodec::kSuccess) {
|
|
|
|
SkDebugf("okay - decoded frame %d\n", i);
|
|
|
|
} else {
|
|
|
|
SkDebugf("[terminated] incremental decode failed with "
|
|
|
|
"error %d\n", result);
|
2017-01-06 13:26:56 +00:00
|
|
|
return;
|
2016-12-08 14:07:56 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
SkDebugf("[terminated] Success!\n");
|
|
|
|
break;
|
|
|
|
}
|
2016-01-21 13:03:28 +00:00
|
|
|
default:
|
2016-02-17 00:14:23 +00:00
|
|
|
SkDebugf("[terminated] Mode not implemented yet\n");
|
2016-01-21 13:03:28 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
dump_png(bitmap);
|
2016-01-13 20:57:57 +00:00
|
|
|
}
|
|
|
|
|
2020-07-31 17:21:29 +00:00
|
|
|
void FuzzSKP(sk_sp<SkData> bytes);
|
2017-01-06 13:26:56 +00:00
|
|
|
static void fuzz_skp(sk_sp<SkData> bytes) {
|
2020-07-31 17:21:29 +00:00
|
|
|
FuzzSKP(bytes);
|
|
|
|
SkDebugf("[terminated] Finished SKP\n");
|
2016-01-21 13:03:28 +00:00
|
|
|
}
|
2016-01-13 20:57:57 +00:00
|
|
|
|
2017-01-06 13:26:56 +00:00
|
|
|
static void fuzz_color_deserialize(sk_sp<SkData> bytes) {
|
2016-06-23 17:49:27 +00:00
|
|
|
sk_sp<SkColorSpace> space(SkColorSpace::Deserialize(bytes->data(), bytes->size()));
|
|
|
|
if (!space) {
|
|
|
|
SkDebugf("[terminated] Couldn't deserialize Colorspace.\n");
|
2017-01-06 13:26:56 +00:00
|
|
|
return;
|
2016-06-23 17:49:27 +00:00
|
|
|
}
|
|
|
|
SkDebugf("[terminated] Success! deserialized Colorspace.\n");
|
|
|
|
}
|
|
|
|
|
2018-02-08 19:31:24 +00:00
|
|
|
void FuzzPathDeserialize(SkReadBuffer& buf);
|
|
|
|
|
2017-01-06 18:48:19 +00:00
|
|
|
static void fuzz_path_deserialize(sk_sp<SkData> bytes) {
|
2017-12-06 21:09:20 +00:00
|
|
|
SkReadBuffer buf(bytes->data(), bytes->size());
|
2018-02-08 19:31:24 +00:00
|
|
|
FuzzPathDeserialize(buf);
|
|
|
|
SkDebugf("[terminated] path_deserialize didn't crash!\n");
|
2017-01-06 18:48:19 +00:00
|
|
|
}
|
|
|
|
|
2018-01-11 15:27:14 +00:00
|
|
|
bool FuzzRegionDeserialize(sk_sp<SkData> bytes);
|
|
|
|
|
2017-02-20 22:47:18 +00:00
|
|
|
static void fuzz_region_deserialize(sk_sp<SkData> bytes) {
|
2018-01-11 15:27:14 +00:00
|
|
|
if (!FuzzRegionDeserialize(bytes)) {
|
2017-02-20 22:47:18 +00:00
|
|
|
SkDebugf("[terminated] Couldn't initialize SkRegion.\n");
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
SkDebugf("[terminated] Success! Initialized SkRegion.\n");
|
|
|
|
}
|
|
|
|
|
2018-02-08 19:31:24 +00:00
|
|
|
void FuzzTextBlobDeserialize(SkReadBuffer& buf);
|
|
|
|
|
2017-11-21 22:13:35 +00:00
|
|
|
static void fuzz_textblob_deserialize(sk_sp<SkData> bytes) {
|
2017-12-06 21:09:20 +00:00
|
|
|
SkReadBuffer buf(bytes->data(), bytes->size());
|
2018-02-08 19:31:24 +00:00
|
|
|
FuzzTextBlobDeserialize(buf);
|
|
|
|
SkDebugf("[terminated] textblob didn't crash!\n");
|
2017-11-21 22:13:35 +00:00
|
|
|
}
|
|
|
|
|
2018-01-11 15:27:14 +00:00
|
|
|
void FuzzRegionSetPath(Fuzz* fuzz);
|
|
|
|
|
|
|
|
static void fuzz_region_set_path(sk_sp<SkData> bytes) {
|
|
|
|
Fuzz fuzz(bytes);
|
|
|
|
FuzzRegionSetPath(&fuzz);
|
|
|
|
SkDebugf("[terminated] region_set_path didn't crash!\n");
|
|
|
|
}
|
|
|
|
|
2018-02-08 19:31:24 +00:00
|
|
|
void FuzzImageFilterDeserialize(sk_sp<SkData> bytes);
|
2017-03-16 16:30:43 +00:00
|
|
|
|
2018-02-08 19:31:24 +00:00
|
|
|
static void fuzz_filter_fuzz(sk_sp<SkData> bytes) {
|
|
|
|
FuzzImageFilterDeserialize(bytes);
|
|
|
|
SkDebugf("[terminated] filter_fuzz didn't crash!\n");
|
2017-03-16 16:30:43 +00:00
|
|
|
}
|
|
|
|
|
Add SkRuntimeEffect Fuzzer
The major improvement is that now the fuzzer is able to execute
the sksl code (before it just compiled it). The fuzzer will
reserve 256 bytes for providing uniforms to the shader;
meanwhile, the fuzzer will read the remaining bytes as sksl code
to create SkRuntimeEffect. It then creates a shader and executes
it by painting the shader on a canvas.
The code was tested locally with afl-fuzz, and the execution
speed was around 700/sec.
An alternative implementation would have been using Fuzz.h to
read bytes; I decided to go with sk_sp<SkData> since it has a
comparable format to other binary fuzzer and meets all the
functionality in this fuzzer.
For future changes, there are 2 important improvements to the
implementation:
1) Current shader does not have children shaders; thus,
makeShader() will fail if the SkSL ever tries to use an 'in shader'.
As pointed out in patchset 11, after creating the runtime effect,
effect->children().count() will tell you how many children it's
expecting (how many 'in shader' variables were declared). When you
call makeShader(), the second and third arguments are a
(C-style) array of shader pointers, and
a count (which must match children().count()).
Some helpful examples can be SkRTShader::CreateProc in
SkRuntimeEffect.cpp, make_fuzz_shader in FuzzCanvas.cpp.
2)
In this fuzzer, after creating the paint from a shader, the paint
can be drawn on either GPU canvas or CPU, so a possible way is to
use SkSurface::MakeRenderTarget to create GPU canvas and use a byte
to determine which canvas it will be drawn on.
Change-Id: Ib0385edd0f5ec2f23744aa517135a6955c53ba38
Reviewed-on: https://skia-review.googlesource.com/c/skia/+/300618
Commit-Queue: Zepeng Hu <zepenghu@google.com>
Reviewed-by: Brian Osman <brianosman@google.com>
Reviewed-by: Kevin Lubick <kjlubick@google.com>
2020-07-10 13:36:20 +00:00
|
|
|
bool FuzzSkRuntimeEffect(sk_sp<SkData> bytes);
|
|
|
|
|
|
|
|
static void fuzz_skruntimeeffect(sk_sp<SkData> bytes) {
|
|
|
|
if (FuzzSkRuntimeEffect(bytes)) {
|
|
|
|
SkDebugf("[terminated] Success! Compiled and Executed sksl code.\n");
|
|
|
|
} else {
|
|
|
|
SkDebugf("[terminated] Could not Compile or Execute sksl code.\n");
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-03-11 15:09:40 +00:00
|
|
|
bool FuzzSKSL2GLSL(sk_sp<SkData> bytes);
|
|
|
|
|
2017-01-06 13:26:56 +00:00
|
|
|
static void fuzz_sksl2glsl(sk_sp<SkData> bytes) {
|
2019-03-11 15:09:40 +00:00
|
|
|
if (FuzzSKSL2GLSL(bytes)) {
|
|
|
|
SkDebugf("[terminated] Success! Compiled input to GLSL.\n");
|
|
|
|
} else {
|
|
|
|
SkDebugf("[terminated] Could not compile input to GLSL.\n");
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
bool FuzzSKSL2SPIRV(sk_sp<SkData> bytes);
|
|
|
|
|
|
|
|
static void fuzz_sksl2spirv(sk_sp<SkData> bytes) {
|
|
|
|
if (FuzzSKSL2SPIRV(bytes)) {
|
|
|
|
SkDebugf("[terminated] Success! Compiled input to SPIRV.\n");
|
|
|
|
} else {
|
|
|
|
SkDebugf("[terminated] Could not compile input to SPIRV.\n");
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
bool FuzzSKSL2Metal(sk_sp<SkData> bytes);
|
|
|
|
|
|
|
|
static void fuzz_sksl2metal(sk_sp<SkData> bytes) {
|
|
|
|
if (FuzzSKSL2Metal(bytes)) {
|
2019-03-11 19:38:00 +00:00
|
|
|
SkDebugf("[terminated] Success! Compiled input to Metal.\n");
|
2019-03-11 15:09:40 +00:00
|
|
|
} else {
|
2019-03-11 19:38:00 +00:00
|
|
|
SkDebugf("[terminated] Could not compile input to Metal.\n");
|
2016-10-18 17:06:24 +00:00
|
|
|
}
|
|
|
|
}
|
2019-03-18 20:20:55 +00:00
|
|
|
|
|
|
|
bool FuzzSKSL2Pipeline(sk_sp<SkData> bytes);
|
|
|
|
|
|
|
|
static void fuzz_sksl2pipeline(sk_sp<SkData> bytes) {
|
|
|
|
if (FuzzSKSL2Pipeline(bytes)) {
|
|
|
|
SkDebugf("[terminated] Success! Compiled input to pipeline stage.\n");
|
|
|
|
} else {
|
|
|
|
SkDebugf("[terminated] Could not compile input to pipeline stage.\n");
|
|
|
|
}
|
|
|
|
}
|
2019-10-21 17:44:48 +00:00
|
|
|
|
|
|
|
void FuzzSkDescriptorDeserialize(sk_sp<SkData> bytes);
|
|
|
|
|
|
|
|
static void fuzz_skdescriptor_deserialize(sk_sp<SkData> bytes) {
|
|
|
|
FuzzSkDescriptorDeserialize(bytes);
|
|
|
|
SkDebugf("[terminated] Did not crash while deserializing an SkDescriptor.\n");
|
|
|
|
}
|
|
|
|
|