skia2/fuzz/oss_fuzz/FuzzSkRuntimeEffect.cpp
John Stiles e4d3d3947c Synthesize uniforms/children in the SkRuntimeEffect fuzzer.
Previously, we used a fixed portion of the fuzzer input for uniform
data, and assumed zero children. Now we synthesize uniform and child
inputs based on the needs of the input shader. We no longer need to
reserve the last 256 bytes of each shader for uniform data; a followup
CL will remove the special corpus that this fuzzer used.

The contents of each uniform/child is not particularly important, since
we don't care what the final paint looks like. Each uniform is filled in
with increasing values in each slot, starting at zero (0, 1, 2, 3, ...).
Each child is a simple built-in shader/filter/blender.

(Fuzzer corpus change: https://github.com/google/oss-fuzz/pull/7266 )

Change-Id: Idc8bfcb17c10a470ef35fafc1789b915495e1d67
Bug: skia:12781
Reviewed-on: https://skia-review.googlesource.com/c/skia/+/507924
Reviewed-by: Arman Uguray <armansito@google.com>
Commit-Queue: John Stiles <johnstiles@google.com>
Auto-Submit: John Stiles <johnstiles@google.com>
2022-02-12 01:59:09 +00:00

124 lines
4.6 KiB
C++

/*
* 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/SkColorFilter.h"
#include "include/core/SkPaint.h"
#include "include/core/SkShader.h"
#include "include/core/SkSurface.h"
#include "include/effects/SkBlenders.h"
#include "include/effects/SkRuntimeEffect.h"
#include "src/gpu/GrShaderCaps.h"
#include "fuzz/Fuzz.h"
/**
* The fuzzer treats the input bytes as an SkSL program. The requested number of uniforms and
* children are automatically synthesized to match the program's needs.
*
* We fuzz twice, with two different settings for inlining in the SkSL compiler. By default, the
* compiler inlines most small to medium functions. This can hide bugs related to function-calling.
* So we run the fuzzer once with inlining disabled, and again with it enabled.
* This gives us better coverage, and eases the burden on the fuzzer to inject useless noise into
* functions to suppress inlining.
*/
static bool FuzzSkRuntimeEffect_Once(sk_sp<SkData> codeBytes,
const SkRuntimeEffect::Options& options) {
SkString shaderText{static_cast<const char*>(codeBytes->data()), codeBytes->size()};
SkRuntimeEffect::Result result = SkRuntimeEffect::MakeForShader(shaderText, options);
SkRuntimeEffect* effect = result.effect.get();
if (!effect) {
return false;
}
// Create storage for our uniforms.
sk_sp<SkData> uniformBytes = SkData::MakeZeroInitialized(effect->uniformSize());
void* uniformData = uniformBytes->writable_data();
for (const SkRuntimeEffect::Uniform& u : effect->uniforms()) {
// We treat scalars, vectors, matrices and arrays the same. We just figure out how many
// uniform slots need to be filled, and write consecutive numbers into those slots.
static_assert(sizeof(int) == 4 && sizeof(float) == 4);
size_t numFields = u.sizeInBytes() / 4;
if (u.type == SkRuntimeEffect::Uniform::Type::kInt ||
u.type == SkRuntimeEffect::Uniform::Type::kInt2 ||
u.type == SkRuntimeEffect::Uniform::Type::kInt3 ||
u.type == SkRuntimeEffect::Uniform::Type::kInt4) {
int intVal = 0;
while (numFields--) {
// Assign increasing integer values to each slot (0, 1, 2, ...).
*static_cast<int*>(uniformData) = intVal++;
uniformData = static_cast<int*>(uniformData) + 1;
}
} else {
float floatVal = 0.0f;
while (numFields--) {
// Assign increasing float values to each slot (0.0, 1.0, 2.0, ...).
*static_cast<float*>(uniformData) = floatVal++;
uniformData = static_cast<float*>(uniformData) + 1;
}
}
}
// Create valid children for any requested child effects.
std::vector<SkRuntimeEffect::ChildPtr> children;
children.reserve(effect->children().size());
for (const SkRuntimeEffect::Child& c : effect->children()) {
switch (c.type) {
case SkRuntimeEffect::ChildType::kShader:
children.push_back(SkShaders::Color(SK_ColorRED));
break;
case SkRuntimeEffect::ChildType::kColorFilter:
children.push_back(SkColorFilters::Blend(SK_ColorBLUE, SkBlendMode::kModulate));
break;
case SkRuntimeEffect::ChildType::kBlender:
children.push_back(SkBlenders::Arithmetic(0.50f, 0.25f, 0.10f, 0.05f, false));
break;
}
}
sk_sp<SkShader> shader = effect->makeShader(uniformBytes, SkMakeSpan(children));
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;
}
bool FuzzSkRuntimeEffect(sk_sp<SkData> bytes) {
// Test once with the inliner disabled...
SkRuntimeEffect::Options options;
options.forceNoInline = true;
bool result = FuzzSkRuntimeEffect_Once(bytes, options);
// ... and then with the inliner enabled.
options.forceNoInline = false;
result = FuzzSkRuntimeEffect_Once(bytes, options) || result;
return result;
}
#if defined(SK_BUILD_FOR_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