| /* |
| * Copyright 2022 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/SkMesh.h" |
| #include "include/private/base/SkTArray.h" |
| |
| #include "fuzz/Fuzz.h" |
| |
| template <typename T> |
| T extract(SkSpan<const uint8_t>& data) { |
| T result = 0; |
| size_t bytesToCopy = std::min(sizeof(T), data.size()); |
| memcpy(&result, &data.front(), bytesToCopy); |
| data = data.subspan(bytesToCopy); |
| return result; |
| } |
| |
| static void FuzzSkMeshSpecification(SkSpan<const uint8_t> data) { |
| using Attribute = SkMeshSpecification::Attribute; |
| using Varying = SkMeshSpecification::Varying; |
| |
| SkSTArray<SkMeshSpecification::kMaxAttributes, Attribute> attributes; |
| SkSTArray<SkMeshSpecification::kMaxVaryings, Varying> varyings; |
| size_t vertexStride; |
| SkString vs, fs; |
| |
| auto fuzzByteToASCII = [&](uint8_t c, SkString* str) -> bool { |
| // Most control characters (including \0) and all high ASCII are treated as stop bytes. |
| if ((c >= 32 && c <= 127) || c == '\r' || c == '\n' || c == '\t') { |
| char ascii = c; |
| str->append(&ascii, 1); |
| return true; |
| } |
| return false; |
| }; |
| |
| auto fuzzByteToSkSL = [&](uint8_t c, SkString* str) -> bool { |
| // In the 0x00 - 0x80 range, treat characters as ASCII. |
| if (c < 128) { |
| return fuzzByteToASCII(c, str); |
| } |
| c -= 128; |
| |
| // Dedicate a few bytes to injecting our attribute and varying names. |
| if (c < SkMeshSpecification::kMaxAttributes) { |
| if (!attributes.empty()) { |
| str->append(attributes[c % attributes.size()].name); |
| } |
| return true; |
| } |
| c -= SkMeshSpecification::kMaxAttributes; |
| |
| if (c < SkMeshSpecification::kMaxVaryings) { |
| if (!varyings.empty()) { |
| str->append(varyings[c % varyings.size()].name); |
| } |
| return true; |
| } |
| c -= SkMeshSpecification::kMaxVaryings; |
| |
| // Replace the remaining high-ASCII bytes with valid SkSL operators and keywords in order to |
| // improve our chances of generating a program. (We omit single-character operators since |
| // single-byte versions of those already exist in the low-ASCII space.) |
| static constexpr std::string_view kSkSLData[] = { |
| " true ", |
| " false ", |
| " if ", |
| " else ", |
| " for ", |
| " while ", |
| " do ", |
| " switch ", |
| " case ", |
| " default ", |
| " break ", |
| " continue ", |
| " discard ", |
| " return ", |
| " in ", |
| " out ", |
| " inout ", |
| " uniform ", |
| " const ", |
| " flat ", |
| " noperspective ", |
| " inline ", |
| " noinline ", |
| " $pure ", |
| " readonly ", |
| " writeonly ", |
| " buffer ", |
| " struct ", |
| " layout ", |
| " highp ", |
| " mediump ", |
| " lowp ", |
| " $es3 ", |
| " $export ", |
| " workgroup ", |
| " << ", |
| " >> ", |
| " && ", |
| " || ", |
| " ^^ ", |
| " == ", |
| " != ", |
| " <= ", |
| " >= ", |
| " += ", |
| " -= ", |
| " *= ", |
| " /= ", |
| " %= ", |
| " <<= ", |
| " >>= ", |
| " &= ", |
| " |= ", |
| " ^= ", |
| " ++ ", |
| " -- ", |
| " //", |
| " /*", |
| "*/ ", |
| " float", |
| " half", |
| " int", |
| " uint", |
| " short", |
| " ushort", |
| " bool", |
| " void", |
| " vec", |
| " ivec", |
| " bvec", |
| " mat", |
| " Attributes ", |
| " Varyings ", |
| }; |
| |
| c %= std::size(kSkSLData); |
| str->append(kSkSLData[c]); |
| return true; |
| }; |
| |
| // Pick a vertex stride; intentionally allow some bad values through. |
| vertexStride = extract<uint16_t>(data) % (SkMeshSpecification::kMaxStride + 2); |
| |
| while (!data.empty()) { |
| uint8_t control = extract<uint8_t>(data) % 4; |
| switch (control) { |
| case 0: { |
| // Add an attribute. |
| Attribute& a = attributes.push_back(); |
| a.type = (Attribute::Type)(extract<uint8_t>(data) % |
| ((int)Attribute::Type::kLast + 1)); |
| a.offset = extract<uint16_t>(data) % (SkMeshSpecification::kMaxStride + 2); |
| while (uint8_t c = extract<char>(data)) { |
| if (!fuzzByteToASCII(c, &a.name)) { |
| break; |
| } |
| } |
| break; |
| } |
| case 1: { |
| // Add a varying. |
| Varying& v = varyings.push_back(); |
| v.type = (Varying::Type)(extract<uint8_t>(data) % ((int)Varying::Type::kLast + 1)); |
| while (uint8_t c = extract<char>(data)) { |
| if (!fuzzByteToASCII(c, &v.name)) { |
| break; |
| } |
| } |
| break; |
| } |
| case 2: { |
| // Convert the following data into SkSL and add it into the vertex program. |
| while (uint8_t c = extract<char>(data)) { |
| if (!fuzzByteToSkSL(c, &vs)) { |
| break; |
| } |
| } |
| break; |
| } |
| case 3: { |
| // Convert the following data into SkSL and add it into the fragment program. |
| while (uint8_t c = extract<char>(data)) { |
| if (!fuzzByteToSkSL(c, &fs)) { |
| break; |
| } |
| } |
| break; |
| } |
| } |
| } |
| |
| auto result = SkMeshSpecification::Make(attributes, vertexStride, varyings, vs, fs); |
| if (result.error.isEmpty()) { |
| // TODO: synthesize a mesh with this specification and paint it. |
| printf("----\n%s\n----\n\n----\n%s\n----\n\n\n", vs.c_str(), fs.c_str()); |
| } |
| } |
| |
| bool FuzzSkMeshSpecification(sk_sp<SkData> fuzz) { |
| FuzzSkMeshSpecification(SkSpan(fuzz->bytes(), fuzz->size())); |
| return true; |
| } |
| |
| #if defined(SK_BUILD_FOR_LIBFUZZER) |
| extern "C" int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size) { |
| if (size > 8000) { |
| return 0; |
| } |
| FuzzSkMeshSpecification(SkSpan<const uint8_t>(data, size)); |
| return 0; |
| } |
| #endif |