//===- 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 #include #include #include #include #include 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. template 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 one by one // - function is called with std::tuple containing arguments. namespace impl { // Single argument unpacking. template size_t UnpackPrimitive(const uint8_t *Data, size_t Size, T *Value) { if (Size < sizeof(T)) return Size; *Value = *reinterpret_cast(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 size_t UnpackSingle(const uint8_t *Data, size_t Size, T *Value); #define UNPACK_SINGLE_PRIMITIVE(Type) \ template <> \ size_t UnpackSingle(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>(const uint8_t *Data, size_t Size, std::vector *Value) { if (Size < 1) return Size; size_t Len = std::min(static_cast(*Data), Size - 1); std::vector V(Data + 1, Data + 1 + Len); Value->swap(V); return Size - Len - 1; } template <> size_t UnpackSingle(const uint8_t *Data, size_t Size, std::string *Value) { if (Size < 1) return Size; size_t Len = std::min(static_cast(*Data), Size - 1); std::string S(Data + 1, Data + 1 + Len); Value->swap(S); return Size - Len - 1; } // Unpacking into arbitrary tuple. // Recursion guard. template typename std::enable_if::value, bool>::type UnpackImpl(const uint8_t *Data, size_t Size, TupleT *Tuple) { return true; } // Unpack tuple elements starting from Nth. template typename std::enable_if::value, bool>::type UnpackImpl(const uint8_t *Data, size_t Size, TupleT *Tuple) { size_t NewSize = UnpackSingle(Data, Size, &std::get(*Tuple)); if (NewSize == Size) { return false; } return UnpackImpl(Data + (Size - NewSize), NewSize, Tuple); } // Unpacks into arbitrary tuple and returns true if successful. template bool Unpack(const uint8_t *Data, size_t Size, std::tuple *Tuple) { return UnpackImpl<0, std::tuple>(Data, Size, Tuple); } // Helper integer sequence templates. template struct Seq {}; template struct GenSeq : GenSeq {}; // GenSeq::type is Seq<0, 1, ..., N-1> template struct GenSeq<0, S...> { typedef Seq type; }; // Function signature introspection. template struct FnTraits {}; template struct FnTraits { enum { Arity = sizeof...(Args) }; typedef std::tuple ArgsTupleT; }; // Calling a function with arguments in a tuple. template void ApplyImpl(Fn F, const typename FnTraits::ArgsTupleT &Params, Seq) { F(std::get(Params)...); } template void Apply(Fn F, const typename FnTraits::ArgsTupleT &Params) { // S is Seq<0, ..., Arity-1> auto S = typename GenSeq::Arity>::type(); ApplyImpl(F, Params, S); } // Unpacking data into arguments tuple of correct type and calling the function. template bool UnpackAndApply(Fn F, const uint8_t *Data, size_t Size) { typename FnTraits::ArgsTupleT Tuple; if (!Unpack(Data, Size, &Tuple)) return false; Apply(F, Tuple); return true; } } // namespace impl template bool Adapt(Fn F, const uint8_t *Data, size_t Size) { return impl::UnpackAndApply(F, Data, Size); } } // namespace fuzzer #endif