From 811c8b58c515a63691b68aab4b6aad698661fc32 Mon Sep 17 00:00:00 2001 From: Victor Zverovich Date: Wed, 14 Oct 2020 07:13:37 -0700 Subject: [PATCH 1/4] Add float fuzzer and cleanup --- test/fuzzing/CMakeLists.txt | 4 +- test/fuzzing/build.sh | 22 ------- test/fuzzing/chrono-duration.cc | 3 +- test/fuzzing/float.cc | 34 +++++++++++ test/fuzzing/named-arg.cc | 23 ++++---- test/fuzzing/one-arg.cc | 100 ++++++++++++++++---------------- 6 files changed, 98 insertions(+), 88 deletions(-) create mode 100644 test/fuzzing/float.cc 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; } From 271eff149fee0af55e40bbdecb043225f7a1e5f2 Mon Sep 17 00:00:00 2001 From: Bart Siwek Date: Fri, 16 Oct 2020 02:41:56 +0200 Subject: [PATCH 2/4] Make classes derived from buffer final to silence the virtual destructor warning. (#1937) Co-authored-by: Bart Siwek --- include/fmt/core.h | 8 ++++---- include/fmt/format.h | 2 +- include/fmt/os.h | 2 +- test/core-test.cc | 4 ++-- test/format-test.cc | 8 ++------ test/ostream-test.cc | 2 +- 6 files changed, 11 insertions(+), 15 deletions(-) diff --git a/include/fmt/core.h b/include/fmt/core.h index 2e1ae4b6..ee98ea94 100644 --- a/include/fmt/core.h +++ b/include/fmt/core.h @@ -761,7 +761,7 @@ class fixed_buffer_traits { // A buffer that writes to an output iterator when flushed. template -class iterator_buffer : public Traits, public buffer { +class iterator_buffer final : public Traits, public buffer { private: OutputIt out_; enum { buffer_size = 256 }; @@ -787,7 +787,7 @@ class iterator_buffer : public Traits, public buffer { size_t count() const { return Traits::count() + this->size(); } }; -template class iterator_buffer : public buffer { +template class iterator_buffer final : public buffer { protected: void grow(size_t) final FMT_OVERRIDE {} @@ -801,7 +801,7 @@ template class iterator_buffer : public buffer { template class iterator_buffer, enable_if_t::value, - typename Container::value_type>> + typename Container::value_type>> final : public buffer { private: Container& container_; @@ -823,7 +823,7 @@ class iterator_buffer, }; // A buffer that counts the number of code units written discarding the output. -template class counting_buffer : public buffer { +template class counting_buffer final : public buffer { private: enum { buffer_size = 256 }; T data_[buffer_size]; diff --git a/include/fmt/format.h b/include/fmt/format.h index eb9e216a..6dbd12d6 100644 --- a/include/fmt/format.h +++ b/include/fmt/format.h @@ -651,7 +651,7 @@ enum { inline_buffer_size = 500 }; */ template > -class basic_memory_buffer : public detail::buffer { +class basic_memory_buffer final : public detail::buffer { private: T store_[SIZE]; diff --git a/include/fmt/os.h b/include/fmt/os.h index 66e66678..88151006 100644 --- a/include/fmt/os.h +++ b/include/fmt/os.h @@ -378,7 +378,7 @@ struct ostream_params { static constexpr detail::buffer_size buffer_size; // A fast output stream which is not thread-safe. -class ostream : private detail::buffer { +class ostream final : private detail::buffer { private: file file_; diff --git a/test/core-test.cc b/test/core-test.cc index 78d54bb1..9d88070d 100644 --- a/test/core-test.cc +++ b/test/core-test.cc @@ -78,7 +78,7 @@ TEST(BufferTest, Indestructible) { "buffer's destructor is protected"); } -template struct mock_buffer : buffer { +template struct mock_buffer final : buffer { MOCK_METHOD1(do_grow, size_t(size_t capacity)); void grow(size_t capacity) { this->set(this->data(), do_grow(capacity)); } @@ -368,7 +368,7 @@ TEST(ArgTest, PointerArg) { struct check_custom { test_result operator()( fmt::basic_format_arg::handle h) const { - struct test_buffer : fmt::detail::buffer { + struct test_buffer final : fmt::detail::buffer { char data[10]; test_buffer() : fmt::detail::buffer(data, 0, 10) {} void grow(size_t) {} diff --git a/test/format-test.cc b/test/format-test.cc index bf563e17..a3c925b0 100644 --- a/test/format-test.cc +++ b/test/format-test.cc @@ -293,12 +293,8 @@ TEST(MemoryBufferTest, MoveAssignment) { TEST(MemoryBufferTest, Grow) { typedef allocator_ref> Allocator; - typedef basic_memory_buffer Base; mock_allocator alloc; - struct TestMemoryBuffer : Base { - TestMemoryBuffer(Allocator alloc) : Base(alloc) {} - using Base::grow; - } buffer((Allocator(&alloc))); + basic_memory_buffer buffer((Allocator(&alloc))); buffer.resize(7); using fmt::detail::to_unsigned; for (int i = 0; i < 7; ++i) buffer[to_unsigned(i)] = i * i; @@ -306,7 +302,7 @@ TEST(MemoryBufferTest, Grow) { int mem[20]; mem[7] = 0xdead; EXPECT_CALL(alloc, allocate(20)).WillOnce(Return(mem)); - buffer.grow(20); + buffer.try_reserve(20); EXPECT_EQ(20u, buffer.capacity()); // Check if size elements have been copied for (int i = 0; i < 7; ++i) EXPECT_EQ(i * i, buffer[to_unsigned(i)]); diff --git a/test/ostream-test.cc b/test/ostream-test.cc index 407b05a1..ebf14210 100644 --- a/test/ostream-test.cc +++ b/test/ostream-test.cc @@ -150,7 +150,7 @@ TEST(OStreamTest, WriteToOStreamMaxSize) { std::streamsize max_streamsize = fmt::detail::max_value(); if (max_size <= fmt::detail::to_unsigned(max_streamsize)) return; - struct test_buffer : fmt::detail::buffer { + struct test_buffer final : fmt::detail::buffer { explicit test_buffer(size_t size) : fmt::detail::buffer(nullptr, size, size) {} void grow(size_t) {} From 37d738fa6b099072ee17a7634da837d428a35815 Mon Sep 17 00:00:00 2001 From: Victor Zverovich Date: Fri, 16 Oct 2020 06:46:39 -0700 Subject: [PATCH 3/4] Update README.rst --- README.rst | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/README.rst b/README.rst index ad697e6e..a2710eef 100644 --- a/README.rst +++ b/README.rst @@ -7,11 +7,11 @@ .. image:: https://ci.appveyor.com/api/projects/status/ehjkiefde6gucy1v :target: https://ci.appveyor.com/project/vitaut/fmt -.. image:: https://oss-fuzz-build-logs.storage.googleapis.com/badges/libfmt.svg +.. image:: https://oss-fuzz-build-logs.storage.googleapis.com/badges/fmt.svg :alt: fmt is continuously fuzzed at oss-fuzz :target: https://bugs.chromium.org/p/oss-fuzz/issues/list?\ colspec=ID%20Type%20Component%20Status%20Proj%20Reported%20Owner%20\ - Summary&q=proj%3Dlibfmt&can=1 + Summary&q=proj%3Dfmt&can=1 .. image:: https://img.shields.io/badge/stackoverflow-fmt-blue.svg :alt: Ask questions at StackOverflow with the tag fmt @@ -57,7 +57,7 @@ Features * Reliability: the library has an extensive set of `tests `_ and is `continuously fuzzed `_ + Component%20Status%20Proj%20Reported%20Owner%20Summary&q=proj%3Dfmt&can=1>`_ * Safety: the library is fully type safe, errors in format strings can be reported at compile time, automatic memory management prevents buffer overflow errors From 4034715713b0c7e2c41b6b9f326a7152af238b14 Mon Sep 17 00:00:00 2001 From: Victor Zverovich Date: Fri, 16 Oct 2020 06:47:16 -0700 Subject: [PATCH 4/4] Update README.rst --- README.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.rst b/README.rst index a2710eef..9038f883 100644 --- a/README.rst +++ b/README.rst @@ -7,7 +7,7 @@ .. image:: https://ci.appveyor.com/api/projects/status/ehjkiefde6gucy1v :target: https://ci.appveyor.com/project/vitaut/fmt -.. image:: https://oss-fuzz-build-logs.storage.googleapis.com/badges/fmt.svg +.. image:: https://oss-fuzz-build-logs.storage.googleapis.com/badges/libfmt.svg :alt: fmt is continuously fuzzed at oss-fuzz :target: https://bugs.chromium.org/p/oss-fuzz/issues/list?\ colspec=ID%20Type%20Component%20Status%20Proj%20Reported%20Owner%20\