220 lines
6.8 KiB
C++
220 lines
6.8 KiB
C++
|
|
/*
|
||
|
|
* 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 "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
|