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 <typename Item, typename Ratio> -void invoke_inner(fmt::string_view format_str, Item item) { - auto value = std::chrono::duration<Item, Ratio>(item); +template <typename Period, typename Rep> +void invoke_inner(fmt::string_view format_str, Rep rep) { + auto value = std::chrono::duration<Rep, Period>(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 <typename Item> -void invoke_outer(const uint8_t* data, size_t size, int scaling) { +// Rep is a duration's representation type. +template <typename Rep> +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<Item>(data); - - // Fast forward. - data += nfixed; - size -= nfixed; + const Rep rep = assign_from_buf<Rep>(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<Item, std::atto>(format_str, item); + invoke_inner<std::atto>(format_str, rep); break; case 2: - invoke_inner<Item, std::femto>(format_str, item); + invoke_inner<std::femto>(format_str, rep); break; case 3: - invoke_inner<Item, std::pico>(format_str, item); + invoke_inner<std::pico>(format_str, rep); break; case 4: - invoke_inner<Item, std::nano>(format_str, item); + invoke_inner<std::nano>(format_str, rep); break; case 5: - invoke_inner<Item, std::micro>(format_str, item); + invoke_inner<std::micro>(format_str, rep); break; case 6: - invoke_inner<Item, std::milli>(format_str, item); + invoke_inner<std::milli>(format_str, rep); break; case 7: - invoke_inner<Item, std::centi>(format_str, item); + invoke_inner<std::centi>(format_str, rep); break; case 8: - invoke_inner<Item, std::deci>(format_str, item); + invoke_inner<std::deci>(format_str, rep); break; case 9: - invoke_inner<Item, std::deca>(format_str, item); + invoke_inner<std::deca>(format_str, rep); break; case 10: - invoke_inner<Item, std::kilo>(format_str, item); + invoke_inner<std::kilo>(format_str, rep); break; case 11: - invoke_inner<Item, std::mega>(format_str, item); + invoke_inner<std::mega>(format_str, rep); break; case 12: - invoke_inner<Item, std::giga>(format_str, item); + invoke_inner<std::giga>(format_str, rep); break; case 13: - invoke_inner<Item, std::tera>(format_str, item); + invoke_inner<std::tera>(format_str, rep); break; case 14: - invoke_inner<Item, std::peta>(format_str, item); + invoke_inner<std::peta>(format_str, rep); break; case 15: - invoke_inner<Item, std::exa>(format_str, item); + invoke_inner<std::exa>(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<char>(data, size, scaling); + invoke_outer<char>(data, size, period); break; case 2: - invoke_outer<signed char>(data, size, scaling); + invoke_outer<signed char>(data, size, period); break; case 3: - invoke_outer<unsigned char>(data, size, scaling); + invoke_outer<unsigned char>(data, size, period); break; case 4: - invoke_outer<short>(data, size, scaling); + invoke_outer<short>(data, size, period); break; case 5: - invoke_outer<unsigned short>(data, size, scaling); + invoke_outer<unsigned short>(data, size, period); break; case 6: - invoke_outer<int>(data, size, scaling); + invoke_outer<int>(data, size, period); break; case 7: - invoke_outer<unsigned int>(data, size, scaling); + invoke_outer<unsigned int>(data, size, period); break; case 8: - invoke_outer<long>(data, size, scaling); + invoke_outer<long>(data, size, period); break; case 9: - invoke_outer<unsigned long>(data, size, scaling); + invoke_outer<unsigned long>(data, size, period); break; case 10: - invoke_outer<float>(data, size, scaling); + invoke_outer<float>(data, size, period); break; case 11: - invoke_outer<double>(data, size, scaling); + invoke_outer<double>(data, size, period); break; case 12: - invoke_outer<long double>(data, size, scaling); + invoke_outer<long double>(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 <cstdint> // std::uint8_t #include <cstring> // memcpy +#include <vector> + +#include <fmt/core.h> // 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 <typename T> inline const char* as_chars(const T* data) { @@ -38,17 +39,37 @@ template <typename T> 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 <class Item> inline Item assignFromBuf(const std::uint8_t* data) { +template <class Item> 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<bool>(const std::uint8_t* data) { +template <> inline bool assign_from_buf<bool>(const std::uint8_t* data) { return *data != 0; } -} // namespace fmt_fuzzer +struct data_to_string { +#if FMT_FUZZ_SEPARATE_ALLOCATION + std::vector<char> 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<char> buf(static_cast<size_t>(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 <fmt/chrono.h> + #include <cstdint> #include <type_traits> #include <vector> -#include <fmt/chrono.h> #include "fuzzer-common.h" -template <typename Item1> -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<Item1>(data); +template <typename T> +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<T>(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<char> 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<char> 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 <typename Callback> void invoke(int index, Callback callback) { - switch (index) { +template <typename Callback> 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<decltype(param1)>(data, size, second); + invoke(type, [=](auto arg) { + invoke_fmt<decltype(arg)>(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 <cstdint> -#include <stdexcept> -#include <type_traits> -#include <vector> +#include <exception> #include <fmt/chrono.h> #include "fuzzer-common.h" -using fmt_fuzzer::nfixed; +template <typename T, typename Repr> +const T* from_repr(const Repr& r) { return &r; } -template <typename Item> -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<Item>(data); - data += nfixed; - size -= nfixed; - -#if FMT_FUZZ_SEPARATE_ALLOCATION - std::vector<char> 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<std::tm>(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<Item>(data); - data += nfixed; - size -= nfixed; -#if FMT_FUZZ_SEPARATE_ALLOCATION - std::vector<char> 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 <typename T, typename Repr = T> +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<Repr>(data); + const T* value = from_repr<T>(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<long double>(data, size); break; case 13: - invoke_fmt_time(data, size); + invoke_fmt<std::tm, std::time_t>(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 <typename Item1, typename Item2> 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<Item1>(data); - data += nfixed; - size -= nfixed; + const Item1 item1 = assign_from_buf<Item1>(data); + data += fixed_size; + size -= fixed_size; - const Item2 item2 = fmt_fuzzer::assignFromBuf<Item2>(data); - data += nfixed; - size -= nfixed; - - auto format_str = fmt::string_view(fmt_fuzzer::as_chars(data), size); + const Item2 item2 = assign_from_buf<Item2>(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<decltype(param1), decltype(param2)>(data, size); }); });