diff options
author | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-21 11:54:28 +0000 |
---|---|---|
committer | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-21 11:54:28 +0000 |
commit | e6918187568dbd01842d8d1d2c808ce16a894239 (patch) | |
tree | 64f88b554b444a49f656b6c656111a145cbbaa28 /src/fmt/test/fuzzing | |
parent | Initial commit. (diff) | |
download | ceph-b26c4052f3542036551aa9dec9caa4226e456195.tar.xz ceph-b26c4052f3542036551aa9dec9caa4226e456195.zip |
Adding upstream version 18.2.2.upstream/18.2.2
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'src/fmt/test/fuzzing')
-rw-r--r-- | src/fmt/test/fuzzing/.gitignore | 3 | ||||
-rw-r--r-- | src/fmt/test/fuzzing/CMakeLists.txt | 30 | ||||
-rw-r--r-- | src/fmt/test/fuzzing/README.md | 25 | ||||
-rwxr-xr-x | src/fmt/test/fuzzing/build.sh | 90 | ||||
-rw-r--r-- | src/fmt/test/fuzzing/chrono-duration.cc | 136 | ||||
-rw-r--r-- | src/fmt/test/fuzzing/chrono-timepoint.cc | 32 | ||||
-rw-r--r-- | src/fmt/test/fuzzing/float.cc | 39 | ||||
-rw-r--r-- | src/fmt/test/fuzzing/fuzzer-common.h | 77 | ||||
-rw-r--r-- | src/fmt/test/fuzzing/main.cc | 22 | ||||
-rw-r--r-- | src/fmt/test/fuzzing/named-arg.cc | 102 | ||||
-rw-r--r-- | src/fmt/test/fuzzing/one-arg.cc | 92 | ||||
-rw-r--r-- | src/fmt/test/fuzzing/two-args.cc | 106 |
12 files changed, 754 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..0280c5cdf --- /dev/null +++ b/src/fmt/test/fuzzing/CMakeLists.txt @@ -0,0 +1,30 @@ +# Copyright (c) 2019, Paul Dreik +# License: see LICENSE.rst in the fmt root directory + +# Link in the main function. 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") + +# 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) + add_executable(${name} ${source} fuzzer-common.h) + if (FMT_FUZZ_LINKMAIN) + target_sources(${name} PRIVATE main.cc) + 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) +endfunction() + +foreach (source chrono-duration.cc chrono-timepoint.cc float.cc named-arg.cc one-arg.cc two-args.cc) + add_fuzzer(${source}) +endforeach () diff --git a/src/fmt/test/fuzzing/README.md b/src/fmt/test/fuzzing/README.md new file mode 100644 index 000000000..bb3d0e04f --- /dev/null +++ b/src/fmt/test/fuzzing/README.md @@ -0,0 +1,25 @@ +# 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..4497b62c1 --- /dev/null +++ b/src/fmt/test/fuzzing/build.sh @@ -0,0 +1,90 @@ +#!/bin/sh +# +# Creates fuzzer builds of various kinds +# - 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 +# +# For the license information refer to format.h. + +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" + +CLANG=clang++-11 + +# 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 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.cc b/src/fmt/test/fuzzing/chrono-duration.cc new file mode 100644 index 000000000..d66068d9c --- /dev/null +++ b/src/fmt/test/fuzzing/chrono-duration.cc @@ -0,0 +1,136 @@ +// Copyright (c) 2019, Paul Dreik +// For the license information refer to format.h. + +#include <fmt/chrono.h> + +#include <cstdint> + +#include "fuzzer-common.h" + +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); +#else + auto buf = fmt::memory_buffer(); + fmt::format_to(std::back_inserter(buf), format_str, value); +#endif + } catch (std::exception&) { + } +} + +// 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. + static_assert(sizeof(Rep) <= fixed_size, "fixed size is too small"); + if (size <= fixed_size + 1) return; + + 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(as_chars(data), size); + + // yocto, zepto, zetta and yotta are not handled. + switch (period) { + case 1: + invoke_inner<std::atto>(format_str, rep); + break; + case 2: + invoke_inner<std::femto>(format_str, rep); + break; + case 3: + invoke_inner<std::pico>(format_str, rep); + break; + case 4: + invoke_inner<std::nano>(format_str, rep); + break; + case 5: + invoke_inner<std::micro>(format_str, rep); + break; + case 6: + invoke_inner<std::milli>(format_str, rep); + break; + case 7: + invoke_inner<std::centi>(format_str, rep); + break; + case 8: + invoke_inner<std::deci>(format_str, rep); + break; + case 9: + invoke_inner<std::deca>(format_str, rep); + break; + case 10: + invoke_inner<std::kilo>(format_str, rep); + break; + case 11: + invoke_inner<std::mega>(format_str, rep); + break; + case 12: + invoke_inner<std::giga>(format_str, rep); + break; + case 13: + invoke_inner<std::tera>(format_str, rep); + break; + case 14: + invoke_inner<std::peta>(format_str, rep); + break; + case 15: + invoke_inner<std::exa>(format_str, rep); + break; + } +} + +extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) { + if (size <= 4) return 0; + + const auto representation = data[0]; + const auto period = data[1]; + data += 2; + size -= 2; + + switch (representation) { + case 1: + invoke_outer<char>(data, size, period); + break; + case 2: + invoke_outer<signed char>(data, size, period); + break; + case 3: + invoke_outer<unsigned char>(data, size, period); + break; + case 4: + invoke_outer<short>(data, size, period); + break; + case 5: + invoke_outer<unsigned short>(data, size, period); + break; + case 6: + invoke_outer<int>(data, size, period); + break; + case 7: + invoke_outer<unsigned int>(data, size, period); + break; + case 8: + invoke_outer<long>(data, size, period); + break; + case 9: + invoke_outer<unsigned long>(data, size, period); + break; + case 10: + invoke_outer<float>(data, size, period); + break; + case 11: + invoke_outer<double>(data, size, period); + break; + case 12: + invoke_outer<long double>(data, size, period); + break; + } + return 0; +} diff --git a/src/fmt/test/fuzzing/chrono-timepoint.cc b/src/fmt/test/fuzzing/chrono-timepoint.cc new file mode 100644 index 000000000..8a1b24d29 --- /dev/null +++ b/src/fmt/test/fuzzing/chrono-timepoint.cc @@ -0,0 +1,32 @@ +// Copyright (c) 2021, Paul Dreik +// For license information refer to format.h. +#include <fmt/chrono.h> + +#include "fuzzer-common.h" + +/* + * a fuzzer for the chrono timepoints formatters + * C is a clock (std::chrono::system_clock etc) + */ +template <typename C> void doit(const uint8_t* data, size_t size) { + using Rep = typename C::time_point::rep; + constexpr auto N = sizeof(Rep); + if (size < N) return; + + const auto x = assign_from_buf<Rep>(data); + typename C::duration dur{x}; + typename C::time_point timepoint{dur}; + data += N; + size -= N; + data_to_string format_str(data, size); + + std::string message = fmt::format(format_str.get(), timepoint); +} + +extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) { + try { + doit<std::chrono::system_clock>(data, size); + } catch (...) { + } + return 0; +} diff --git a/src/fmt/test/fuzzing/float.cc b/src/fmt/test/fuzzing/float.cc new file mode 100644 index 000000000..d4c9e4f57 --- /dev/null +++ b/src/fmt/test/fuzzing/float.cc @@ -0,0 +1,39 @@ +// A fuzzer for floating-point formatter. +// For the license information refer to format.h. + +#include <fmt/format.h> + +#include <cstdint> +#include <cstdlib> +#include <limits> +#include <stdexcept> + +#include "fuzzer-common.h" + +void check_round_trip(fmt::string_view format_str, double value) { + auto buffer = fmt::memory_buffer(); + fmt::format_to(std::back_inserter(buffer), format_str, value); + + 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; + } + + buffer.push_back('\0'); + char* ptr = nullptr; + if (std::strtod(buffer.data(), &ptr) != value) + throw std::runtime_error("round trip failure"); + if (ptr + 1 != buffer.end()) throw std::runtime_error("unparsed output"); +} + +extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) { + if (size <= sizeof(double) || !std::numeric_limits<double>::is_iec559) + return 0; + check_round_trip("{}", assign_from_buf<double>(data)); + // A larger than necessary precision is used to trigger the fallback + // formatter. + check_round_trip("{:.50g}", assign_from_buf<double>(data)); + 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..c0a246722 --- /dev/null +++ b/src/fmt/test/fuzzing/fuzzer-common.h @@ -0,0 +1,77 @@ +// Copyright (c) 2019, Paul Dreik +// For the license information refer to format.h. + +#ifndef FUZZER_COMMON_H +#define FUZZER_COMMON_H + +#include <fmt/core.h> + +#include <cstdint> // std::uint8_t +#include <cstring> // memcpy +#include <vector> + +// 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 +// 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 + +// 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 fixed_size = 16; + +// Casts data to a char pointer. +template <typename T> inline const char* as_chars(const T* data) { + return reinterpret_cast<const char*>(data); +} + +// Casts data to a byte pointer. +template <typename T> inline const std::uint8_t* as_bytes(const T* data) { + return reinterpret_cast<const std::uint8_t*>(data); +} + +// Blits bytes from data to form an (assumed trivially constructible) object +// of type Item. +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 assign_from_buf<bool>(const std::uint8_t* data) { + return *data != 0; +} + +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)) { + if (size) { + 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/src/fmt/test/fuzzing/main.cc b/src/fmt/test/fuzzing/main.cc new file mode 100644 index 000000000..8f8c719b7 --- /dev/null +++ b/src/fmt/test/fuzzing/main.cc @@ -0,0 +1,22 @@ +#include <cassert> +#include <fstream> +#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 size = in.tellg(); + assert(size >= 0); + in.seekg(0, std::ios_base::beg); + std::vector<char> buf(static_cast<size_t>(size)); + in.read(buf.data(), size); + assert(in.gcount() == size); + LLVMFuzzerTestOneInput(as_bytes(buf.data()), buf.size()); + } +} diff --git a/src/fmt/test/fuzzing/named-arg.cc b/src/fmt/test/fuzzing/named-arg.cc new file mode 100644 index 000000000..3bee1ae3b --- /dev/null +++ b/src/fmt/test/fuzzing/named-arg.cc @@ -0,0 +1,102 @@ +// 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 "fuzzer-common.h" + +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; + + 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); + try { +#if FMT_FUZZ_FORMAT_TO_STRING + std::string message = + fmt::format(format_str.get(), fmt::arg(arg_name.data(), value)); +#else + fmt::memory_buffer out; + fmt::format_to(std::back_inserter(out), format_str.get(), + fmt::arg(arg_name.data(), value)); +#endif + } catch (std::exception&) { + } +} + +// For dynamic dispatching to an explicit instantiation. +template <typename Callback> void invoke(int type, Callback callback) { + switch (type) { + 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 type = data[0] & 0x0F; + const unsigned arg_name_size = (data[0] & 0xF0) >> 4; + data++; + size--; + + invoke(type, [=](auto arg) { + invoke_fmt<decltype(arg)>(data, size, arg_name_size); + }); + return 0; +} diff --git a/src/fmt/test/fuzzing/one-arg.cc b/src/fmt/test/fuzzing/one-arg.cc new file mode 100644 index 000000000..af9787f81 --- /dev/null +++ b/src/fmt/test/fuzzing/one-arg.cc @@ -0,0 +1,92 @@ +// Copyright (c) 2019, Paul Dreik +// For the license information refer to format.h. + +#include <fmt/chrono.h> + +#include <cstdint> +#include <exception> + +#include "fuzzer-common.h" + +template <typename T, typename Repr> const T* from_repr(const Repr& r) { + return &r; +} + +template <> const std::tm* from_repr<std::tm>(const std::time_t& t) { + return std::localtime(&t); +} + +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); + try { +#if FMT_FUZZ_FORMAT_TO_STRING + std::string message = fmt::format(format_str.get(), *value); +#else + auto buf = fmt::memory_buffer(); + fmt::format_to(std::back_inserter(buf), format_str.get(), *value); +#endif + } catch (std::exception&) { + } +} + +extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) { + if (size <= 3) return 0; + + const auto first = data[0]; + data++; + size--; + + 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<std::tm, std::time_t>(data, size); + break; + } + return 0; +} diff --git a/src/fmt/test/fuzzing/two-args.cc b/src/fmt/test/fuzzing/two-args.cc new file mode 100644 index 000000000..931c64656 --- /dev/null +++ b/src/fmt/test/fuzzing/two-args.cc @@ -0,0 +1,106 @@ +// Copyright (c) 2019, Paul Dreik +// For the license information refer to format.h. + +#include <fmt/format.h> + +#include <cstdint> +#include <exception> +#include <string> + +#include "fuzzer-common.h" + +template <typename Item1, typename Item2> +void invoke_fmt(const uint8_t* data, size_t size) { + 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 = assign_from_buf<Item1>(data); + data += fixed_size; + size -= fixed_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 + auto buf = fmt::memory_buffer(); + fmt::format_to(std::back_inserter(buf), format_str, 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 type1 = data[0] & 0x0F; + const auto type2 = (data[0] & 0xF0) >> 4; + data++; + size--; + try { + invoke(type1, [=](auto param1) { + invoke(type2, [=](auto param2) { + invoke_fmt<decltype(param1), decltype(param2)>(data, size); + }); + }); + } catch (std::exception&) { + } + return 0; +} |