| /* |
| * 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/ganesh/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, SkSpan(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 optimization disabled... |
| SkRuntimeEffect::Options options; |
| options.forceUnoptimized = true; |
| bool result = FuzzSkRuntimeEffect_Once(bytes, options); |
| |
| // ... and then with optimization enabled. |
| options.forceUnoptimized = 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 |