| /* | |
| * 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/SkTArray.h" | |
| #include "fuzz/Fuzz.h" | |
| #include <array> | |
| using namespace skia_private; | |
| template <typename T> | |
| T extract(SkSpan<const uint8_t>& data) { | |
| T result = 0; | |
| size_t bytesToCopy = std::min(sizeof(T), data.size()); | |
| if (bytesToCopy > 0) { | |
| memcpy(&result, &data.front(), bytesToCopy); | |
| data = data.subspan(bytesToCopy); | |
| } | |
| return result; | |
| } | |
| void FuzzSkMeshSpecification(const uint8_t *fuzzData, size_t fuzzSize) { | |
| using Attribute = SkMeshSpecification::Attribute; | |
| using Varying = SkMeshSpecification::Varying; | |
| SkSpan<const uint8_t> data(fuzzData, fuzzSize); | |
| STArray<SkMeshSpecification::kMaxAttributes, Attribute> attributes; | |
| STArray<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 auto kSkSLData = std::to_array<std::string_view>({ | |
| " 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; | |
| // A control code with no payload can be ignored. | |
| if (data.empty()) { | |
| break; | |
| } | |
| 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()); | |
| } | |
| } | |
| #if defined(SK_BUILD_FOR_LIBFUZZER) | |
| extern "C" int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size) { | |
| if (size > 8000) { | |
| return 0; | |
| } | |
| FuzzSkMeshSpecification(data, size); | |
| return 0; | |
| } | |
| #endif |