From 5bd98a244bb1ab1b3cad945e2fa1ce3dfd62d8cf Mon Sep 17 00:00:00 2001 From: kjlubick Date: Thu, 18 Feb 2016 06:27:38 -0800 Subject: Create ParsePath API fuzz This is based on https://codereview.chromium.org/1675053002 BUG=skia:4438 GOLD_TRYBOT_URL= https://gold.skia.org/search2?unt=true&query=source_type%3Dgm&master=false&issue=1702383003 Review URL: https://codereview.chromium.org/1702383003 --- fuzz/Fuzz.h | 10 +++++ fuzz/FuzzParsePath.cpp | 115 +++++++++++++++++++++++++++++++++++++++++++++++++ fuzz/fuzz.cpp | 30 ++++++++++++- 3 files changed, 153 insertions(+), 2 deletions(-) create mode 100644 fuzz/FuzzParsePath.cpp diff --git a/fuzz/Fuzz.h b/fuzz/Fuzz.h index 0f34ef4b3e..26a8d429b3 100644 --- a/fuzz/Fuzz.h +++ b/fuzz/Fuzz.h @@ -16,10 +16,20 @@ class Fuzz : SkNoncopyable { public: explicit Fuzz(SkData*); + bool nextBool(); uint8_t nextB(); uint32_t nextU(); + // This can be nan, +- infinity, 0, anything. float nextF(); + // Return the next fuzzed value [min, max) as an unsigned 32bit integer. + uint32_t nextRangeU(uint32_t min, uint32_t max); + /** + * Returns next fuzzed value [min...max) as a float. + * Will not be Infinity or NaN. + */ + float nextRangeF(float min, float max); + void signalBug (); // Tell afl-fuzz these inputs found a bug. void signalBoring(); // Tell afl-fuzz these inputs are not worth testing. diff --git a/fuzz/FuzzParsePath.cpp b/fuzz/FuzzParsePath.cpp new file mode 100644 index 0000000000..fe1355037d --- /dev/null +++ b/fuzz/FuzzParsePath.cpp @@ -0,0 +1,115 @@ +/* + * Copyright 2016 Google Inc. + * + * Use of this source code is governed by a BSD-style license that can be + * found in the LICENSE file. + */ + +#include "Fuzz.h" +#include "SkString.h" +#include "SkParsePath.h" +#include + +// Most of this is taken from random_parse_path.cpp and adapted to use the Fuzz +// instead of SKRandom + +const struct Legal { + char fSymbol; + int fScalars; +} gLegal[] = { + { 'M', 2 }, + { 'H', 1 }, + { 'V', 1 }, + { 'L', 2 }, + { 'Q', 4 }, + { 'T', 2 }, + { 'C', 6 }, + { 'S', 4 }, + { 'A', 4 }, + { 'Z', 0 }, +}; + +bool gEasy = false; // set to true while debugging to suppress unusual whitespace + +// mostly do nothing, then bias towards spaces +const char gWhiteSpace[] = { 0, 0, 0, 0, 0, 0, 0, 0, ' ', ' ', ' ', ' ', 0x09, 0x0D, 0x0A }; + +static void add_white(Fuzz* fuzz, SkString* atom) { + if (gEasy) { + atom->append(" "); + return; + } + int reps = fuzz->nextRangeU(0, 2); + for (int rep = 0; rep < reps; ++rep) { + int index = fuzz->nextRangeU(0, (int) SK_ARRAY_COUNT(gWhiteSpace) - 1); + if (gWhiteSpace[index]) { + atom->append(&gWhiteSpace[index], 1); + } + } +} + +static void add_comma(Fuzz* fuzz, SkString* atom) { + if (gEasy) { + atom->append(","); + return; + } + size_t count = atom->size(); + add_white(fuzz, atom); + if (fuzz->nextBool()) { + atom->append(","); + } + do { + add_white(fuzz, atom); + } while (count == atom->size()); +} + +static void add_some_white(Fuzz* fuzz, SkString* atom) { + size_t count = atom->size(); + do { + add_white(fuzz, atom); + } while (count == atom->size()); +} + +SkString MakeRandomParsePathPiece(Fuzz* fuzz) { + SkString atom; + int index = fuzz->nextRangeU(0, (int) SK_ARRAY_COUNT(gLegal) - 1); + const Legal& legal = gLegal[index]; + gEasy ? atom.append("\n") : add_white(fuzz, &atom); + char symbol = legal.fSymbol | (fuzz->nextBool() ? 0x20 : 0); + atom.append(&symbol, 1); + int reps = fuzz->nextRangeU(1, 3); + for (int rep = 0; rep < reps; ++rep) { + for (int index = 0; index < legal.fScalars; ++index) { + SkScalar coord = fuzz->nextRangeF(0, 100); + add_white(fuzz, &atom); + atom.appendScalar(coord); + if (rep < reps - 1 && index < legal.fScalars - 1) { + add_comma(fuzz, &atom); + } else { + add_some_white(fuzz, &atom); + } + if ('A' == legal.fSymbol && 1 == index) { + atom.appendScalar(fuzz->nextRangeF(-720, 720)); + add_comma(fuzz, &atom); + atom.appendU32(fuzz->nextRangeU(0, 1)); + add_comma(fuzz, &atom); + atom.appendU32(fuzz->nextRangeU(0, 1)); + add_comma(fuzz, &atom); + } + } + } + return atom; +} + +DEF_FUZZ(ParsePath, fuzz) { + SkPath path; + SkString spec; + uint32_t count = fuzz->nextRangeU(0, 40); + for (uint32_t i = 0; i < count; ++i) { + spec.append(MakeRandomParsePathPiece(fuzz)); + } + SkDebugf("SkParsePath::FromSVGString(%s, &path);\n",spec.c_str()); + if (!SkParsePath::FromSVGString(spec.c_str(), &path)){ + fuzz->signalBug(); + } +} diff --git a/fuzz/fuzz.cpp b/fuzz/fuzz.cpp index 596000d408..dce9c8adbb 100644 --- a/fuzz/fuzz.cpp +++ b/fuzz/fuzz.cpp @@ -17,6 +17,7 @@ #include "SkPicture.h" #include "SkStream.h" +#include #include #include @@ -114,8 +115,8 @@ static void dump_png(SkBitmap bitmap) { int fuzz_img(SkData* bytes, uint8_t scale, uint8_t mode) { // We can scale 1x, 2x, 4x, 8x, 16x scale = scale % 5; - float fscale = pow(2.0f, scale); - SkDebugf("Scaling factor: %d\n", fscale); + float fscale = (float)pow(2.0f, scale); + SkDebugf("Scaling factor: %f\n", fscale); // We have 4 different modes of decoding, just like DM. mode = mode % 4; @@ -393,6 +394,31 @@ T Fuzz::nextT() { } uint8_t Fuzz::nextB() { return this->nextT(); } +bool Fuzz::nextBool() { return nextB()&1; } uint32_t Fuzz::nextU() { return this->nextT(); } float Fuzz::nextF() { return this->nextT(); } + +uint32_t Fuzz::nextRangeU(uint32_t min, uint32_t max) { + if (min > max) { + SkDebugf("Check mins and maxes (%d, %d)\n", min, max); + this->signalBoring(); + } + uint32_t range = max - min + 1; + if (0 == range) { + return this->nextU(); + } else { + return min + this->nextU() % range; + } +} +float Fuzz::nextRangeF(float min, float max) { + if (min > max) { + SkDebugf("Check mins and maxes (%f, %f)\n", min, max); + this->signalBoring(); + } + float f = std::abs(this->nextF()); + if (!std::isnormal(f) && f != 0.0) { + this->signalBoring(); + } + return min + fmod(f, (max - min + 1)); +} -- cgit v1.2.3