diff --git a/BUILD.gn b/BUILD.gn index 369ac06376..eddcf72d2c 100644 --- a/BUILD.gn +++ b/BUILD.gn @@ -1920,6 +1920,7 @@ if (skia_enable_tools) { "fuzz/FuzzPathop.cpp", "fuzz/FuzzPolyUtils.cpp", "fuzz/FuzzRegionOp.cpp", + "fuzz/FuzzSkDescriptor.cpp", "fuzz/oss_fuzz/FuzzAndroidCodec.cpp", "fuzz/oss_fuzz/FuzzAnimatedImage.cpp", "fuzz/oss_fuzz/FuzzImage.cpp", @@ -1933,6 +1934,7 @@ if (skia_enable_tools) { "fuzz/oss_fuzz/FuzzSKSL2Metal.cpp", "fuzz/oss_fuzz/FuzzSKSL2Pipeline.cpp", "fuzz/oss_fuzz/FuzzSKSL2SPIRV.cpp", + "fuzz/oss_fuzz/FuzzSkDescriptorDeserialize.cpp", "fuzz/oss_fuzz/FuzzTextBlobDeserialize.cpp", "tools/UrlDataManager.cpp", "tools/debugger/DebugCanvas.cpp", diff --git a/fuzz/FuzzMain.cpp b/fuzz/FuzzMain.cpp index 923524b07d..456736642d 100644 --- a/fuzz/FuzzMain.cpp +++ b/fuzz/FuzzMain.cpp @@ -55,6 +55,7 @@ static constexpr char g_type_message[] = "How to interpret --bytes, one of:\n" "path_deserialize\n" "region_deserialize\n" "region_set_path\n" + "skdescriptor_deserialize\n" "skp\n" "sksl2glsl\n" "sksl2metal\n" @@ -83,11 +84,12 @@ static void fuzz_json(sk_sp); static void fuzz_path_deserialize(sk_sp); static void fuzz_region_deserialize(sk_sp); static void fuzz_region_set_path(sk_sp); +static void fuzz_skdescriptor_deserialize(sk_sp); static void fuzz_skp(sk_sp); static void fuzz_sksl2glsl(sk_sp); static void fuzz_sksl2metal(sk_sp); -static void fuzz_sksl2spirv(sk_sp); static void fuzz_sksl2pipeline(sk_sp); +static void fuzz_sksl2spirv(sk_sp); static void fuzz_textblob_deserialize(sk_sp); static void print_api_names(); @@ -199,6 +201,10 @@ static int fuzz_file(SkString path, SkString type) { SkDebugf("I would prefer not to.\n"); return 0; } + if (type.equals("skdescriptor_deserialize")) { + fuzz_skdescriptor_deserialize(bytes); + return 0; + } #if defined(SK_ENABLE_SKOTTIE) if (type.equals("skottie_json")) { fuzz_skottie_json(bytes); @@ -244,6 +250,7 @@ static std::map cf_api_map = { {"api_pathop", "Pathop"}, {"api_polyutils", "PolyUtils"}, {"api_raster_n32_canvas", "RasterN32Canvas"}, + {"api_skdescriptor", "SkDescriptor"}, {"jpeg_encoder", "JPEGEncoder"}, {"png_encoder", "PNGEncoder"}, {"skia_pathop_fuzzer", "LegacyChromiumPathop"}, @@ -261,6 +268,7 @@ static std::map cf_map = { {"path_deserialize", "path_deserialize"}, {"region_deserialize", "region_deserialize"}, {"region_set_path", "region_set_path"}, + {"skdescriptor_deserialize", "skdescriptor_deserialize"}, {"skjson", "json"}, {"sksl2glsl", "sksl2glsl"}, {"sksl2metal", "sksl2metal"}, @@ -761,3 +769,11 @@ static void fuzz_sksl2pipeline(sk_sp bytes) { SkDebugf("[terminated] Could not compile input to pipeline stage.\n"); } } + +void FuzzSkDescriptorDeserialize(sk_sp bytes); + +static void fuzz_skdescriptor_deserialize(sk_sp bytes) { + FuzzSkDescriptorDeserialize(bytes); + SkDebugf("[terminated] Did not crash while deserializing an SkDescriptor.\n"); +} + diff --git a/fuzz/FuzzSkDescriptor.cpp b/fuzz/FuzzSkDescriptor.cpp new file mode 100644 index 0000000000..f72f53a012 --- /dev/null +++ b/fuzz/FuzzSkDescriptor.cpp @@ -0,0 +1,77 @@ +/* + * Copyright 2019 Google, LLC + * + * Use of this source code is governed by a BSD-style license that can be + * found in the LICENSE file. + */ + +#include "fuzz/Fuzz.h" +#include "src/core/SkDescriptor.h" +#include "src/core/SkScalerContext.h" + +DEF_FUZZ(SkDescriptor, fuzz) { + int32_t numEntries; + fuzz->next(&numEntries); + + // Limit this to keep the fuzz operations fast. + if (numEntries < 0 || numEntries > 300) { + return; + } + + size_t len = SkDescriptor::ComputeOverhead(numEntries); + auto desc = SkDescriptor::Alloc(len); + for (int32_t i = 0; iexhausted(); i++) { + uint32_t tag; + fuzz->next(&tag); + // Valid use of the API requires that tag is truthy and that + // the length is aligned to 4. If the fuzzed data doesn't conform, + // return to signal that this is "boring" data. + if (!tag) { + return; + } + size_t length; + fuzz->next(&length); + if (SkAlign4(length) != length) { + return; + } + + uint8_t choice; + fuzz->nextRange(&choice, 0, 2); + switch(choice) { + case 0: { // use nullptr + desc->addEntry(tag, length, nullptr); + break; + } + case 1: { // use SkScalerContextRec + SkScalerContextRec rec; + fuzz->next(&rec); + desc->addEntry(tag, sizeof(rec), &rec); + break; + } + case 2: { // use arbitrary data + if (fuzz->remaining() < length) { + // Can't initialize all that we requested, so bail out. + return; + } + uint8_t* bytes = new uint8_t[length]; + fuzz->nextN(bytes, length); + desc->addEntry(tag, length, bytes); + break; + } + default: { + SK_ABORT("Did you update the range in FuzzSkDescriptor?"); + } + } + } + + // Exercise the API to make sure we don't step out of bounds, etc. + + desc->computeChecksum(); + desc->isValid(); + + uint32_t tagToFind; + fuzz->next(&tagToFind); + + uint32_t ignore; + desc->findEntry(tagToFind, &ignore); +} diff --git a/fuzz/oss_fuzz/FuzzAPISkDescriptor.cpp b/fuzz/oss_fuzz/FuzzAPISkDescriptor.cpp new file mode 100644 index 0000000000..e1e76411ce --- /dev/null +++ b/fuzz/oss_fuzz/FuzzAPISkDescriptor.cpp @@ -0,0 +1,18 @@ +/* + * Copyright 2019 Google, LLC + * + * Use of this source code is governed by a BSD-style license that can be + * found in the LICENSE file. + */ + +#include "fuzz/Fuzz.h" + +void fuzz_SkDescriptor(Fuzz* f); + +#if defined(IS_FUZZING_WITH_LIBFUZZER) +extern "C" int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size) { + auto fuzz = Fuzz(SkData::MakeWithoutCopy(data, size)); + fuzz_SkDescriptor(&fuzz); + return 0; +} +#endif diff --git a/fuzz/oss_fuzz/FuzzSkDescriptorDeserialize.cpp b/fuzz/oss_fuzz/FuzzSkDescriptorDeserialize.cpp new file mode 100644 index 0000000000..8dda96dd9d --- /dev/null +++ b/fuzz/oss_fuzz/FuzzSkDescriptorDeserialize.cpp @@ -0,0 +1,36 @@ +/* + * Copyright 2019 Google, LLC + * + * Use of this source code is governed by a BSD-style license that can be + * found in the LICENSE file. + */ + +#include "src/core/SkDescriptor.h" +#include "src/core/SkRemoteGlyphCache.h" + +void FuzzSkDescriptorDeserialize(sk_sp bytes) { + SkAutoDescriptor aDesc; + bool ok = SkFuzzDeserializeSkDescriptor(bytes, &aDesc); + if (!ok) { + return; + } + + auto desc = aDesc.getDesc(); + + desc->computeChecksum(); + desc->isValid(); + + // An arbitrary number + uint32_t tagToFind = 117; + + uint32_t ignore; + desc->findEntry(tagToFind, &ignore); +} + +#if defined(IS_FUZZING_WITH_LIBFUZZER) +extern "C" int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size) { + auto bytes = SkData::MakeWithoutCopy(data, size); + FuzzSkDescriptorDeserialize(&fuzz); + return 0; +} +#endif diff --git a/src/core/SkRemoteGlyphCache.cpp b/src/core/SkRemoteGlyphCache.cpp index 2e0bb3ed17..5842c23379 100644 --- a/src/core/SkRemoteGlyphCache.cpp +++ b/src/core/SkRemoteGlyphCache.cpp @@ -171,6 +171,11 @@ private: size_t fBytesRead = 0u; }; +bool SkFuzzDeserializeSkDescriptor(sk_sp bytes, SkAutoDescriptor* ad) { + auto d = Deserializer(reinterpret_cast(bytes->data()), bytes->size()); + return d.readDescriptor(ad); +} + // Paths use a SkWriter32 which requires 4 byte alignment. static const size_t kPathAlignment = 4u; diff --git a/src/core/SkRemoteGlyphCache.h b/src/core/SkRemoteGlyphCache.h index d81413f55c..3be29b3ab0 100644 --- a/src/core/SkRemoteGlyphCache.h +++ b/src/core/SkRemoteGlyphCache.h @@ -29,6 +29,7 @@ class Deserializer; class Serializer; enum SkAxisAlignment : uint32_t; class SkDescriptor; +class SkAutoDescriptor; class SkStrike; struct SkPackedGlyphID; enum SkScalerContextFlags : uint32_t; @@ -221,4 +222,7 @@ private: const bool fIsLogging; }; +// For exposure to fuzzing only. +bool SkFuzzDeserializeSkDescriptor(sk_sp bytes, SkAutoDescriptor* ad); + #endif // SkRemoteGlyphCache_DEFINED