• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 //===- 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 <string>
23 #include <tuple>
24 #include <vector>
25 
26 namespace fuzzer {
27 
28 /// Unpacks bytes from \p Data according to \p F argument types
29 /// and calls the function.
30 /// Use to automatically adapt LLVMFuzzerTestOneInput interface to
31 /// a specific function.
32 /// Supported argument types: primitive types, std::vector<uint8_t>.
33 template <typename Fn> bool Adapt(Fn F, const uint8_t *Data, size_t Size);
34 
35 // The implementation performs several steps:
36 // - function argument types are obtained (Args...)
37 // - data is unpacked into std::tuple<Args...> one by one
38 // - function is called with std::tuple<Args...> containing arguments.
39 namespace impl {
40 
41 // Single argument unpacking.
42 
43 template <typename T>
UnpackPrimitive(const uint8_t * Data,size_t Size,T * Value)44 size_t UnpackPrimitive(const uint8_t *Data, size_t Size, T *Value) {
45   if (Size < sizeof(T))
46     return Size;
47   *Value = *reinterpret_cast<const T *>(Data);
48   return Size - sizeof(T);
49 }
50 
51 /// Unpacks into a given Value and returns the Size - num_consumed_bytes.
52 /// Return value equal to Size signals inability to unpack the data (typically
53 /// because there are not enough bytes).
54 template <typename T>
55 size_t UnpackSingle(const uint8_t *Data, size_t Size, T *Value);
56 
57 #define UNPACK_SINGLE_PRIMITIVE(Type)                                          \
58   template <>                                                                  \
59   size_t UnpackSingle<Type>(const uint8_t *Data, size_t Size, Type *Value) {   \
60     return UnpackPrimitive(Data, Size, Value);                                 \
61   }
62 
63 UNPACK_SINGLE_PRIMITIVE(char)
UNPACK_SINGLE_PRIMITIVE(signed char)64 UNPACK_SINGLE_PRIMITIVE(signed char)
65 UNPACK_SINGLE_PRIMITIVE(unsigned char)
66 
67 UNPACK_SINGLE_PRIMITIVE(short int)
68 UNPACK_SINGLE_PRIMITIVE(unsigned short int)
69 
70 UNPACK_SINGLE_PRIMITIVE(int)
71 UNPACK_SINGLE_PRIMITIVE(unsigned int)
72 
73 UNPACK_SINGLE_PRIMITIVE(long int)
74 UNPACK_SINGLE_PRIMITIVE(unsigned long int)
75 
76 UNPACK_SINGLE_PRIMITIVE(bool)
77 UNPACK_SINGLE_PRIMITIVE(wchar_t)
78 
79 UNPACK_SINGLE_PRIMITIVE(float)
80 UNPACK_SINGLE_PRIMITIVE(double)
81 UNPACK_SINGLE_PRIMITIVE(long double)
82 
83 #undef UNPACK_SINGLE_PRIMITIVE
84 
85 template <>
86 size_t UnpackSingle<std::vector<uint8_t>>(const uint8_t *Data, size_t Size,
87                                           std::vector<uint8_t> *Value) {
88   if (Size < 1)
89     return Size;
90   size_t Len = std::min(static_cast<size_t>(*Data), Size - 1);
91   std::vector<uint8_t> V(Data + 1, Data + 1 + Len);
92   Value->swap(V);
93   return Size - Len - 1;
94 }
95 
96 template <>
97 size_t UnpackSingle<std::string>(const uint8_t *Data, size_t Size,
98     std::string *Value) {
99   if (Size < 1)
100     return Size;
101   size_t Len = std::min(static_cast<size_t>(*Data), Size - 1);
102   std::string S(Data + 1, Data + 1 + Len);
103   Value->swap(S);
104   return Size - Len - 1;
105 }
106 
107 // Unpacking into arbitrary tuple.
108 
109 // Recursion guard.
110 template <int N, typename TupleT>
111 typename std::enable_if<N == std::tuple_size<TupleT>::value, bool>::type
UnpackImpl(const uint8_t * Data,size_t Size,TupleT * Tuple)112 UnpackImpl(const uint8_t *Data, size_t Size, TupleT *Tuple) {
113   return true;
114 }
115 
116 // Unpack tuple elements starting from Nth.
117 template <int N, typename TupleT>
118 typename std::enable_if<N < std::tuple_size<TupleT>::value, bool>::type
119 UnpackImpl(const uint8_t *Data, size_t Size, TupleT *Tuple) {
120   size_t NewSize = UnpackSingle(Data, Size, &std::get<N>(*Tuple));
121   if (NewSize == Size) {
122     return false;
123   }
124 
125   return UnpackImpl<N + 1, TupleT>(Data + (Size - NewSize), NewSize, Tuple);
126 }
127 
128 // Unpacks into arbitrary tuple and returns true if successful.
129 template <typename... Args>
Unpack(const uint8_t * Data,size_t Size,std::tuple<Args...> * Tuple)130 bool Unpack(const uint8_t *Data, size_t Size, std::tuple<Args...> *Tuple) {
131   return UnpackImpl<0, std::tuple<Args...>>(Data, Size, Tuple);
132 }
133 
134 // Helper integer sequence templates.
135 
136 template <int...> struct Seq {};
137 
138 template <int N, int... S> struct GenSeq : GenSeq<N - 1, N - 1, S...> {};
139 
140 // GenSeq<N>::type is Seq<0, 1, ..., N-1>
141 template <int... S> struct GenSeq<0, S...> { typedef Seq<S...> type; };
142 
143 // Function signature introspection.
144 
145 template <typename T> struct FnTraits {};
146 
147 template <typename ReturnType, typename... Args>
148 struct FnTraits<ReturnType (*)(Args...)> {
149   enum { Arity = sizeof...(Args) };
150   typedef std::tuple<Args...> ArgsTupleT;
151 };
152 
153 // Calling a function with arguments in a tuple.
154 
155 template <typename Fn, int... S>
156 void ApplyImpl(Fn F, const typename FnTraits<Fn>::ArgsTupleT &Params,
157                Seq<S...>) {
158   F(std::get<S>(Params)...);
159 }
160 
161 template <typename Fn>
162 void Apply(Fn F, const typename FnTraits<Fn>::ArgsTupleT &Params) {
163   // S is Seq<0, ..., Arity-1>
164   auto S = typename GenSeq<FnTraits<Fn>::Arity>::type();
165   ApplyImpl(F, Params, S);
166 }
167 
168 // Unpacking data into arguments tuple of correct type and calling the function.
169 template <typename Fn>
170 bool UnpackAndApply(Fn F, const uint8_t *Data, size_t Size) {
171   typename FnTraits<Fn>::ArgsTupleT Tuple;
172   if (!Unpack(Data, Size, &Tuple))
173     return false;
174 
175   Apply(F, Tuple);
176   return true;
177 }
178 
179 } // namespace impl
180 
181 template <typename Fn> bool Adapt(Fn F, const uint8_t *Data, size_t Size) {
182   return impl::UnpackAndApply(F, Data, Size);
183 }
184 
185 } // namespace fuzzer
186 
187 #endif
188