diff options
Diffstat (limited to 'llvm/lib/Fuzzer/FuzzerFnAdapter.h')
-rw-r--r-- | llvm/lib/Fuzzer/FuzzerFnAdapter.h | 175 |
1 files changed, 175 insertions, 0 deletions
diff --git a/llvm/lib/Fuzzer/FuzzerFnAdapter.h b/llvm/lib/Fuzzer/FuzzerFnAdapter.h new file mode 100644 index 00000000000..eec378e7e3b --- /dev/null +++ b/llvm/lib/Fuzzer/FuzzerFnAdapter.h @@ -0,0 +1,175 @@ +//===- FuzzerAdapter.h - Arbitrary function Fuzzer adapter -------*- C++ -*===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// W A R N I N G : E X P E R I M E N T A L. +// +// Defines an adapter to fuzz functions with (almost) arbitrary signatures. +//===----------------------------------------------------------------------===// + +#ifndef LLVM_FUZZER_ADAPTER_H +#define LLVM_FUZZER_ADAPTER_H + +#include <stddef.h> +#include <stdint.h> + +#include <algorithm> +#include <tuple> +#include <vector> + +namespace fuzzer { + +/// Unpacks bytes from \p Data according to \p F argument types +/// and calls the function. +/// Use to automatically adapt LLVMFuzzerTestOneInput interface to +/// a specific function. +/// Supported argument types: primitive types, std::vector<uint8_t>. +template <typename Fn> bool Adapt(Fn F, const uint8_t *Data, size_t Size); + +// The implementation performs several steps: +// - function argument types are obtained (Args...) +// - data is unpacked into std::tuple<Args...> one by one +// - function is called with std::tuple<Args...> containing arguments. +namespace impl { + +// Single argument unpacking. + +template <typename T> +size_t UnpackPrimitive(const uint8_t *Data, size_t Size, T *Value) { + if (Size < sizeof(T)) + return Size; + *Value = *reinterpret_cast<const T *>(Data); + return Size - sizeof(T); +} + +/// Unpacks into a given Value and returns the Size - num_consumed_bytes. +/// Return value equal to Size signals inability to unpack the data (typically +/// because there are not enough bytes). +template <typename T> +size_t UnpackSingle(const uint8_t *Data, size_t Size, T *Value); + +#define UNPACK_SINGLE_PRIMITIVE(Type) \ + template <> \ + size_t UnpackSingle<Type>(const uint8_t *Data, size_t Size, Type *Value) { \ + return UnpackPrimitive(Data, Size, Value); \ + } + +UNPACK_SINGLE_PRIMITIVE(char) +UNPACK_SINGLE_PRIMITIVE(signed char) +UNPACK_SINGLE_PRIMITIVE(unsigned char) + +UNPACK_SINGLE_PRIMITIVE(short int) +UNPACK_SINGLE_PRIMITIVE(unsigned short int) + +UNPACK_SINGLE_PRIMITIVE(int) +UNPACK_SINGLE_PRIMITIVE(unsigned int) + +UNPACK_SINGLE_PRIMITIVE(long int) +UNPACK_SINGLE_PRIMITIVE(unsigned long int) + +UNPACK_SINGLE_PRIMITIVE(bool) +UNPACK_SINGLE_PRIMITIVE(wchar_t) + +UNPACK_SINGLE_PRIMITIVE(float) +UNPACK_SINGLE_PRIMITIVE(double) +UNPACK_SINGLE_PRIMITIVE(long double) + +#undef UNPACK_SINGLE_PRIMITIVE + +template <> +size_t UnpackSingle<std::vector<uint8_t>>(const uint8_t *Data, size_t Size, + std::vector<uint8_t> *Value) { + if (Size < 1) + return Size; + size_t Len = std::min(static_cast<size_t>(*Data), Size - 1); + std::vector<uint8_t> V(Data + 1, Data + 1 + Len); + Value->swap(V); + return Size - Len - 1; +} + +// Unpacking into arbitrary tuple. + +// Recursion guard. +template <int N, typename TupleT> +typename std::enable_if<N == std::tuple_size<TupleT>::value, bool>::type +UnpackImpl(const uint8_t *Data, size_t Size, TupleT *Tuple) { + return true; +} + +// Unpack tuple elements starting from Nth. +template <int N, typename TupleT> +typename std::enable_if<N < std::tuple_size<TupleT>::value, bool>::type +UnpackImpl(const uint8_t *Data, size_t Size, TupleT *Tuple) { + size_t NewSize = UnpackSingle(Data, Size, &std::get<N>(*Tuple)); + if (NewSize == Size) { + return false; + } + + return UnpackImpl<N + 1, TupleT>(Data + (Size - NewSize), NewSize, Tuple); +} + +// Unpacks into arbitrary tuple and returns true if successful. +template <typename... Args> +bool Unpack(const uint8_t *Data, size_t Size, std::tuple<Args...> *Tuple) { + return UnpackImpl<0, std::tuple<Args...>>(Data, Size, Tuple); +} + +// Helper integer sequence templates. + +template <int...> struct Seq {}; + +template <int N, int... S> struct GenSeq : GenSeq<N - 1, N - 1, S...> {}; + +// GenSeq<N>::type is Seq<0, 1, ..., N-1> +template <int... S> struct GenSeq<0, S...> { typedef Seq<S...> type; }; + +// Function signature introspection. + +template <typename T> struct FnTraits {}; + +template <typename ReturnType, typename... Args> +struct FnTraits<ReturnType (*)(Args...)> { + enum { Arity = sizeof...(Args) }; + typedef std::tuple<Args...> ArgsTupleT; +}; + +// Calling a function with arguments in a tuple. + +template <typename Fn, int... S> +void ApplyImpl(Fn F, const typename FnTraits<Fn>::ArgsTupleT &Params, + Seq<S...>) { + F(std::get<S>(Params)...); +} + +template <typename Fn> +void Apply(Fn F, const typename FnTraits<Fn>::ArgsTupleT &Params) { + // S is Seq<0, ..., Arity-1> + auto S = typename GenSeq<FnTraits<Fn>::Arity>::type(); + ApplyImpl(F, Params, S); +} + +// Unpacking data into arguments tuple of correct type and calling the function. +template <typename Fn> +bool UnpackAndApply(Fn F, const uint8_t *Data, size_t Size) { + typename FnTraits<Fn>::ArgsTupleT Tuple; + if (!Unpack(Data, Size, &Tuple)) + return false; + + Apply(F, Tuple); + return true; +} + +} // namespace impl + +template <typename Fn> bool Adapt(Fn F, const uint8_t *Data, size_t Size) { + return impl::UnpackAndApply(F, Data, Size); +} + +} // namespace fuzzer + +#endif |