Skip to content

Commit

Permalink
Add SkRuntimeEffect Fuzzer
Browse files Browse the repository at this point in the history
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>
  • Loading branch information
Zepeng Hu authored and Skia Commit-Bot committed Jul 10, 2020
1 parent 5160e8c commit a5783f3
Show file tree
Hide file tree
Showing 3 changed files with 85 additions and 0 deletions.
1 change: 1 addition & 0 deletions BUILD.gn
Original file line number Diff line number Diff line change
Expand Up @@ -2082,6 +2082,7 @@ if (skia_enable_tools) {
"fuzz/oss_fuzz/FuzzSKSL2SPIRV.cpp",
"fuzz/oss_fuzz/FuzzSVG.cpp",
"fuzz/oss_fuzz/FuzzSkDescriptorDeserialize.cpp",
"fuzz/oss_fuzz/FuzzSkRuntimeEffect.cpp",
"fuzz/oss_fuzz/FuzzTextBlobDeserialize.cpp",
"tools/UrlDataManager.cpp",
"tools/debugger/DebugCanvas.cpp",
Expand Down
17 changes: 17 additions & 0 deletions fuzz/FuzzMain.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,7 @@ static constexpr char g_type_message[] = "How to interpret --bytes, one of:\n"
"region_set_path\n"
"skdescriptor_deserialize\n"
"skp\n"
"skruntimeeffect\n"
"sksl2glsl\n"
"svg_dom\n"
"sksl2metal\n"
Expand Down Expand Up @@ -87,6 +88,7 @@ static void fuzz_region_deserialize(sk_sp<SkData>);
static void fuzz_region_set_path(sk_sp<SkData>);
static void fuzz_skdescriptor_deserialize(sk_sp<SkData>);
static void fuzz_skp(sk_sp<SkData>);
static void fuzz_skruntimeeffect(sk_sp<SkData>);
static void fuzz_sksl2glsl(sk_sp<SkData>);
static void fuzz_sksl2metal(sk_sp<SkData>);
static void fuzz_sksl2pipeline(sk_sp<SkData>);
Expand Down Expand Up @@ -217,6 +219,10 @@ static int fuzz_file(SkString path, SkString type) {
fuzz_skp(bytes);
return 0;
}
if (type.equals("skruntimeeffect")) {
fuzz_skruntimeeffect(bytes);
return 0;
}
if (type.equals("sksl2glsl")) {
fuzz_sksl2glsl(bytes);
return 0;
Expand Down Expand Up @@ -277,6 +283,7 @@ static std::map<std::string, std::string> cf_map = {
{"region_set_path", "region_set_path"},
{"skdescriptor_deserialize", "skdescriptor_deserialize"},
{"skjson", "json"},
{"skruntimeeffect", "skruntimeeffect"},
{"sksl2glsl", "sksl2glsl"},
{"sksl2metal", "sksl2metal"},
{"sksl2spirv", "sksl2spirv"},
Expand Down Expand Up @@ -745,6 +752,16 @@ static void fuzz_filter_fuzz(sk_sp<SkData> bytes) {
SkDebugf("[terminated] filter_fuzz didn't crash!\n");
}

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");
}
}

bool FuzzSKSL2GLSL(sk_sp<SkData> bytes);

static void fuzz_sksl2glsl(sk_sp<SkData> bytes) {
Expand Down
67 changes: 67 additions & 0 deletions fuzz/oss_fuzz/FuzzSkRuntimeEffect.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
/*
* Copyright 2020 Google, LLC
*
* Use of this source code is governed by a BSD-style license that can be
* found in the LICENSE file.
*/

#include "include/core/SkCanvas.h"
#include "include/core/SkPaint.h"
#include "include/core/SkSurface.h"
#include "include/effects/SkRuntimeEffect.h"
#include "src/gpu/GrShaderCaps.h"

#include "fuzz/Fuzz.h"

static constexpr size_t kReservedBytes = 256;
/**
* The fuzzer will take in the bytes and divide into two parts.
* original bytes : [... code bytes ... | 256 bytes]
* The first part is codeBytes, the original bytes minus 256 bytes, which will be treated
* as sksl code, intending to create SkRuntimeEffect.
* For the second part, it will first reserve 256 bytes and then allocate bytes with same size
* as effect->inputSize() to uniformBytes. The uniformBytes is intended to create makeShader().
* Note that if uniformBytes->size() != effect->inputSize() the shader won't be created.
*/
bool FuzzSkRuntimeEffect(sk_sp<SkData> bytes) {
if (bytes->size() < kReservedBytes) {
return false;
}
sk_sp<SkData> codeBytes = SkData::MakeSubset(bytes.get(), 0, bytes->size() - kReservedBytes);

SkRuntimeEffect::EffectResult tuple = SkRuntimeEffect::Make(
SkString((const char*) codeBytes->data(), codeBytes->size())
);
SkRuntimeEffect* effect = std::get<0>(tuple).get();

if (!effect || effect->inputSize() > kReservedBytes) { // if there is not enough uniform bytes
return false;
}
sk_sp<SkData> uniformBytes = SkData::MakeSubset(bytes.get(), bytes->size() - kReservedBytes,
effect->inputSize());
auto shader = effect->makeShader(uniformBytes, nullptr, 0, nullptr, false);
if (!shader) {
return false;
}
SkPaint paint;
paint.setShader(std::move(shader));

sk_sp<SkSurface> s = SkSurface::MakeRasterN32Premul(128, 128);
if (!s) {
return false;
}
s->getCanvas()->drawPaint(paint);

return true;
}

#if defined(IS_FUZZING_WITH_LIBFUZZER)
extern "C" int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size) {
if (size > 3000) {
return 0;
}
auto bytes = SkData::MakeWithoutCopy(data, size);
FuzzSkRuntimeEffect(bytes);
return 0;
}
#endif

0 comments on commit a5783f3

Please sign in to comment.