summaryrefslogtreecommitdiffstats
path: root/llvm/lib/Fuzzer/FuzzerFnAdapter.h
diff options
context:
space:
mode:
Diffstat (limited to 'llvm/lib/Fuzzer/FuzzerFnAdapter.h')
-rw-r--r--llvm/lib/Fuzzer/FuzzerFnAdapter.h175
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
OpenPOWER on IntegriCloud