summaryrefslogtreecommitdiffstats
path: root/src/fmt/test/fuzzing
diff options
context:
space:
mode:
authorDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-07 18:45:59 +0000
committerDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-07 18:45:59 +0000
commit19fcec84d8d7d21e796c7624e521b60d28ee21ed (patch)
tree42d26aa27d1e3f7c0b8bd3fd14e7d7082f5008dc /src/fmt/test/fuzzing
parentInitial commit. (diff)
downloadceph-19fcec84d8d7d21e796c7624e521b60d28ee21ed.tar.xz
ceph-19fcec84d8d7d21e796c7624e521b60d28ee21ed.zip
Adding upstream version 16.2.11+ds.upstream/16.2.11+dsupstream
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'src/fmt/test/fuzzing')
-rw-r--r--src/fmt/test/fuzzing/.gitignore3
-rw-r--r--src/fmt/test/fuzzing/CMakeLists.txt38
-rw-r--r--src/fmt/test/fuzzing/README.md48
-rwxr-xr-xsrc/fmt/test/fuzzing/build.sh110
-rw-r--r--src/fmt/test/fuzzing/chrono_duration.cpp152
-rw-r--r--src/fmt/test/fuzzing/fuzzer_common.h67
-rw-r--r--src/fmt/test/fuzzing/main.cpp21
-rw-r--r--src/fmt/test/fuzzing/named_arg.cpp128
-rw-r--r--src/fmt/test/fuzzing/one_arg.cpp131
-rw-r--r--src/fmt/test/fuzzing/sprintf.cpp116
-rw-r--r--src/fmt/test/fuzzing/two_args.cpp112
11 files changed, 926 insertions, 0 deletions
diff --git a/src/fmt/test/fuzzing/.gitignore b/src/fmt/test/fuzzing/.gitignore
new file mode 100644
index 000000000..ea4104026
--- /dev/null
+++ b/src/fmt/test/fuzzing/.gitignore
@@ -0,0 +1,3 @@
+# ignore artifacts from the build.sh script
+build-*/
+
diff --git a/src/fmt/test/fuzzing/CMakeLists.txt b/src/fmt/test/fuzzing/CMakeLists.txt
new file mode 100644
index 000000000..31344fc5c
--- /dev/null
+++ b/src/fmt/test/fuzzing/CMakeLists.txt
@@ -0,0 +1,38 @@
+# Copyright (c) 2019, Paul Dreik
+# License: see LICENSE.rst in the fmt root directory
+
+# settings this links in a main. useful for reproducing,
+# kcov, gdb, afl, valgrind.
+# (note that libFuzzer can also reproduce, just pass it the files)
+option(FMT_FUZZ_LINKMAIN "enables the reproduce mode, instead of libFuzzer" On)
+
+# For oss-fuzz - insert $LIB_FUZZING_ENGINE into the link flags, but only for
+# the fuzz targets, otherwise the cmake configuration step fails.
+set(FMT_FUZZ_LDFLAGS "" CACHE STRING "LDFLAGS for the fuzz targets")
+
+# Find all fuzzers.
+set(SOURCES
+ chrono_duration.cpp
+ named_arg.cpp
+ one_arg.cpp
+ sprintf.cpp
+ two_args.cpp
+)
+
+macro(implement_fuzzer sourcefile)
+ get_filename_component(basename ${sourcefile} NAME_WE)
+ set(name fuzzer_${basename})
+ add_executable(${name} ${sourcefile} fuzzer_common.h)
+ if (FMT_FUZZ_LINKMAIN)
+ target_sources(${name} PRIVATE main.cpp)
+ endif ()
+ target_link_libraries(${name} PRIVATE fmt)
+if (FMT_FUZZ_LDFLAGS)
+ target_link_libraries(${name} PRIVATE ${FMT_FUZZ_LDFLAGS})
+endif ()
+ target_compile_features(${name} PRIVATE cxx_generic_lambdas)
+endmacro ()
+
+foreach (X IN ITEMS ${SOURCES})
+ implement_fuzzer(${X})
+endforeach ()
diff --git a/src/fmt/test/fuzzing/README.md b/src/fmt/test/fuzzing/README.md
new file mode 100644
index 000000000..8f7a4536e
--- /dev/null
+++ b/src/fmt/test/fuzzing/README.md
@@ -0,0 +1,48 @@
+# FMT Fuzzer
+
+Fuzzing has revealed [several bugs](https://github.com/fmtlib/fmt/issues?&q=is%3Aissue+fuzz)
+in fmt. It is a part of the continous fuzzing at
+[oss-fuzz](https://github.com/google/oss-fuzz).
+
+The source code is modified to make the fuzzing possible without locking up on
+resource exhaustion:
+```cpp
+#ifdef FMT_FUZZ
+if(spec.precision>100000) {
+ throw std::runtime_error("fuzz mode - avoiding large precision");
+}
+#endif
+```
+This macro `FMT_FUZZ` is enabled on OSS-Fuzz builds and makes fuzzing
+practically possible. It is used in fmt code to prevent resource exhaustion in
+fuzzing mode.
+The macro `FUZZING_BUILD_MODE_UNSAFE_FOR_PRODUCTION` is the
+defacto standard for making fuzzing practically possible to disable certain
+fuzzing-unfriendly features (for example, randomness), see [the libFuzzer
+documentation](https://llvm.org/docs/LibFuzzer.html#fuzzer-friendly-build-mode).
+
+## Running the fuzzers locally
+
+There is a [helper script](build.sh) to build the fuzzers, which has only been
+tested on Debian and Ubuntu linux so far. There should be no problems fuzzing on
+Windows (using clang>=8) or on Mac, but the script will probably not work out of
+the box.
+
+Something along
+```sh
+mkdir build
+cd build
+export CXX=clang++
+export CXXFLAGS="-fsanitize=fuzzer-no-link -DFUZZING_BUILD_MODE_UNSAFE_FOR_PRODUCTION= -g"
+cmake .. -DFMT_SAFE_DURATION_CAST=On -DFMT_FUZZ=On -DFMT_FUZZ_LINKMAIN=Off -DFMT_FUZZ_LDFLAGS="-fsanitize=fuzzer"
+cmake --build .
+```
+should work to build the fuzzers for all platforms which clang supports.
+
+Execute a fuzzer with for instance
+```sh
+cd build
+export UBSAN_OPTIONS=halt_on_error=1
+mkdir out_chrono
+bin/fuzzer_chrono_duration out_chrono
+```
diff --git a/src/fmt/test/fuzzing/build.sh b/src/fmt/test/fuzzing/build.sh
new file mode 100755
index 000000000..141a50d95
--- /dev/null
+++ b/src/fmt/test/fuzzing/build.sh
@@ -0,0 +1,110 @@
+#!/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)
+#
+#
+# Copyright (c) 2019 Paul Dreik
+#
+# License: see LICENSE.rst in the fmt root directory
+
+set -e
+me=$(basename $0)
+root=$(readlink -f "$(dirname "$0")/../..")
+
+
+echo $me: root=$root
+
+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
+cd $builddir
+CXX="ccache g++" CXXFLAGS="$CXXFLAGSALL -g" cmake \
+$CMAKEFLAGSALL \
+-DFMT_FUZZ_LINKMAIN=On \
+-DCMAKE_BUILD_TYPE=Release
+
+cmake --build $builddir
+
+#builds the fuzzers as oss-fuzz does
+builddir=$here/build-fuzzers-ossfuzz
+mkdir -p $builddir
+cd $builddir
+CXX="clang++" \
+CXXFLAGS="$CXXFLAGSALL -fsanitize=fuzzer-no-link" cmake \
+cmake $CMAKEFLAGSALL \
+-DFMT_FUZZ_LINKMAIN=Off \
+-DFMT_FUZZ_LDFLAGS="-fsanitize=fuzzer"
+
+cmake --build $builddir
+
+
+#builds fuzzers for local fuzzing with libfuzzer with asan+usan
+builddir=$here/build-fuzzers-libfuzzer
+mkdir -p $builddir
+cd $builddir
+CXX="clang++" \
+CXXFLAGS="$CXXFLAGSALL -fsanitize=fuzzer-no-link,address,undefined" cmake \
+cmake $CMAKEFLAGSALL \
+-DFMT_FUZZ_LINKMAIN=Off \
+-DFMT_FUZZ_LDFLAGS="-fsanitize=fuzzer"
+
+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
+cd $builddir
+CXX="clang++" \
+CXXFLAGS="$CXXFLAGSALL -fsanitize=fuzzer-no-link -O3" cmake \
+cmake $CMAKEFLAGSALL \
+-DFMT_FUZZ_LINKMAIN=Off \
+-DFMT_FUZZ_LDFLAGS="-fsanitize=fuzzer" \
+ -DCMAKE_BUILD_TYPE=Release
+
+cmake --build $builddir
+
+
+#builds fuzzers for local fuzzing with afl
+builddir=$here/build-fuzzers-afl
+mkdir -p $builddir
+cd $builddir
+CXX="afl-g++" \
+CXXFLAGS="$CXXFLAGSALL -fsanitize=address,undefined" \
+cmake $CMAKEFLAGSALL \
+-DFMT_FUZZ_LINKMAIN=On
+
+cmake --build $builddir
+
+
+echo $me: all good
+
diff --git a/src/fmt/test/fuzzing/chrono_duration.cpp b/src/fmt/test/fuzzing/chrono_duration.cpp
new file mode 100644
index 000000000..3f25f6be7
--- /dev/null
+++ b/src/fmt/test/fuzzing/chrono_duration.cpp
@@ -0,0 +1,152 @@
+// Copyright (c) 2019, Paul Dreik
+// License: see LICENSE.rst in the fmt root directory
+
+#include <fmt/chrono.h>
+#include <cstdint>
+#include <limits>
+#include <stdexcept>
+#include <type_traits>
+#include <vector>
+#include "fuzzer_common.h"
+
+template <typename Item, typename Ratio>
+void invoke_inner(fmt::string_view formatstring, const Item item) {
+ const std::chrono::duration<Item, Ratio> value(item);
+ try {
+#if FMT_FUZZ_FORMAT_TO_STRING
+ std::string message = fmt::format(formatstring, value);
+#else
+ fmt::memory_buffer buf;
+ fmt::format_to(buf, formatstring, value);
+#endif
+ } catch (std::exception& /*e*/) {
+ }
+}
+
+// Item is the underlying type for duration (int, long etc)
+template <typename Item>
+void invoke_outer(const uint8_t* Data, size_t Size, const int scaling) {
+ // always use a fixed location of the data
+ using fmt_fuzzer::Nfixed;
+
+ constexpr auto N = sizeof(Item);
+ static_assert(N <= 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;
+
+ // Data is already allocated separately in libFuzzer so reading past
+ // the end will most likely be detected anyway
+ const auto formatstring = fmt::string_view(fmt_fuzzer::as_chars(Data), Size);
+
+ // doit_impl<Item,std::yocto>(buf.data(),item);
+ // doit_impl<Item,std::zepto>(buf.data(),item);
+ switch (scaling) {
+ case 1:
+ invoke_inner<Item, std::atto>(formatstring, item);
+ break;
+ case 2:
+ invoke_inner<Item, std::femto>(formatstring, item);
+ break;
+ case 3:
+ invoke_inner<Item, std::pico>(formatstring, item);
+ break;
+ case 4:
+ invoke_inner<Item, std::nano>(formatstring, item);
+ break;
+ case 5:
+ invoke_inner<Item, std::micro>(formatstring, item);
+ break;
+ case 6:
+ invoke_inner<Item, std::milli>(formatstring, item);
+ break;
+ case 7:
+ invoke_inner<Item, std::centi>(formatstring, item);
+ break;
+ case 8:
+ invoke_inner<Item, std::deci>(formatstring, item);
+ break;
+ case 9:
+ invoke_inner<Item, std::deca>(formatstring, item);
+ break;
+ case 10:
+ invoke_inner<Item, std::kilo>(formatstring, item);
+ break;
+ case 11:
+ invoke_inner<Item, std::mega>(formatstring, item);
+ break;
+ case 12:
+ invoke_inner<Item, std::giga>(formatstring, item);
+ break;
+ case 13:
+ invoke_inner<Item, std::tera>(formatstring, item);
+ break;
+ case 14:
+ invoke_inner<Item, std::peta>(formatstring, item);
+ break;
+ case 15:
+ invoke_inner<Item, std::exa>(formatstring, item);
+ }
+ // doit_impl<Item,std::zeta>(buf.data(),item);
+ // doit_impl<Item,std::yotta>(buf.data(),item);
+}
+
+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];
+ Data += 2;
+ Size -= 2;
+
+ switch (representation) {
+ case 1:
+ invoke_outer<char>(Data, Size, scaling);
+ break;
+ case 2:
+ invoke_outer<unsigned char>(Data, Size, scaling);
+ break;
+ case 3:
+ invoke_outer<signed char>(Data, Size, scaling);
+ break;
+ case 4:
+ invoke_outer<short>(Data, Size, scaling);
+ break;
+ case 5:
+ invoke_outer<unsigned short>(Data, Size, scaling);
+ break;
+ case 6:
+ invoke_outer<int>(Data, Size, scaling);
+ break;
+ case 7:
+ invoke_outer<unsigned int>(Data, Size, scaling);
+ break;
+ case 8:
+ invoke_outer<long>(Data, Size, scaling);
+ break;
+ case 9:
+ invoke_outer<unsigned long>(Data, Size, scaling);
+ break;
+ case 10:
+ invoke_outer<float>(Data, Size, scaling);
+ break;
+ case 11:
+ invoke_outer<double>(Data, Size, scaling);
+ break;
+ case 12:
+ invoke_outer<long double>(Data, Size, scaling);
+ break;
+ default:
+ break;
+ }
+
+ return 0;
+}
diff --git a/src/fmt/test/fuzzing/fuzzer_common.h b/src/fmt/test/fuzzing/fuzzer_common.h
new file mode 100644
index 000000000..c3d85619a
--- /dev/null
+++ b/src/fmt/test/fuzzing/fuzzer_common.h
@@ -0,0 +1,67 @@
+#ifndef FUZZER_COMMON_H
+#define FUZZER_COMMON_H
+
+// Copyright (c) 2019, Paul Dreik
+// License: see LICENSE.rst in the fmt root directory
+
+#include <cstdint> // std::uint8_t
+#include <cstring> // memcpy
+#include <type_traits> // trivially copyable
+
+// one can format to either a string, or a buf. buf is faster,
+// but one may be interested in formatting to a string instead to
+// verify it works as intended. to avoid a combinatoric explosion,
+// select this at compile time instead of dynamically from the fuzz data
+#define FMT_FUZZ_FORMAT_TO_STRING 0
+
+// if fmt is given a buffer that is separately allocated,
+// chances that address sanitizer detects out of bound reads is
+// much higher. However, it slows down the fuzzing.
+#define FMT_FUZZ_SEPARATE_ALLOCATION 1
+
+// 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.
+// For this, we must know the size of the largest possible type in use.
+
+// There are some problems on travis, claiming Nfixed is not a constant
+// expression which seems to be an issue with older versions of libstdc++
+#if _GLIBCXX_RELEASE >= 7
+# include <algorithm>
+namespace fmt_fuzzer {
+constexpr auto Nfixed = std::max(sizeof(long double), sizeof(std::intmax_t));
+}
+#else
+namespace fmt_fuzzer {
+constexpr auto Nfixed = 16;
+}
+#endif
+
+namespace fmt_fuzzer {
+// view data as a c char pointer.
+template <typename T> inline const char* as_chars(const T* data) {
+ return static_cast<const char*>(static_cast<const void*>(data));
+}
+
+// view data as a byte pointer
+template <typename T> inline const std::uint8_t* as_bytes(const T* data) {
+ return static_cast<const std::uint8_t*>(static_cast<const void*>(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) {
+ 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) {
+ return !!Data[0];
+}
+
+} // namespace fmt_fuzzer
+
+#endif // FUZZER_COMMON_H
diff --git a/src/fmt/test/fuzzing/main.cpp b/src/fmt/test/fuzzing/main.cpp
new file mode 100644
index 000000000..1053eeeb0
--- /dev/null
+++ b/src/fmt/test/fuzzing/main.cpp
@@ -0,0 +1,21 @@
+#include <cassert>
+#include <fstream>
+#include <sstream>
+#include <vector>
+#include "fuzzer_common.h"
+
+extern "C" int LLVMFuzzerTestOneInput(const uint8_t* Data, size_t Size);
+int main(int argc, char* argv[]) {
+ for (int i = 1; i < argc; ++i) {
+ std::ifstream in(argv[i]);
+ assert(in);
+ in.seekg(0, std::ios_base::end);
+ const auto pos = in.tellg();
+ assert(pos >= 0);
+ in.seekg(0, std::ios_base::beg);
+ std::vector<char> buf(static_cast<size_t>(pos));
+ in.read(buf.data(), static_cast<long>(buf.size()));
+ assert(in.gcount() == pos);
+ LLVMFuzzerTestOneInput(fmt_fuzzer::as_bytes(buf.data()), buf.size());
+ }
+}
diff --git a/src/fmt/test/fuzzing/named_arg.cpp b/src/fmt/test/fuzzing/named_arg.cpp
new file mode 100644
index 000000000..bd0cb686c
--- /dev/null
+++ b/src/fmt/test/fuzzing/named_arg.cpp
@@ -0,0 +1,128 @@
+// Copyright (c) 2019, Paul Dreik
+// License: see LICENSE.rst in the fmt root directory
+
+#include <fmt/chrono.h>
+#include <fmt/core.h>
+#include <cstdint>
+#include <stdexcept>
+#include <type_traits>
+#include <vector>
+#include "fuzzer_common.h"
+
+template <typename Item1>
+void invoke_fmt(const uint8_t* Data, size_t Size, unsigned int argsize) {
+ constexpr auto N1 = sizeof(Item1);
+ static_assert(N1 <= fmt_fuzzer::Nfixed, "Nfixed too small");
+ if (Size <= fmt_fuzzer::Nfixed) {
+ return;
+ }
+ const Item1 item1 = fmt_fuzzer::assignFromBuf<Item1>(Data);
+
+ Data += fmt_fuzzer::Nfixed;
+ Size -= fmt_fuzzer::Nfixed;
+
+ // how many chars should be used for the argument name?
+ if (argsize <= 0 || argsize >= Size) {
+ return;
+ }
+
+ // allocating buffers separately is slower, but increases chances
+ // of detecting memory errors
+#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
+ // allocates as tight as possible, making it easier to catch buffer overruns.
+ std::vector<char> fmtstringbuffer(Size);
+ std::memcpy(fmtstringbuffer.data(), Data, Size);
+ auto fmtstring = fmt::string_view(fmtstringbuffer.data(), Size);
+#else
+ auto fmtstring = fmt::string_view(fmt_fuzzer::as_chars(Data), Size);
+#endif
+
+#if FMT_FUZZ_FORMAT_TO_STRING
+ std::string message = fmt::format(fmtstring, fmt::arg(argname, item1));
+#else
+ fmt::memory_buffer outbuf;
+ fmt::format_to(outbuf, fmtstring, fmt::arg(argname, item1));
+#endif
+}
+
+// for dynamic dispatching to an explicit instantiation
+template <typename Callback> void invoke(int index, Callback callback) {
+ switch (index) {
+ case 0:
+ callback(bool{});
+ break;
+ case 1:
+ callback(char{});
+ break;
+ case 2:
+ using sc = signed char;
+ callback(sc{});
+ break;
+ case 3:
+ using uc = unsigned char;
+ callback(uc{});
+ break;
+ case 4:
+ callback(short{});
+ break;
+ case 5:
+ using us = unsigned short;
+ callback(us{});
+ break;
+ case 6:
+ callback(int{});
+ break;
+ case 7:
+ callback(unsigned{});
+ break;
+ case 8:
+ callback(long{});
+ break;
+ case 9:
+ using ul = unsigned long;
+ callback(ul{});
+ break;
+ case 10:
+ callback(float{});
+ break;
+ case 11:
+ callback(double{});
+ break;
+ case 12:
+ using LD = long double;
+ callback(LD{});
+ break;
+ }
+}
+
+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 int second = (Data[0] & 0xF0) >> 4;
+ Data++;
+ Size--;
+
+ auto outerfcn = [=](auto param1) {
+ invoke_fmt<decltype(param1)>(Data, Size, second);
+ };
+
+ try {
+ invoke(first, outerfcn);
+ } catch (std::exception& /*e*/) {
+ }
+ return 0;
+}
diff --git a/src/fmt/test/fuzzing/one_arg.cpp b/src/fmt/test/fuzzing/one_arg.cpp
new file mode 100644
index 000000000..3a1bf5ccb
--- /dev/null
+++ b/src/fmt/test/fuzzing/one_arg.cpp
@@ -0,0 +1,131 @@
+// Copyright (c) 2019, Paul Dreik
+// License: see LICENSE.rst in the fmt root directory
+
+#include <fmt/core.h>
+#include <cstdint>
+#include <stdexcept>
+#include <type_traits>
+#include <vector>
+
+#include <fmt/chrono.h>
+#include "fuzzer_common.h"
+
+using fmt_fuzzer::Nfixed;
+
+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
+ // allocates as tight as possible, making it easier to catch buffer overruns.
+ std::vector<char> fmtstringbuffer(Size);
+ std::memcpy(fmtstringbuffer.data(), Data, Size);
+ auto fmtstring = fmt::string_view(fmtstringbuffer.data(), Size);
+#else
+ auto fmtstring = fmt::string_view(fmt_fuzzer::as_chars(Data), Size);
+#endif
+
+#if FMT_FUZZ_FORMAT_TO_STRING
+ std::string message = fmt::format(fmtstring, item);
+#else
+ fmt::memory_buffer message;
+ fmt::format_to(message, fmtstring, item);
+#endif
+}
+
+void invoke_fmt_time(const uint8_t* Data, size_t Size) {
+ using Item = std::time_t;
+ constexpr auto N = sizeof(Item);
+ static_assert(N <= 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
+ // allocates as tight as possible, making it easier to catch buffer overruns.
+ std::vector<char> fmtstringbuffer(Size);
+ std::memcpy(fmtstringbuffer.data(), Data, Size);
+ auto fmtstring = fmt::string_view(fmtstringbuffer.data(), Size);
+#else
+ auto fmtstring = fmt::string_view(fmt_fuzzer::as_chars(Data), Size);
+#endif
+ auto* b = std::localtime(&item);
+ if (b) {
+#if FMT_FUZZ_FORMAT_TO_STRING
+ std::string message = fmt::format(fmtstring, *b);
+#else
+ fmt::memory_buffer message;
+ fmt::format_to(message, fmtstring, *b);
+#endif
+ }
+}
+
+extern "C" int LLVMFuzzerTestOneInput(const uint8_t* Data, size_t Size) {
+ if (Size <= 3) {
+ return 0;
+ }
+
+ const auto first = Data[0];
+ Data++;
+ Size--;
+
+ try {
+ switch (first) {
+ case 0:
+ invoke_fmt<bool>(Data, Size);
+ break;
+ case 1:
+ invoke_fmt<char>(Data, Size);
+ break;
+ case 2:
+ invoke_fmt<unsigned char>(Data, Size);
+ break;
+ case 3:
+ invoke_fmt<signed char>(Data, Size);
+ break;
+ case 4:
+ invoke_fmt<short>(Data, Size);
+ break;
+ case 5:
+ invoke_fmt<unsigned short>(Data, Size);
+ break;
+ case 6:
+ invoke_fmt<int>(Data, Size);
+ break;
+ case 7:
+ invoke_fmt<unsigned int>(Data, Size);
+ break;
+ case 8:
+ invoke_fmt<long>(Data, Size);
+ break;
+ case 9:
+ invoke_fmt<unsigned long>(Data, Size);
+ break;
+ case 10:
+ invoke_fmt<float>(Data, Size);
+ break;
+ case 11:
+ invoke_fmt<double>(Data, Size);
+ break;
+ case 12:
+ invoke_fmt<long double>(Data, Size);
+ break;
+ case 13:
+ invoke_fmt_time(Data, Size);
+ break;
+ default:
+ break;
+ }
+ } catch (std::exception& /*e*/) {
+ }
+ return 0;
+}
diff --git a/src/fmt/test/fuzzing/sprintf.cpp b/src/fmt/test/fuzzing/sprintf.cpp
new file mode 100644
index 000000000..aa0287569
--- /dev/null
+++ b/src/fmt/test/fuzzing/sprintf.cpp
@@ -0,0 +1,116 @@
+// Copyright (c) 2019, Paul Dreik
+// License: see LICENSE.rst in the fmt root directory
+#include <fmt/format.h>
+#include <fmt/printf.h>
+#include <cstdint>
+#include <stdexcept>
+
+#include "fuzzer_common.h"
+
+using fmt_fuzzer::Nfixed;
+
+template <typename Item1, typename Item2>
+void invoke_fmt(const uint8_t* Data, size_t Size) {
+ constexpr auto N1 = sizeof(Item1);
+ constexpr auto N2 = sizeof(Item2);
+ static_assert(N1 <= Nfixed, "size1 exceeded");
+ static_assert(N2 <= Nfixed, "size2 exceeded");
+ if (Size <= Nfixed + Nfixed) {
+ return;
+ }
+ Item1 item1 = fmt_fuzzer::assignFromBuf<Item1>(Data);
+ Data += Nfixed;
+ Size -= Nfixed;
+
+ Item2 item2 = fmt_fuzzer::assignFromBuf<Item2>(Data);
+ Data += Nfixed;
+ Size -= Nfixed;
+
+ auto fmtstring = fmt::string_view(fmt_fuzzer::as_chars(Data), Size);
+
+#if FMT_FUZZ_FORMAT_TO_STRING
+ std::string message = fmt::format(fmtstring, item1, item2);
+#else
+ fmt::memory_buffer message;
+ fmt::format_to(message, fmtstring, item1, item2);
+#endif
+}
+
+// for dynamic dispatching to an explicit instantiation
+template <typename Callback> void invoke(int index, Callback callback) {
+ switch (index) {
+ case 0:
+ callback(bool{});
+ break;
+ case 1:
+ callback(char{});
+ break;
+ case 2:
+ using sc = signed char;
+ callback(sc{});
+ break;
+ case 3:
+ using uc = unsigned char;
+ callback(uc{});
+ break;
+ case 4:
+ callback(short{});
+ break;
+ case 5:
+ using us = unsigned short;
+ callback(us{});
+ break;
+ case 6:
+ callback(int{});
+ break;
+ case 7:
+ callback(unsigned{});
+ break;
+ case 8:
+ callback(long{});
+ break;
+ case 9:
+ using ul = unsigned long;
+ callback(ul{});
+ break;
+ case 10:
+ callback(float{});
+ break;
+ case 11:
+ callback(double{});
+ break;
+ case 12:
+ using LD = long double;
+ callback(LD{});
+ break;
+ case 13:
+ using ptr = void*;
+ callback(ptr{});
+ break;
+ }
+}
+
+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;
+ Data++;
+ Size--;
+
+ auto outer = [=](auto param1) {
+ auto inner = [=](auto param2) {
+ invoke_fmt<decltype(param1), decltype(param2)>(Data, Size);
+ };
+ invoke(second, inner);
+ };
+
+ try {
+ invoke(first, outer);
+ } catch (std::exception& /*e*/) {
+ }
+ return 0;
+}
diff --git a/src/fmt/test/fuzzing/two_args.cpp b/src/fmt/test/fuzzing/two_args.cpp
new file mode 100644
index 000000000..af3495ce8
--- /dev/null
+++ b/src/fmt/test/fuzzing/two_args.cpp
@@ -0,0 +1,112 @@
+// Copyright (c) 2019, Paul Dreik
+// License: see LICENSE.rst in the fmt root directory
+#include <fmt/format.h>
+#include <cstdint>
+#include <stdexcept>
+#include <type_traits>
+
+#include "fuzzer_common.h"
+
+constexpr auto Nfixed = fmt_fuzzer::Nfixed;
+
+template <typename Item1, typename Item2>
+void invoke_fmt(const uint8_t* Data, size_t Size) {
+ constexpr auto N1 = sizeof(Item1);
+ constexpr auto N2 = sizeof(Item2);
+ static_assert(N1 <= Nfixed, "size1 exceeded");
+ static_assert(N2 <= Nfixed, "size2 exceeded");
+ if (Size <= Nfixed + Nfixed) {
+ return;
+ }
+ const Item1 item1 = fmt_fuzzer::assignFromBuf<Item1>(Data);
+ Data += Nfixed;
+ Size -= Nfixed;
+
+ const Item2 item2 = fmt_fuzzer::assignFromBuf<Item2>(Data);
+ Data += Nfixed;
+ Size -= Nfixed;
+
+ auto fmtstring = fmt::string_view(fmt_fuzzer::as_chars(Data), Size);
+
+#if FMT_FUZZ_FORMAT_TO_STRING
+ std::string message = fmt::format(fmtstring, item1, item2);
+#else
+ fmt::memory_buffer message;
+ fmt::format_to(message, fmtstring, item1, item2);
+#endif
+}
+
+// for dynamic dispatching to an explicit instantiation
+template <typename Callback> void invoke(int index, Callback callback) {
+ switch (index) {
+ case 0:
+ callback(bool{});
+ break;
+ case 1:
+ callback(char{});
+ break;
+ case 2:
+ using sc = signed char;
+ callback(sc{});
+ break;
+ case 3:
+ using uc = unsigned char;
+ callback(uc{});
+ break;
+ case 4:
+ callback(short{});
+ break;
+ case 5:
+ using us = unsigned short;
+ callback(us{});
+ break;
+ case 6:
+ callback(int{});
+ break;
+ case 7:
+ callback(unsigned{});
+ break;
+ case 8:
+ callback(long{});
+ break;
+ case 9:
+ using ul = unsigned long;
+ callback(ul{});
+ break;
+ case 10:
+ callback(float{});
+ break;
+ case 11:
+ callback(double{});
+ break;
+ case 12:
+ using LD = long double;
+ callback(LD{});
+ break;
+ }
+}
+
+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;
+ Data++;
+ Size--;
+
+ auto outer = [=](auto param1) {
+ auto inner = [=](auto param2) {
+ invoke_fmt<decltype(param1), decltype(param2)>(Data, Size);
+ };
+ invoke(second, inner);
+ };
+
+ try {
+ invoke(first, outer);
+ } catch (std::exception& /*e*/) {
+ }
+ return 0;
+}