From 82c4e2236a28efa3abc189433ce3bf61e4904a9b Mon Sep 17 00:00:00 2001 From: Victor Zverovich Date: Tue, 13 Oct 2020 08:08:48 -0700 Subject: [PATCH] Cleanup fuzzing --- test/fuzzing/chrono-duration.cc | 86 ++++++++++++++++----------------- test/fuzzing/fuzzer-common.h | 33 ++++++++++--- test/fuzzing/main.cc | 2 +- test/fuzzing/named-arg.cc | 60 +++++++++-------------- test/fuzzing/one-arg.cc | 68 ++++++++------------------ test/fuzzing/two-args.cc | 30 ++++++------ 6 files changed, 126 insertions(+), 153 deletions(-) diff --git a/test/fuzzing/chrono-duration.cc b/test/fuzzing/chrono-duration.cc index e8e51934..71463a92 100644 --- a/test/fuzzing/chrono-duration.cc +++ b/test/fuzzing/chrono-duration.cc @@ -6,9 +6,9 @@ #include "fuzzer-common.h" -template -void invoke_inner(fmt::string_view format_str, Item item) { - auto value = std::chrono::duration(item); +template +void invoke_inner(fmt::string_view format_str, Rep rep) { + auto value = std::chrono::duration(rep); try { #if FMT_FUZZ_FORMAT_TO_STRING std::string message = fmt::format(format_str, value); @@ -20,71 +20,67 @@ void invoke_inner(fmt::string_view format_str, Item item) { } } -// Item is the underlying type for duration (int, long, etc.) -template -void invoke_outer(const uint8_t* data, size_t size, int scaling) { +// Rep is a duration's representation type. +template +void invoke_outer(const uint8_t* data, size_t size, int period) { // Always use a fixed location of the data. - using fmt_fuzzer::nfixed; + static_assert(sizeof(Rep) <= fixed_size, "fixed size is too small"); + if (size <= fixed_size + 1) return; - static_assert(sizeof(Item) <= nfixed, "fixed size is too small"); - if (size <= nfixed + 1) return; - - const Item item = fmt_fuzzer::assignFromBuf(data); - - // Fast forward. - data += nfixed; - size -= nfixed; + const Rep rep = assign_from_buf(data); + data += fixed_size; + size -= fixed_size; // data is already allocated separately in libFuzzer so reading past the end // will most likely be detected anyway. - const auto format_str = fmt::string_view(fmt_fuzzer::as_chars(data), size); + const auto format_str = fmt::string_view(as_chars(data), size); // yocto, zepto, zetta and yotta are not handled. - switch (scaling) { + switch (period) { case 1: - invoke_inner(format_str, item); + invoke_inner(format_str, rep); break; case 2: - invoke_inner(format_str, item); + invoke_inner(format_str, rep); break; case 3: - invoke_inner(format_str, item); + invoke_inner(format_str, rep); break; case 4: - invoke_inner(format_str, item); + invoke_inner(format_str, rep); break; case 5: - invoke_inner(format_str, item); + invoke_inner(format_str, rep); break; case 6: - invoke_inner(format_str, item); + invoke_inner(format_str, rep); break; case 7: - invoke_inner(format_str, item); + invoke_inner(format_str, rep); break; case 8: - invoke_inner(format_str, item); + invoke_inner(format_str, rep); break; case 9: - invoke_inner(format_str, item); + invoke_inner(format_str, rep); break; case 10: - invoke_inner(format_str, item); + invoke_inner(format_str, rep); break; case 11: - invoke_inner(format_str, item); + invoke_inner(format_str, rep); break; case 12: - invoke_inner(format_str, item); + invoke_inner(format_str, rep); break; case 13: - invoke_inner(format_str, item); + invoke_inner(format_str, rep); break; case 14: - invoke_inner(format_str, item); + invoke_inner(format_str, rep); break; case 15: - invoke_inner(format_str, item); + invoke_inner(format_str, rep); } } @@ -92,46 +88,46 @@ extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) { if (size <= 4) return 0; const auto representation = data[0]; - const auto scaling = data[1]; + const auto period = data[1]; data += 2; size -= 2; switch (representation) { case 1: - invoke_outer(data, size, scaling); + invoke_outer(data, size, period); break; case 2: - invoke_outer(data, size, scaling); + invoke_outer(data, size, period); break; case 3: - invoke_outer(data, size, scaling); + invoke_outer(data, size, period); break; case 4: - invoke_outer(data, size, scaling); + invoke_outer(data, size, period); break; case 5: - invoke_outer(data, size, scaling); + invoke_outer(data, size, period); break; case 6: - invoke_outer(data, size, scaling); + invoke_outer(data, size, period); break; case 7: - invoke_outer(data, size, scaling); + invoke_outer(data, size, period); break; case 8: - invoke_outer(data, size, scaling); + invoke_outer(data, size, period); break; case 9: - invoke_outer(data, size, scaling); + invoke_outer(data, size, period); break; case 10: - invoke_outer(data, size, scaling); + invoke_outer(data, size, period); break; case 11: - invoke_outer(data, size, scaling); + invoke_outer(data, size, period); break; case 12: - invoke_outer(data, size, scaling); + invoke_outer(data, size, period); break; default: break; diff --git a/test/fuzzing/fuzzer-common.h b/test/fuzzing/fuzzer-common.h index 6aef8d8f..635a5d99 100644 --- a/test/fuzzing/fuzzer-common.h +++ b/test/fuzzing/fuzzer-common.h @@ -6,6 +6,9 @@ #include // std::uint8_t #include // memcpy +#include + +#include // One can format to either a string, or a buffer. The latter is faster, but // one may be interested in formatting to a string instead to verify it works @@ -18,13 +21,11 @@ // the fuzzing. #define FMT_FUZZ_SEPARATE_ALLOCATION 1 -namespace fmt_fuzzer { - // The size of the largest possible type in use. // To let the the fuzzer mutation be efficient at cross pollinating between // different types, use a fixed size format. The same bit pattern, interpreted // as another type, is likely interesting. -constexpr auto nfixed = 16; +constexpr auto fixed_size = 16; // Casts data to a char pointer. template inline const char* as_chars(const T* data) { @@ -38,17 +39,37 @@ template inline const std::uint8_t* as_bytes(const T* data) { // Blits bytes from data to form an (assumed trivially constructible) object // of type Item. -template inline Item assignFromBuf(const std::uint8_t* data) { +template inline Item assign_from_buf(const std::uint8_t* data) { auto item = Item(); std::memcpy(&item, data, sizeof(Item)); return item; } // Reads a boolean value by looking at the first byte from data. -template <> inline bool assignFromBuf(const std::uint8_t* data) { +template <> inline bool assign_from_buf(const std::uint8_t* data) { return *data != 0; } -} // namespace fmt_fuzzer +struct data_to_string { +#if FMT_FUZZ_SEPARATE_ALLOCATION + std::vector buffer; + + data_to_string(const uint8_t* data, size_t size, bool add_terminator = false) + : buffer(size + (add_terminator ? 1 : 0)) { + std::memcpy(buffer.data(), data, size); + } + + fmt::string_view get() const { return {buffer.data(), buffer.size()}; } +#else + fmt::string_view sv; + + data_to_string(const uint8_t* data, size_t size, bool = false) + : str(as_chars(data), size) {} + + fmt::string_view get() const { return sv; } +#endif + + const char* data() const { return get().data(); } +}; #endif // FUZZER_COMMON_H diff --git a/test/fuzzing/main.cc b/test/fuzzing/main.cc index 7fd7cc70..8f8c719b 100644 --- a/test/fuzzing/main.cc +++ b/test/fuzzing/main.cc @@ -17,6 +17,6 @@ int main(int argc, char** argv) { std::vector buf(static_cast(size)); in.read(buf.data(), size); assert(in.gcount() == size); - LLVMFuzzerTestOneInput(fmt_fuzzer::as_bytes(buf.data()), buf.size()); + LLVMFuzzerTestOneInput(as_bytes(buf.data()), buf.size()); } } diff --git a/test/fuzzing/named-arg.cc b/test/fuzzing/named-arg.cc index 9db51555..26a9c26a 100644 --- a/test/fuzzing/named-arg.cc +++ b/test/fuzzing/named-arg.cc @@ -1,54 +1,40 @@ // Copyright (c) 2019, Paul Dreik // For the license information refer to format.h. +#include + #include #include #include -#include #include "fuzzer-common.h" -template -void invoke_fmt(const uint8_t* data, size_t size, unsigned int argsize) { - static_assert(sizeof(Item1) <= fmt_fuzzer::nfixed, "nfixed too small"); - if (size <= fmt_fuzzer::nfixed) return; - const Item1 item1 = fmt_fuzzer::assignFromBuf(data); +template +void invoke_fmt(const uint8_t* data, size_t size, unsigned arg_name_size) { + static_assert(sizeof(T) <= fixed_size, "fixed_size too small"); + if (size <= fixed_size) return; + const T value = assign_from_buf(data); + data += fixed_size; + size -= fixed_size; - data += fmt_fuzzer::nfixed; - size -= fmt_fuzzer::nfixed; - - // How many chars should be used for the argument name? - if (argsize <= 0 || argsize >= size) return; - -#if FMT_FUZZ_SEPARATE_ALLOCATION - std::vector argnamebuffer(argsize + 1); - std::memcpy(argnamebuffer.data(), data, argsize); - auto argname = argnamebuffer.data(); -#else - auto argname = fmt_fuzzer::as_chars(data); -#endif - data += argsize; - size -= argsize; - -#if FMT_FUZZ_SEPARATE_ALLOCATION - std::vector fmtstringbuffer(size); - std::memcpy(fmtstringbuffer.data(), data, size); - auto format_str = fmt::string_view(fmtstringbuffer.data(), size); -#else - auto format_str = fmt::string_view(fmt_fuzzer::as_chars(data), size); -#endif + if (arg_name_size <= 0 || arg_name_size >= size) return; + data_to_string arg_name(data, arg_name_size, true); + data += arg_name_size; + size -= arg_name_size; + data_to_string format_str(data, size); #if FMT_FUZZ_FORMAT_TO_STRING - std::string message = fmt::format(format_str, fmt::arg(argname, item1)); + std::string message = + fmt::format(format_str.get(), fmt::arg(arg_name.data(), value)); #else fmt::memory_buffer out; - fmt::format_to(out, format_str, fmt::arg(argname, item1)); + fmt::format_to(out, format_str.get(), fmt::arg(arg_name.data(), value)); #endif } // For dynamic dispatching to an explicit instantiation. -template void invoke(int index, Callback callback) { - switch (index) { +template void invoke(int type, Callback callback) { + switch (type) { case 0: callback(bool()); break; @@ -100,14 +86,14 @@ extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) { if (size <= 3) return 0; // Switch types depending on the first byte of the input. - const auto first = data[0] & 0x0F; - const unsigned second = (data[0] & 0xF0) >> 4; + const auto type = data[0] & 0x0F; + const unsigned arg_name_size = (data[0] & 0xF0) >> 4; data++; size--; try { - invoke(first, [=](auto param1) { - invoke_fmt(data, size, second); + invoke(type, [=](auto arg) { + invoke_fmt(data, size, arg_name_size); }); } catch (std::exception&) { } diff --git a/test/fuzzing/one-arg.cc b/test/fuzzing/one-arg.cc index 170d3b0a..c386772a 100644 --- a/test/fuzzing/one-arg.cc +++ b/test/fuzzing/one-arg.cc @@ -2,63 +2,35 @@ // For the license information refer to format.h. #include -#include -#include -#include +#include #include #include "fuzzer-common.h" -using fmt_fuzzer::nfixed; +template +const T* from_repr(const Repr& r) { return &r; } -template -void invoke_fmt(const uint8_t* data, size_t size) { - constexpr auto N = sizeof(Item); - static_assert(N <= nfixed, "Nfixed is too small"); - if (size <= nfixed) return; - const Item item = fmt_fuzzer::assignFromBuf(data); - data += nfixed; - size -= nfixed; - -#if FMT_FUZZ_SEPARATE_ALLOCATION - std::vector fmtstringbuffer(size); - std::memcpy(fmtstringbuffer.data(), data, size); - auto format_str = fmt::string_view(fmtstringbuffer.data(), size); -#else - auto format_str = fmt::string_view(fmt_fuzzer::as_chars(data), size); -#endif - -#if FMT_FUZZ_FORMAT_TO_STRING - std::string message = fmt::format(format_str, item); -#else - fmt::memory_buffer message; - fmt::format_to(message, format_str, item); -#endif +template <> +const std::tm* from_repr(const std::time_t& t) { + return std::localtime(&t); } -void invoke_fmt_time(const uint8_t* data, size_t size) { - using Item = std::time_t; - static_assert(sizeof(Item) <= nfixed, "Nfixed too small"); - if (size <= nfixed) return; - const Item item = fmt_fuzzer::assignFromBuf(data); - data += nfixed; - size -= nfixed; -#if FMT_FUZZ_SEPARATE_ALLOCATION - std::vector fmtstringbuffer(size); - std::memcpy(fmtstringbuffer.data(), data, size); - auto format_str = fmt::string_view(fmtstringbuffer.data(), size); -#else - auto format_str = fmt::string_view(fmt_fuzzer::as_chars(data), size); -#endif - auto* b = std::localtime(&item); - if (b) { +template +void invoke_fmt(const uint8_t* data, size_t size) { + static_assert(sizeof(Repr) <= fixed_size, "Nfixed is too small"); + if (size <= fixed_size) return; + auto repr = assign_from_buf(data); + const T* value = from_repr(repr); + if (!value) return; + data += fixed_size; + size -= fixed_size; + data_to_string format_str(data, size); #if FMT_FUZZ_FORMAT_TO_STRING - std::string message = fmt::format(format_str, *b); + std::string message = fmt::format(format_str.get(), *value); #else - fmt::memory_buffer message; - fmt::format_to(message, format_str, *b); + fmt::memory_buffer message; + fmt::format_to(message, format_str.get(), *value); #endif - } } extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) { @@ -110,7 +82,7 @@ extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) { invoke_fmt(data, size); break; case 13: - invoke_fmt_time(data, size); + invoke_fmt(data, size); break; default: break; diff --git a/test/fuzzing/two-args.cc b/test/fuzzing/two-args.cc index bcbd033a..4d7d3453 100644 --- a/test/fuzzing/two-args.cc +++ b/test/fuzzing/two-args.cc @@ -10,21 +10,19 @@ template void invoke_fmt(const uint8_t* data, size_t size) { - using fmt_fuzzer::nfixed; - static_assert(sizeof(Item1) <= nfixed, "size1 exceeded"); - static_assert(sizeof(Item2) <= nfixed, "size2 exceeded"); - if (size <= nfixed + nfixed) return; + static_assert(sizeof(Item1) <= fixed_size, "size1 exceeded"); + static_assert(sizeof(Item2) <= fixed_size, "size2 exceeded"); + if (size <= fixed_size + fixed_size) return; - const Item1 item1 = fmt_fuzzer::assignFromBuf(data); - data += nfixed; - size -= nfixed; + const Item1 item1 = assign_from_buf(data); + data += fixed_size; + size -= fixed_size; - const Item2 item2 = fmt_fuzzer::assignFromBuf(data); - data += nfixed; - size -= nfixed; - - auto format_str = fmt::string_view(fmt_fuzzer::as_chars(data), size); + const Item2 item2 = assign_from_buf(data); + data += fixed_size; + size -= fixed_size; + auto format_str = fmt::string_view(as_chars(data), size); #if FMT_FUZZ_FORMAT_TO_STRING std::string message = fmt::format(format_str, item1, item2); #else @@ -91,13 +89,13 @@ extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) { if (size <= 3) return 0; // Switch types depending on the first byte of the input. - const auto first = data[0] & 0x0F; - const auto second = (data[0] & 0xF0) >> 4; + const auto type1 = data[0] & 0x0F; + const auto type2 = (data[0] & 0xF0) >> 4; data++; size--; try { - invoke(first, [=](auto param1) { - invoke(second, [=](auto param2) { + invoke(type1, [=](auto param1) { + invoke(type2, [=](auto param2) { invoke_fmt(data, size); }); });