xref: /aosp_15_r20/external/skia/fuzz/oss_fuzz/FuzzSkMeshSpecification.cpp (revision c8dee2aa9b3f27cf6c858bd81872bdeb2c07ed17)
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