diff options
author | Dylan Katz <dylan.katz@leviathansecurity.com> | 2020-09-25 16:22:38 -0700 |
---|---|---|
committer | Dylan Katz <dylan.katz@leviathansecurity.com> | 2020-09-25 16:28:49 -0700 |
commit | 98e14de0570fbf66a1f2fd11803837aa83ad1159 (patch) | |
tree | 4f51d763b8746153bc47e568ff435d6cdea0cde3 /libutils/String8_fuzz.cpp | |
parent | 3bede4f44f8b80fd7470c4300aba710d30654db3 (diff) |
Fixes to String8 fuzzer
Adds support for format fuzzing, fixes several bugs.
Fix: 163680603
Fix: 162926178
Fix: 157469647
Test: libutils_fuzz_string8 clusterfuzz-testcase-minimized-libutils_fuzz_string8-6188353572306944
Test: libutils_fuzz_string8 clusterfuzz-testcase-minimized-libutils_fuzz_string8-6413754773012480
Test: libutils_fuzz_string8 clusterfuzz-testcase-minimized-libutils_fuzz_string8-5691524777246720
Signed-off-by: Dylan Katz <dylan.katz@leviathansecurity.com>
Change-Id: I7107c3637a4befccab345e17171a1119f8677557
Diffstat (limited to 'libutils/String8_fuzz.cpp')
-rw-r--r-- | libutils/String8_fuzz.cpp | 214 |
1 files changed, 157 insertions, 57 deletions
diff --git a/libutils/String8_fuzz.cpp b/libutils/String8_fuzz.cpp index 2adfe98b0..b02683c42 100644 --- a/libutils/String8_fuzz.cpp +++ b/libutils/String8_fuzz.cpp @@ -15,97 +15,199 @@ */ #include <functional> #include <iostream> +#include <memory> +#include "FuzzFormatTypes.h" #include "fuzzer/FuzzedDataProvider.h" #include "utils/String8.h" static constexpr int MAX_STRING_BYTES = 256; static constexpr uint8_t MAX_OPERATIONS = 50; +// Interestingly, 2147483614 (INT32_MAX - 33) seems to be the max value that is handled for format +// flags. Unfortunately we need to use a smaller value so we avoid consuming too much memory. -std::vector<std::function<void(FuzzedDataProvider&, android::String8, android::String8)>> +void fuzzFormat(FuzzedDataProvider* dataProvider, android::String8* str1, bool shouldAppend); +std::vector<std::function<void(FuzzedDataProvider*, android::String8*, android::String8*)>> operations = { - // Bytes and size - [](FuzzedDataProvider&, android::String8 str1, android::String8) -> void { - str1.bytes(); - }, - [](FuzzedDataProvider&, android::String8 str1, android::String8) -> void { - str1.isEmpty(); + [](FuzzedDataProvider*, android::String8* str1, android::String8*) -> void { + str1->bytes(); }, - [](FuzzedDataProvider&, android::String8 str1, android::String8) -> void { - str1.length(); + [](FuzzedDataProvider*, android::String8* str1, android::String8*) -> void { + str1->isEmpty(); }, - [](FuzzedDataProvider&, android::String8 str1, android::String8) -> void { - str1.size(); + [](FuzzedDataProvider*, android::String8* str1, android::String8*) -> void { + str1->length(); }, // Casing - [](FuzzedDataProvider&, android::String8 str1, android::String8) -> void { - str1.toUpper(); + [](FuzzedDataProvider*, android::String8* str1, android::String8*) -> void { + str1->toUpper(); }, - [](FuzzedDataProvider&, android::String8 str1, android::String8) -> void { - str1.toLower(); + [](FuzzedDataProvider*, android::String8* str1, android::String8*) -> void { + str1->toLower(); }, - - [](FuzzedDataProvider&, android::String8 str1, android::String8 str2) -> void { - str1.removeAll(str2.c_str()); + [](FuzzedDataProvider*, android::String8* str1, android::String8* str2) -> void { + str1->removeAll(str2->c_str()); }, - [](FuzzedDataProvider&, android::String8 str1, android::String8 str2) -> void { - str1.compare(str2); + [](FuzzedDataProvider*, android::String8* str1, android::String8* str2) -> void { + const android::String8& constRef(*str2); + str1->compare(constRef); }, // Append and format - [](FuzzedDataProvider&, android::String8 str1, android::String8 str2) -> void { - str1.append(str2); - }, - [](FuzzedDataProvider&, android::String8 str1, android::String8 str2) -> void { - str1.appendFormat(str1.c_str(), str2.c_str()); - }, - [](FuzzedDataProvider&, android::String8 str1, android::String8 str2) -> void { - str1.format(str1.c_str(), str2.c_str()); + [](FuzzedDataProvider*, android::String8* str1, android::String8* str2) -> void { + str1->append(str2->c_str()); }, + [](FuzzedDataProvider* dataProvider, android::String8* str1, android::String8*) + -> void { fuzzFormat(dataProvider, str1, dataProvider->ConsumeBool()); }, // Find operation - [](FuzzedDataProvider& dataProvider, android::String8 str1, - android::String8) -> void { + [](FuzzedDataProvider* dataProvider, android::String8* str1, + android::String8* str2) -> void { // We need to get a value from our fuzzer here. - int start_index = dataProvider.ConsumeIntegralInRange<int>(0, str1.size()); - str1.find(str1.c_str(), start_index); + int start_index = dataProvider->ConsumeIntegralInRange<int>(0, str1->size()); + str1->find(str2->c_str(), start_index); }, // Path handling - [](FuzzedDataProvider&, android::String8 str1, android::String8) -> void { - str1.getBasePath(); + [](FuzzedDataProvider*, android::String8* str1, android::String8*) -> void { + str1->getBasePath(); }, - [](FuzzedDataProvider&, android::String8 str1, android::String8) -> void { - str1.getPathExtension(); + [](FuzzedDataProvider*, android::String8* str1, android::String8*) -> void { + str1->getPathExtension(); }, - [](FuzzedDataProvider&, android::String8 str1, android::String8) -> void { - str1.getPathLeaf(); + [](FuzzedDataProvider*, android::String8* str1, android::String8*) -> void { + str1->getPathLeaf(); }, - [](FuzzedDataProvider&, android::String8 str1, android::String8) -> void { - str1.getPathDir(); + [](FuzzedDataProvider*, android::String8* str1, android::String8*) -> void { + str1->getPathDir(); }, - [](FuzzedDataProvider&, android::String8 str1, android::String8) -> void { - str1.convertToResPath(); + [](FuzzedDataProvider*, android::String8* str1, android::String8*) -> void { + str1->convertToResPath(); }, - [](FuzzedDataProvider&, android::String8 str1, android::String8) -> void { - android::String8 path_out_str = android::String8(); - str1.walkPath(&path_out_str); - path_out_str.clear(); + [](FuzzedDataProvider*, android::String8* str1, android::String8*) -> void { + std::shared_ptr<android::String8> path_out_str = + std::make_shared<android::String8>(); + str1->walkPath(path_out_str.get()); + path_out_str->clear(); }, - [](FuzzedDataProvider& dataProvider, android::String8 str1, - android::String8) -> void { - str1.setPathName(dataProvider.ConsumeBytesWithTerminator<char>(5).data()); + [](FuzzedDataProvider* dataProvider, android::String8* str1, + android::String8*) -> void { + str1->setPathName(dataProvider->ConsumeBytesWithTerminator<char>(5).data()); }, - [](FuzzedDataProvider& dataProvider, android::String8 str1, - android::String8) -> void { - str1.appendPath(dataProvider.ConsumeBytesWithTerminator<char>(5).data()); + [](FuzzedDataProvider* dataProvider, android::String8* str1, + android::String8*) -> void { + str1->appendPath(dataProvider->ConsumeBytesWithTerminator<char>(5).data()); }, }; -void callFunc(uint8_t index, FuzzedDataProvider& dataProvider, android::String8 str1, - android::String8 str2) { +void fuzzFormat(FuzzedDataProvider* dataProvider, android::String8* str1, bool shouldAppend) { + FormatChar formatType = dataProvider->ConsumeEnum<FormatChar>(); + + std::string formatString("%"); + // Width specifier + if (dataProvider->ConsumeBool()) { + // Left pad with zeroes + if (dataProvider->ConsumeBool()) { + formatString.push_back('0'); + } + // Right justify (or left justify if negative) + int32_t justify = dataProvider->ConsumeIntegralInRange<int32_t>(-kMaxFormatFlagValue, + kMaxFormatFlagValue); + formatString += std::to_string(justify); + } + + // The # specifier only works with o, x, X, a, A, e, E, f, F, g, and G + if (canApplyFlag(formatType, '#') && dataProvider->ConsumeBool()) { + formatString.push_back('#'); + } + + // Precision specifier + if (canApplyFlag(formatType, '.') && dataProvider->ConsumeBool()) { + formatString.push_back('.'); + formatString += + std::to_string(dataProvider->ConsumeIntegralInRange<int>(0, kMaxFormatFlagValue)); + } + + formatString.push_back(kFormatChars.at(static_cast<uint8_t>(formatType))); + + switch (formatType) { + case SIGNED_DECIMAL: { + int val = dataProvider->ConsumeIntegral<int>(); + if (shouldAppend) { + str1->appendFormat(formatString.c_str(), val); + } else { + str1->format(formatString.c_str(), dataProvider->ConsumeIntegral<int>()); + } + break; + } + + case UNSIGNED_DECIMAL: + case UNSIGNED_OCTAL: + case UNSIGNED_HEX_LOWER: + case UNSIGNED_HEX_UPPER: { + // Unsigned integers for u, o, x, and X + uint val = dataProvider->ConsumeIntegral<uint>(); + if (shouldAppend) { + str1->appendFormat(formatString.c_str(), val); + } else { + str1->format(formatString.c_str(), val); + } + break; + } + + case FLOAT_LOWER: + case FLOAT_UPPER: + case EXPONENT_LOWER: + case EXPONENT_UPPER: + case SHORT_EXP_LOWER: + case SHORT_EXP_UPPER: + case HEX_FLOAT_LOWER: + case HEX_FLOAT_UPPER: { + // Floating points for f, F, e, E, g, G, a, and A + float val = dataProvider->ConsumeFloatingPoint<float>(); + if (shouldAppend) { + str1->appendFormat(formatString.c_str(), val); + } else { + str1->format(formatString.c_str(), val); + } + break; + } + + case CHAR: { + char val = dataProvider->ConsumeIntegral<char>(); + if (shouldAppend) { + str1->appendFormat(formatString.c_str(), val); + } else { + str1->format(formatString.c_str(), val); + } + break; + } + + case STRING: { + std::string val = dataProvider->ConsumeRandomLengthString(MAX_STRING_BYTES); + if (shouldAppend) { + str1->appendFormat(formatString.c_str(), val.c_str()); + } else { + str1->format(formatString.c_str(), val.c_str()); + } + break; + } + case POINTER: { + uintptr_t val = dataProvider->ConsumeIntegral<uintptr_t>(); + if (shouldAppend) { + str1->appendFormat(formatString.c_str(), val); + } else { + str1->format(formatString.c_str(), val); + } + break; + } + } +} + +void callFunc(uint8_t index, FuzzedDataProvider* dataProvider, android::String8* str1, + android::String8* str2) { operations[index](dataProvider, str1, str2); } @@ -120,14 +222,12 @@ extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) { // Create UTF-8 pointers android::String8 str_one_utf8 = android::String8(vec.data()); android::String8 str_two_utf8 = android::String8(vec_two.data()); - // Run operations against strings int opsRun = 0; while (dataProvider.remaining_bytes() > 0 && opsRun++ < MAX_OPERATIONS) { uint8_t op = dataProvider.ConsumeIntegralInRange<uint8_t>(0, operations.size() - 1); - callFunc(op, dataProvider, str_one_utf8, str_two_utf8); + operations[op](&dataProvider, &str_one_utf8, &str_two_utf8); } - // Just to be extra sure these can be freed, we're going to explicitly clear // them str_one_utf8.clear(); |