1 /*
2 * Copyright 2020 Google, LLC
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 "include/core/SkCanvas.h"
9 #include "include/core/SkColorFilter.h"
10 #include "include/core/SkPaint.h"
11 #include "include/core/SkShader.h"
12 #include "include/core/SkSurface.h"
13 #include "include/effects/SkBlenders.h"
14 #include "include/effects/SkRuntimeEffect.h"
15 #include "src/gpu/GrShaderCaps.h"
16
17 #include "fuzz/Fuzz.h"
18
19 /**
20 * The fuzzer treats the input bytes as an SkSL program. The requested number of uniforms and
21 * children are automatically synthesized to match the program's needs.
22 *
23 * We fuzz twice, with two different settings for inlining in the SkSL compiler. By default, the
24 * compiler inlines most small to medium functions. This can hide bugs related to function-calling.
25 * So we run the fuzzer once with inlining disabled, and again with it enabled.
26 * This gives us better coverage, and eases the burden on the fuzzer to inject useless noise into
27 * functions to suppress inlining.
28 */
FuzzSkRuntimeEffect_Once(sk_sp<SkData> codeBytes,const SkRuntimeEffect::Options & options)29 static bool FuzzSkRuntimeEffect_Once(sk_sp<SkData> codeBytes,
30 const SkRuntimeEffect::Options& options) {
31 SkString shaderText{static_cast<const char*>(codeBytes->data()), codeBytes->size()};
32 SkRuntimeEffect::Result result = SkRuntimeEffect::MakeForShader(shaderText, options);
33 SkRuntimeEffect* effect = result.effect.get();
34 if (!effect) {
35 return false;
36 }
37
38 // Create storage for our uniforms.
39 sk_sp<SkData> uniformBytes = SkData::MakeZeroInitialized(effect->uniformSize());
40 void* uniformData = uniformBytes->writable_data();
41
42 for (const SkRuntimeEffect::Uniform& u : effect->uniforms()) {
43 // We treat scalars, vectors, matrices and arrays the same. We just figure out how many
44 // uniform slots need to be filled, and write consecutive numbers into those slots.
45 static_assert(sizeof(int) == 4 && sizeof(float) == 4);
46 size_t numFields = u.sizeInBytes() / 4;
47
48 if (u.type == SkRuntimeEffect::Uniform::Type::kInt ||
49 u.type == SkRuntimeEffect::Uniform::Type::kInt2 ||
50 u.type == SkRuntimeEffect::Uniform::Type::kInt3 ||
51 u.type == SkRuntimeEffect::Uniform::Type::kInt4) {
52 int intVal = 0;
53 while (numFields--) {
54 // Assign increasing integer values to each slot (0, 1, 2, ...).
55 *static_cast<int*>(uniformData) = intVal++;
56 uniformData = static_cast<int*>(uniformData) + 1;
57 }
58 } else {
59 float floatVal = 0.0f;
60 while (numFields--) {
61 // Assign increasing float values to each slot (0.0, 1.0, 2.0, ...).
62 *static_cast<float*>(uniformData) = floatVal++;
63 uniformData = static_cast<float*>(uniformData) + 1;
64 }
65 }
66 }
67
68 // Create valid children for any requested child effects.
69 std::vector<SkRuntimeEffect::ChildPtr> children;
70 children.reserve(effect->children().size());
71 for (const SkRuntimeEffect::Child& c : effect->children()) {
72 switch (c.type) {
73 case SkRuntimeEffect::ChildType::kShader:
74 children.push_back(SkShaders::Color(SK_ColorRED));
75 break;
76 case SkRuntimeEffect::ChildType::kColorFilter:
77 children.push_back(SkColorFilters::Blend(SK_ColorBLUE, SkBlendMode::kModulate));
78 break;
79 case SkRuntimeEffect::ChildType::kBlender:
80 children.push_back(SkBlenders::Arithmetic(0.50f, 0.25f, 0.10f, 0.05f, false));
81 break;
82 }
83 }
84
85 sk_sp<SkShader> shader = effect->makeShader(uniformBytes, SkMakeSpan(children));
86 if (!shader) {
87 return false;
88 }
89 SkPaint paint;
90 paint.setShader(std::move(shader));
91
92 sk_sp<SkSurface> s = SkSurface::MakeRasterN32Premul(128, 128);
93 if (!s) {
94 return false;
95 }
96 s->getCanvas()->drawPaint(paint);
97
98 return true;
99 }
100
FuzzSkRuntimeEffect(sk_sp<SkData> bytes)101 bool FuzzSkRuntimeEffect(sk_sp<SkData> bytes) {
102 // Test once with the inliner disabled...
103 SkRuntimeEffect::Options options;
104 options.forceNoInline = true;
105 bool result = FuzzSkRuntimeEffect_Once(bytes, options);
106
107 // ... and then with the inliner enabled.
108 options.forceNoInline = false;
109 result = FuzzSkRuntimeEffect_Once(bytes, options) || result;
110
111 return result;
112 }
113
114 #if defined(SK_BUILD_FOR_LIBFUZZER)
LLVMFuzzerTestOneInput(const uint8_t * data,size_t size)115 extern "C" int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size) {
116 if (size > 3000) {
117 return 0;
118 }
119 auto bytes = SkData::MakeWithoutCopy(data, size);
120 FuzzSkRuntimeEffect(bytes);
121 return 0;
122 }
123 #endif
124