diff --git a/test/fuzzing/CMakeLists.txt b/test/fuzzing/CMakeLists.txt index 69632a51..2f716d83 100644 --- a/test/fuzzing/CMakeLists.txt +++ b/test/fuzzing/CMakeLists.txt @@ -9,6 +9,8 @@ option(FMT_FUZZ_LINKMAIN "Enables the reproduce mode, instead of libFuzzer" On) # the fuzz targets, otherwise the CMake configuration step fails. set(FMT_FUZZ_LDFLAGS "" CACHE STRING "LDFLAGS for the fuzz targets") +# Adds a binary for reproducing, i.e. no fuzzing, just enables replaying data +# through the fuzzers. function(add_fuzzer source) get_filename_component(basename ${source} NAME_WE) set(name ${basename}-fuzzer) @@ -23,6 +25,6 @@ function(add_fuzzer source) target_compile_features(${name} PRIVATE cxx_generic_lambdas) endfunction() -foreach (source chrono-duration.cc named-arg.cc one-arg.cc two-args.cc) +foreach (source chrono-duration.cc float.cc named-arg.cc one-arg.cc two-args.cc) add_fuzzer(${source}) endforeach () diff --git a/test/fuzzing/build.sh b/test/fuzzing/build.sh index e54a721d..28c50633 100755 --- a/test/fuzzing/build.sh +++ b/test/fuzzing/build.sh @@ -1,7 +1,6 @@ #!/bin/sh # # Creates fuzzer builds of various kinds -# - reproduce mode (no fuzzing, just enables replaying data through the fuzzers) # - oss-fuzz emulated mode (makes sure a simulated invocation by oss-fuzz works) # - libFuzzer build (you will need clang) # - afl build (you will need afl) @@ -23,15 +22,6 @@ here=$(pwd) CXXFLAGSALL="-DFUZZING_BUILD_MODE_UNSAFE_FOR_PRODUCTION= -g" CMAKEFLAGSALL="$root -GNinja -DCMAKE_BUILD_TYPE=Debug -DFMT_DOC=Off -DFMT_TEST=Off -DFMT_FUZZ=On -DCMAKE_CXX_STANDARD=17" -# Builds the fuzzers as one would do if using afl or just making -# binaries for reproducing. -builddir=$here/build-fuzzers-reproduce -mkdir -p $builddir -cd $builddir -CXX="ccache g++" CXXFLAGS="$CXXFLAGSALL" cmake \ -$CMAKEFLAGSALL -cmake --build $builddir - # For performance analysis of the fuzzers. builddir=$here/build-fuzzers-perfanalysis mkdir -p $builddir @@ -68,18 +58,6 @@ cmake $CMAKEFLAGSALL \ cmake --build $builddir -# Builds fuzzers for local fuzzing with libfuzzer with asan only. -builddir=$here/build-fuzzers-libfuzzer-addr -mkdir -p $builddir -cd $builddir -CXX="clang++" \ -CXXFLAGS="$CXXFLAGSALL -fsanitize=fuzzer-no-link,undefined" cmake \ -cmake $CMAKEFLAGSALL \ --DFMT_FUZZ_LINKMAIN=Off \ --DFMT_FUZZ_LDFLAGS="-fsanitize=fuzzer" - -cmake --build $builddir - # Builds a fast fuzzer for making coverage fast. builddir=$here/build-fuzzers-fast mkdir -p $builddir diff --git a/test/fuzzing/chrono-duration.cc b/test/fuzzing/chrono-duration.cc index 71463a92..fdad9894 100644 --- a/test/fuzzing/chrono-duration.cc +++ b/test/fuzzing/chrono-duration.cc @@ -81,6 +81,7 @@ void invoke_outer(const uint8_t* data, size_t size, int period) { break; case 15: invoke_inner(format_str, rep); + break; } } @@ -129,8 +130,6 @@ extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) { case 12: invoke_outer(data, size, period); break; - default: - break; } return 0; } diff --git a/test/fuzzing/float.cc b/test/fuzzing/float.cc new file mode 100644 index 00000000..1a425690 --- /dev/null +++ b/test/fuzzing/float.cc @@ -0,0 +1,34 @@ +// A fuzzer for floating-point formatter. +// For the license information refer to format.h. + +#include +#include +#include +#include +#include + +#include "fuzzer-common.h" + +extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) { + if (size <= sizeof(double) || !std::numeric_limits::is_iec559) + return 0; + + auto value = assign_from_buf(data); + auto buffer = fmt::memory_buffer(); + fmt::format_to(buffer, "{}", value); + + // Check a round trip. + if (std::isnan(value)) { + auto nan = std::signbit(value) ? "-nan" : "nan"; + if (fmt::string_view(buffer.data(), buffer.size()) != nan) + throw std::runtime_error("round trip failure"); + return 0; + } + buffer.push_back('\0'); + char* ptr = nullptr; + if (std::strtod(buffer.data(), &ptr) != value) + throw std::runtime_error("round trip failure"); + if (ptr != buffer.end()) + throw std::runtime_error("unparsed output"); + return 0; +} diff --git a/test/fuzzing/named-arg.cc b/test/fuzzing/named-arg.cc index 26a9c26a..ffd8e903 100644 --- a/test/fuzzing/named-arg.cc +++ b/test/fuzzing/named-arg.cc @@ -1,11 +1,10 @@ // Copyright (c) 2019, Paul Dreik // For the license information refer to format.h. -#include - #include #include #include +#include #include "fuzzer-common.h" @@ -23,13 +22,16 @@ void invoke_fmt(const uint8_t* data, size_t size, unsigned arg_name_size) { size -= arg_name_size; data_to_string format_str(data, size); + try { #if FMT_FUZZ_FORMAT_TO_STRING - std::string message = - fmt::format(format_str.get(), fmt::arg(arg_name.data(), value)); + 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.get(), fmt::arg(arg_name.data(), value)); + fmt::memory_buffer out; + fmt::format_to(out, format_str.get(), fmt::arg(arg_name.data(), value)); #endif + } catch (std::exception&) { + } } // For dynamic dispatching to an explicit instantiation. @@ -91,11 +93,8 @@ extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) { data++; size--; - try { - invoke(type, [=](auto arg) { - invoke_fmt(data, size, arg_name_size); - }); - } catch (std::exception&) { - } + invoke(type, [=](auto arg) { + invoke_fmt(data, size, arg_name_size); + }); return 0; } diff --git a/test/fuzzing/one-arg.cc b/test/fuzzing/one-arg.cc index c386772a..df173432 100644 --- a/test/fuzzing/one-arg.cc +++ b/test/fuzzing/one-arg.cc @@ -25,12 +25,15 @@ void invoke_fmt(const uint8_t* data, size_t size) { data += fixed_size; size -= fixed_size; data_to_string format_str(data, size); + try { #if FMT_FUZZ_FORMAT_TO_STRING - std::string message = fmt::format(format_str.get(), *value); + std::string message = fmt::format(format_str.get(), *value); #else - fmt::memory_buffer message; - fmt::format_to(message, format_str.get(), *value); + fmt::memory_buffer message; + fmt::format_to(message, format_str.get(), *value); #endif + } catch (std::exception&) { + } } extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) { @@ -40,54 +43,49 @@ extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) { data++; size--; - try { - switch (first) { - case 0: - invoke_fmt(data, size); - break; - case 1: - invoke_fmt(data, size); - break; - case 2: - invoke_fmt(data, size); - break; - case 3: - invoke_fmt(data, size); - break; - case 4: - invoke_fmt(data, size); - break; - case 5: - invoke_fmt(data, size); - break; - case 6: - invoke_fmt(data, size); - break; - case 7: - invoke_fmt(data, size); - break; - case 8: - invoke_fmt(data, size); - break; - case 9: - invoke_fmt(data, size); - break; - case 10: - invoke_fmt(data, size); - break; - case 11: - invoke_fmt(data, size); - break; - case 12: - invoke_fmt(data, size); - break; - case 13: - invoke_fmt(data, size); - break; - default: - break; - } - } catch (std::exception&) { + switch (first) { + case 0: + invoke_fmt(data, size); + break; + case 1: + invoke_fmt(data, size); + break; + case 2: + invoke_fmt(data, size); + break; + case 3: + invoke_fmt(data, size); + break; + case 4: + invoke_fmt(data, size); + break; + case 5: + invoke_fmt(data, size); + break; + case 6: + invoke_fmt(data, size); + break; + case 7: + invoke_fmt(data, size); + break; + case 8: + invoke_fmt(data, size); + break; + case 9: + invoke_fmt(data, size); + break; + case 10: + invoke_fmt(data, size); + break; + case 11: + invoke_fmt(data, size); + break; + case 12: + invoke_fmt(data, size); + break; + case 13: + invoke_fmt(data, size); + break; } return 0; }