blob: ae121d238dcc07b1c0e6adb388eb2ac543179d46 [file] [log] [blame]
Mike Aizatskyb8627a82016-03-03 23:45:29 +00001//===- FuzzerAdapter.h - Arbitrary function Fuzzer adapter -------*- C++ -*===//
2//
3// The LLVM Compiler Infrastructure
4//
5// This file is distributed under the University of Illinois Open Source
6// License. See LICENSE.TXT for details.
7//
8//===----------------------------------------------------------------------===//
9//
10// W A R N I N G : E X P E R I M E N T A L.
11//
12// Defines an adapter to fuzz functions with (almost) arbitrary signatures.
13//===----------------------------------------------------------------------===//
14
15#ifndef LLVM_FUZZER_ADAPTER_H
16#define LLVM_FUZZER_ADAPTER_H
17
18#include <stddef.h>
19#include <stdint.h>
20
21#include <algorithm>
22#include <tuple>
23#include <vector>
24
25namespace fuzzer {
26
27/// Unpacks bytes from \p Data according to \p F argument types
28/// and calls the function.
29/// Use to automatically adapt LLVMFuzzerTestOneInput interface to
30/// a specific function.
31/// Supported argument types: primitive types, std::vector<uint8_t>.
32template <typename Fn> bool Adapt(Fn F, const uint8_t *Data, size_t Size);
33
34// The implementation performs several steps:
35// - function argument types are obtained (Args...)
36// - data is unpacked into std::tuple<Args...> one by one
37// - function is called with std::tuple<Args...> containing arguments.
38namespace impl {
39
40// Single argument unpacking.
41
42template <typename T>
43size_t UnpackPrimitive(const uint8_t *Data, size_t Size, T *Value) {
44 if (Size < sizeof(T))
45 return Size;
46 *Value = *reinterpret_cast<const T *>(Data);
47 return Size - sizeof(T);
48}
49
50/// Unpacks into a given Value and returns the Size - num_consumed_bytes.
51/// Return value equal to Size signals inability to unpack the data (typically
52/// because there are not enough bytes).
53template <typename T>
54size_t UnpackSingle(const uint8_t *Data, size_t Size, T *Value);
55
56#define UNPACK_SINGLE_PRIMITIVE(Type) \
57 template <> \
58 size_t UnpackSingle<Type>(const uint8_t *Data, size_t Size, Type *Value) { \
59 return UnpackPrimitive(Data, Size, Value); \
60 }
61
62UNPACK_SINGLE_PRIMITIVE(char)
63UNPACK_SINGLE_PRIMITIVE(signed char)
64UNPACK_SINGLE_PRIMITIVE(unsigned char)
65
66UNPACK_SINGLE_PRIMITIVE(short int)
67UNPACK_SINGLE_PRIMITIVE(unsigned short int)
68
69UNPACK_SINGLE_PRIMITIVE(int)
70UNPACK_SINGLE_PRIMITIVE(unsigned int)
71
72UNPACK_SINGLE_PRIMITIVE(long int)
73UNPACK_SINGLE_PRIMITIVE(unsigned long int)
74
75UNPACK_SINGLE_PRIMITIVE(bool)
76UNPACK_SINGLE_PRIMITIVE(wchar_t)
77
78UNPACK_SINGLE_PRIMITIVE(float)
79UNPACK_SINGLE_PRIMITIVE(double)
80UNPACK_SINGLE_PRIMITIVE(long double)
81
82#undef UNPACK_SINGLE_PRIMITIVE
83
84template <>
85size_t UnpackSingle<std::vector<uint8_t>>(const uint8_t *Data, size_t Size,
86 std::vector<uint8_t> *Value) {
87 if (Size < 1)
88 return Size;
89 size_t Len = std::min(static_cast<size_t>(*Data), Size - 1);
90 std::vector<uint8_t> V(Data + 1, Data + 1 + Len);
91 Value->swap(V);
92 return Size - Len - 1;
93}
94
Mike Aizatsky243fe2b2016-03-04 23:18:01 +000095template <>
96size_t UnpackSingle<std::string>(const uint8_t *Data, size_t Size,
97 std::string *Value) {
98 if (Size < 1)
99 return Size;
100 size_t Len = std::min(static_cast<size_t>(*Data), Size - 1);
101 std::string S(Data + 1, Data + 1 + Len);
102 Value->swap(S);
103 return Size - Len - 1;
104}
105
Mike Aizatskyb8627a82016-03-03 23:45:29 +0000106// Unpacking into arbitrary tuple.
107
108// Recursion guard.
109template <int N, typename TupleT>
110typename std::enable_if<N == std::tuple_size<TupleT>::value, bool>::type
111UnpackImpl(const uint8_t *Data, size_t Size, TupleT *Tuple) {
112 return true;
113}
114
115// Unpack tuple elements starting from Nth.
116template <int N, typename TupleT>
117typename std::enable_if<N < std::tuple_size<TupleT>::value, bool>::type
118UnpackImpl(const uint8_t *Data, size_t Size, TupleT *Tuple) {
119 size_t NewSize = UnpackSingle(Data, Size, &std::get<N>(*Tuple));
120 if (NewSize == Size) {
121 return false;
122 }
123
124 return UnpackImpl<N + 1, TupleT>(Data + (Size - NewSize), NewSize, Tuple);
125}
126
127// Unpacks into arbitrary tuple and returns true if successful.
128template <typename... Args>
129bool Unpack(const uint8_t *Data, size_t Size, std::tuple<Args...> *Tuple) {
130 return UnpackImpl<0, std::tuple<Args...>>(Data, Size, Tuple);
131}
132
133// Helper integer sequence templates.
134
135template <int...> struct Seq {};
136
137template <int N, int... S> struct GenSeq : GenSeq<N - 1, N - 1, S...> {};
138
139// GenSeq<N>::type is Seq<0, 1, ..., N-1>
140template <int... S> struct GenSeq<0, S...> { typedef Seq<S...> type; };
141
142// Function signature introspection.
143
144template <typename T> struct FnTraits {};
145
146template <typename ReturnType, typename... Args>
147struct FnTraits<ReturnType (*)(Args...)> {
148 enum { Arity = sizeof...(Args) };
149 typedef std::tuple<Args...> ArgsTupleT;
150};
151
152// Calling a function with arguments in a tuple.
153
154template <typename Fn, int... S>
155void ApplyImpl(Fn F, const typename FnTraits<Fn>::ArgsTupleT &Params,
156 Seq<S...>) {
157 F(std::get<S>(Params)...);
158}
159
160template <typename Fn>
161void Apply(Fn F, const typename FnTraits<Fn>::ArgsTupleT &Params) {
162 // S is Seq<0, ..., Arity-1>
163 auto S = typename GenSeq<FnTraits<Fn>::Arity>::type();
164 ApplyImpl(F, Params, S);
165}
166
167// Unpacking data into arguments tuple of correct type and calling the function.
168template <typename Fn>
169bool UnpackAndApply(Fn F, const uint8_t *Data, size_t Size) {
170 typename FnTraits<Fn>::ArgsTupleT Tuple;
171 if (!Unpack(Data, Size, &Tuple))
172 return false;
173
174 Apply(F, Tuple);
175 return true;
176}
177
178} // namespace impl
179
180template <typename Fn> bool Adapt(Fn F, const uint8_t *Data, size_t Size) {
181 return impl::UnpackAndApply(F, Data, Size);
182}
183
184} // namespace fuzzer
185
186#endif