1 /*
2 * Copyright 2022 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/SkMesh.h"
9 #include "include/private/base/SkTArray.h"
10
11 #include "fuzz/Fuzz.h"
12
13 using namespace skia_private;
14
15 template <typename T>
extract(SkSpan<const uint8_t> & data)16 T extract(SkSpan<const uint8_t>& data) {
17 T result = 0;
18 size_t bytesToCopy = std::min(sizeof(T), data.size());
19 if (bytesToCopy > 0) {
20 memcpy(&result, &data.front(), bytesToCopy);
21 data = data.subspan(bytesToCopy);
22 }
23 return result;
24 }
25
FuzzSkMeshSpecification(const uint8_t * fuzzData,size_t fuzzSize)26 void FuzzSkMeshSpecification(const uint8_t *fuzzData, size_t fuzzSize) {
27 using Attribute = SkMeshSpecification::Attribute;
28 using Varying = SkMeshSpecification::Varying;
29
30 SkSpan<const uint8_t> data(fuzzData, fuzzSize);
31 STArray<SkMeshSpecification::kMaxAttributes, Attribute> attributes;
32 STArray<SkMeshSpecification::kMaxVaryings, Varying> varyings;
33 size_t vertexStride;
34 SkString vs, fs;
35
36 auto fuzzByteToASCII = [&](uint8_t c, SkString* str) -> bool {
37 // Most control characters (including \0) and all high ASCII are treated as stop bytes.
38 if ((c >= 32 && c <= 127) || c == '\r' || c == '\n' || c == '\t') {
39 char ascii = c;
40 str->append(&ascii, 1);
41 return true;
42 }
43 return false;
44 };
45
46 auto fuzzByteToSkSL = [&](uint8_t c, SkString* str) -> bool {
47 // In the 0x00 - 0x80 range, treat characters as ASCII.
48 if (c < 128) {
49 return fuzzByteToASCII(c, str);
50 }
51 c -= 128;
52
53 // Dedicate a few bytes to injecting our attribute and varying names.
54 if (c < SkMeshSpecification::kMaxAttributes) {
55 if (!attributes.empty()) {
56 str->append(attributes[c % attributes.size()].name);
57 }
58 return true;
59 }
60 c -= SkMeshSpecification::kMaxAttributes;
61
62 if (c < SkMeshSpecification::kMaxVaryings) {
63 if (!varyings.empty()) {
64 str->append(varyings[c % varyings.size()].name);
65 }
66 return true;
67 }
68 c -= SkMeshSpecification::kMaxVaryings;
69
70 // Replace the remaining high-ASCII bytes with valid SkSL operators and keywords in order to
71 // improve our chances of generating a program. (We omit single-character operators since
72 // single-byte versions of those already exist in the low-ASCII space.)
73 static constexpr std::string_view kSkSLData[] = {
74 " true ",
75 " false ",
76 " if ",
77 " else ",
78 " for ",
79 " while ",
80 " do ",
81 " switch ",
82 " case ",
83 " default ",
84 " break ",
85 " continue ",
86 " discard ",
87 " return ",
88 " in ",
89 " out ",
90 " inout ",
91 " uniform ",
92 " const ",
93 " flat ",
94 " noperspective ",
95 " inline ",
96 " noinline ",
97 " $pure ",
98 " readonly ",
99 " writeonly ",
100 " buffer ",
101 " struct ",
102 " layout ",
103 " highp ",
104 " mediump ",
105 " lowp ",
106 " $es3 ",
107 " $export ",
108 " workgroup ",
109 " << ",
110 " >> ",
111 " && ",
112 " || ",
113 " ^^ ",
114 " == ",
115 " != ",
116 " <= ",
117 " >= ",
118 " += ",
119 " -= ",
120 " *= ",
121 " /= ",
122 " %= ",
123 " <<= ",
124 " >>= ",
125 " &= ",
126 " |= ",
127 " ^= ",
128 " ++ ",
129 " -- ",
130 " //",
131 " /*",
132 "*/ ",
133 " float",
134 " half",
135 " int",
136 " uint",
137 " short",
138 " ushort",
139 " bool",
140 " void",
141 " vec",
142 " ivec",
143 " bvec",
144 " mat",
145 " Attributes ",
146 " Varyings ",
147 };
148
149 c %= std::size(kSkSLData);
150 str->append(kSkSLData[c]);
151 return true;
152 };
153
154 // Pick a vertex stride; intentionally allow some bad values through.
155 vertexStride = extract<uint16_t>(data) % (SkMeshSpecification::kMaxStride + 2);
156
157 while (!data.empty()) {
158 uint8_t control = extract<uint8_t>(data) % 4;
159 // A control code with no payload can be ignored.
160 if (data.empty()) {
161 break;
162 }
163 switch (control) {
164 case 0: {
165 // Add an attribute.
166 Attribute& a = attributes.push_back();
167 a.type = (Attribute::Type)(extract<uint8_t>(data) %
168 ((int)Attribute::Type::kLast + 1));
169 a.offset = extract<uint16_t>(data) % (SkMeshSpecification::kMaxStride + 2);
170 while (uint8_t c = extract<char>(data)) {
171 if (!fuzzByteToASCII(c, &a.name)) {
172 break;
173 }
174 }
175 break;
176 }
177 case 1: {
178 // Add a varying.
179 Varying& v = varyings.push_back();
180 v.type = (Varying::Type)(extract<uint8_t>(data) % ((int)Varying::Type::kLast + 1));
181 while (uint8_t c = extract<char>(data)) {
182 if (!fuzzByteToASCII(c, &v.name)) {
183 break;
184 }
185 }
186 break;
187 }
188 case 2: {
189 // Convert the following data into SkSL and add it into the vertex program.
190 while (uint8_t c = extract<char>(data)) {
191 if (!fuzzByteToSkSL(c, &vs)) {
192 break;
193 }
194 }
195 break;
196 }
197 case 3: {
198 // Convert the following data into SkSL and add it into the fragment program.
199 while (uint8_t c = extract<char>(data)) {
200 if (!fuzzByteToSkSL(c, &fs)) {
201 break;
202 }
203 }
204 break;
205 }
206 }
207 }
208
209 auto result = SkMeshSpecification::Make(attributes, vertexStride, varyings, vs, fs);
210 if (result.error.isEmpty()) {
211 // TODO: synthesize a mesh with this specification and paint it.
212 printf("----\n%s\n----\n\n----\n%s\n----\n\n\n", vs.c_str(), fs.c_str());
213 }
214 }
215
216 #if defined(SK_BUILD_FOR_LIBFUZZER)
LLVMFuzzerTestOneInput(const uint8_t * data,size_t size)217 extern "C" int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size) {
218 if (size > 8000) {
219 return 0;
220 }
221 FuzzSkMeshSpecification(data, size);
222 return 0;
223 }
224 #endif
225