Index: lib/Fuzzer/FuzzerFnAdapter.h =================================================================== --- lib/Fuzzer/FuzzerFnAdapter.h +++ lib/Fuzzer/FuzzerFnAdapter.h @@ -15,13 +15,18 @@ #ifndef LLVM_FUZZER_ADAPTER_H #define LLVM_FUZZER_ADAPTER_H +#include #include #include +#include #include +#include #include #include +#include "FuzzerInterface.h" + namespace fuzzer { /// Unpacks bytes from \p Data according to \p F argument types @@ -31,13 +36,18 @@ /// Supported argument types: primitive types, std::vector. template bool Adapt(Fn F, const uint8_t *Data, size_t Size); +/// Mutates given \p Data acoording to \p F argument types. +template +size_t AdaptMutate(Fn F, uint8_t *Data, size_t Size, size_t MaxSize, + unsigned int Seed); + // 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. +// Single argument packing/unpacking. template size_t UnpackPrimitive(const uint8_t *Data, size_t Size, T *Value) { @@ -47,39 +57,69 @@ return Size - sizeof(T); } +template +size_t PackPrimitive(const T &Value, uint8_t *Data, size_t Size) { + if (Size < sizeof(T)) + return Size; + *reinterpret_cast(Data) = Value; + 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) \ +/// Packs the \p Value into \p Data and return Size - num_written_size; +template +size_t PackSingle(const T &Value, uint8_t *Data, size_t Size); + +// Mutates single primitive value. +template size_t MutatePrimitive(T *Value) { + Mutate(reinterpret_cast(Value), sizeof(T), sizeof(T)); + return sizeof(T); +} + +// Mutates single value. +template size_t MutateSingle(T *Value, size_t MaxSize); + +#define ADAPT_PRIMITIVE_TYPE(Type) \ template <> \ size_t UnpackSingle(const uint8_t *Data, size_t Size, Type *Value) { \ return UnpackPrimitive(Data, Size, Value); \ + } \ + template <> \ + size_t PackSingle(const Type &Value, uint8_t *Data, size_t Size) { \ + return PackPrimitive(Value, Data, Size); \ + } \ + template <> size_t MutateSingle(Type * Value, size_t MaxSize) { \ + return MutatePrimitive(Value); \ } -UNPACK_SINGLE_PRIMITIVE(char) -UNPACK_SINGLE_PRIMITIVE(signed char) -UNPACK_SINGLE_PRIMITIVE(unsigned char) +ADAPT_PRIMITIVE_TYPE(char) +ADAPT_PRIMITIVE_TYPE(signed char) +ADAPT_PRIMITIVE_TYPE(unsigned char) -UNPACK_SINGLE_PRIMITIVE(short int) -UNPACK_SINGLE_PRIMITIVE(unsigned short int) +ADAPT_PRIMITIVE_TYPE(short int) +ADAPT_PRIMITIVE_TYPE(unsigned short int) -UNPACK_SINGLE_PRIMITIVE(int) -UNPACK_SINGLE_PRIMITIVE(unsigned int) +ADAPT_PRIMITIVE_TYPE(int) +ADAPT_PRIMITIVE_TYPE(unsigned int) -UNPACK_SINGLE_PRIMITIVE(long int) -UNPACK_SINGLE_PRIMITIVE(unsigned long int) +ADAPT_PRIMITIVE_TYPE(long int) +ADAPT_PRIMITIVE_TYPE(unsigned long int) -UNPACK_SINGLE_PRIMITIVE(bool) -UNPACK_SINGLE_PRIMITIVE(wchar_t) +ADAPT_PRIMITIVE_TYPE(bool) +ADAPT_PRIMITIVE_TYPE(wchar_t) -UNPACK_SINGLE_PRIMITIVE(float) -UNPACK_SINGLE_PRIMITIVE(double) -UNPACK_SINGLE_PRIMITIVE(long double) +ADAPT_PRIMITIVE_TYPE(float) +ADAPT_PRIMITIVE_TYPE(double) +ADAPT_PRIMITIVE_TYPE(long double) -#undef UNPACK_SINGLE_PRIMITIVE +#undef ADAPT_PRIMITIVE_TYPE + +// std::vector specializations. template <> size_t UnpackSingle>(const uint8_t *Data, size_t Size, @@ -93,8 +133,33 @@ } template <> +size_t PackSingle>(const std::vector &Value, + uint8_t *Data, size_t Size) { + size_t Len = Value.size(); + if (Size < 1 + Len) + return Size; + assert(Len < 256); + *Data = static_cast(Len); + memcpy(Data + 1, Value.data(), Len); + return Size - Len - 1; +} + +template <> +size_t MutateSingle>(std::vector *Value, + size_t MaxSize) { + size_t Len = Value->size(); + Value->resize(MaxSize); + size_t NewLen = + Mutate(reinterpret_cast(&((*Value)[0u])), Len, MaxSize); + Value->resize(NewLen); + return NewLen; +} + +// std::string specializations. + +template <> size_t UnpackSingle(const uint8_t *Data, size_t Size, - std::string *Value) { + std::string *Value) { if (Size < 1) return Size; size_t Len = std::min(static_cast(*Data), Size - 1); @@ -103,6 +168,28 @@ return Size - Len - 1; } +template <> +size_t PackSingle(const std::string &Value, uint8_t *Data, + size_t Size) { + size_t Len = Value.size(); + if (Size < 1 + Len) + return Size; + assert(Len < 256); + *Data = static_cast(Len); + memcpy(Data + 1, Value.c_str(), Len); + return Size - Len - 1; +} + +template <> +size_t MutateSingle(std::string *Value, size_t MaxSize) { + size_t Len = Value->size(); + Value->resize(MaxSize); + size_t NewLen = + Mutate(reinterpret_cast(&((*Value)[0u])), Len, MaxSize); + Value->resize(NewLen); + return NewLen; +} + // Unpacking into arbitrary tuple. // Recursion guard. @@ -130,6 +217,33 @@ return UnpackImpl<0, std::tuple>(Data, Size, Tuple); } +// Pack aribtrary tuple into bytes. + +// recursion guard. +template +typename std::enable_if::value, size_t>::type +PackImpl(const TupleT &Tuple, uint8_t *Data, size_t Size) { + return Size; +} + +// Unpack tuple elements starting from Nth. +template +typename std::enable_if::value, size_t>::type +PackImpl(const TupleT &Tuple, uint8_t *Data, size_t Size) { + size_t NewSize = PackSingle(std::get(Tuple), Data, Size); + if (NewSize == Size) { + return Size; + } + + return (Size - NewSize) + + PackImpl(Tuple, Data + (Size - NewSize), NewSize); +} + +template +size_t Pack(const std::tuple &Tuple, uint8_t *Data, size_t Size) { + return PackImpl<0, std::tuple>(Tuple, Data, Size); +} + // Helper integer sequence templates. template struct Seq {}; @@ -175,12 +289,61 @@ return true; } +using MutatorFn = std::function; + +// Build MutatorFns for each member of a tuple. + +// Recursion guard. +template +typename std::enable_if::type +BuildMutators(std::vector *Mutators, std::tuple *Tuple) {} + +template +typename std::enable_if::type +BuildMutators(std::vector *Mutators, std::tuple *Tuple) { + auto ValuePtr = &std::get(*Tuple); + Mutators->push_back( + [ValuePtr](size_t MaxSize) { return MutateSingle(ValuePtr, MaxSize); }); + BuildMutators(Mutators, Tuple); +} + +template +void Mutate(std::tuple *Tuple, size_t MaxSize, unsigned int Seed) { + std::mt19937 Random(Seed); + + std::vector Mutators; + BuildMutators<0, Args...>(&Mutators, Tuple); + + int Iters = static_cast(Random() % 10) + 1; + for (int Iter = 0; Iter < Iters; Iter++) { + size_t FieldIdx = Random() % sizeof...(Args); + Mutators[FieldIdx](MaxSize); + } +} + +template +size_t AdaptMutate(Fn F, uint8_t *Data, size_t Size, size_t MaxSize, + unsigned int Seed) { + typename FnTraits::ArgsTupleT Tuple; + Unpack(Data, Size, &Tuple); + Mutate(&Tuple, MaxSize, Seed); + size_t NewSize = Pack(Tuple, Data, MaxSize); + assert(NewSize > 0); + return NewSize; +} + } // namespace impl template bool Adapt(Fn F, const uint8_t *Data, size_t Size) { return impl::UnpackAndApply(F, Data, Size); } +template +size_t AdaptMutate(Fn F, uint8_t *Data, size_t Size, size_t MaxSize, + unsigned int Seed) { + return impl::AdaptMutate(F, Data, Size, MaxSize, Seed); +} + } // namespace fuzzer #endif Index: lib/Fuzzer/test/CMakeLists.txt =================================================================== --- lib/Fuzzer/test/CMakeLists.txt +++ lib/Fuzzer/test/CMakeLists.txt @@ -28,6 +28,7 @@ SimpleCmpTest SimpleDictionaryTest SimpleFnAdapterTest + SimpleFnAdapterMutatorTest SimpleHashTest SimpleTest StrcmpTest Index: lib/Fuzzer/test/SimpleFnAdapterMutatorTest.cpp =================================================================== --- /dev/null +++ lib/Fuzzer/test/SimpleFnAdapterMutatorTest.cpp @@ -0,0 +1,24 @@ +#include +#include + +#include "FuzzerInternal.h" +#include "FuzzerFnAdapter.h" + +static void TestFn(std::string S1, std::string S2) { + if (S1.size() > 0 && S1 == S2) { + std::cout << "BINGO; Found the target, exiting\n"; + exit(0); + } +} + +extern "C" int LLVMFuzzerTestOneInput(const uint8_t *Data, size_t Size) { + fuzzer::Adapt(TestFn, Data, Size); + return 0; +} + + +extern "C" size_t LLVMFuzzerCustomMutator(uint8_t *Data, size_t Size, + size_t MaxSize, unsigned int Seed) { + return fuzzer::AdaptMutate(TestFn, Data, Size, MaxSize, Seed); +} + Index: lib/Fuzzer/test/fuzzer-fn-adapter-mutator.test =================================================================== --- /dev/null +++ lib/Fuzzer/test/fuzzer-fn-adapter-mutator.test @@ -0,0 +1,4 @@ +RUN: LLVMFuzzer-SimpleFnAdapterMutatorTest 2>&1 | FileCheck %s + +CHECK: BINGO +