blob: eb2c219b8703c4f3ac7ce903c6f399b0ad28fe1a [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>
Dan Liew9551fdd2016-05-26 21:54:25 +000022#include <string>
Mike Aizatskyb8627a82016-03-03 23:45:29 +000023#include <tuple>
24#include <vector>
25
26namespace 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>.
33template <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.
39namespace impl {
40
41// Single argument unpacking.
42
43template <typename T>
44size_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).
54template <typename T>
55size_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
63UNPACK_SINGLE_PRIMITIVE(char)
64UNPACK_SINGLE_PRIMITIVE(signed char)
65UNPACK_SINGLE_PRIMITIVE(unsigned char)
66
67UNPACK_SINGLE_PRIMITIVE(short int)
68UNPACK_SINGLE_PRIMITIVE(unsigned short int)
69
70UNPACK_SINGLE_PRIMITIVE(int)
71UNPACK_SINGLE_PRIMITIVE(unsigned int)
72
73UNPACK_SINGLE_PRIMITIVE(long int)
74UNPACK_SINGLE_PRIMITIVE(unsigned long int)
75
76UNPACK_SINGLE_PRIMITIVE(bool)
77UNPACK_SINGLE_PRIMITIVE(wchar_t)
78
79UNPACK_SINGLE_PRIMITIVE(float)
80UNPACK_SINGLE_PRIMITIVE(double)
81UNPACK_SINGLE_PRIMITIVE(long double)
82
83#undef UNPACK_SINGLE_PRIMITIVE
84
85template <>
86size_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
Mike Aizatsky243fe2b2016-03-04 23:18:01 +000096template <>
97size_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
Mike Aizatskyb8627a82016-03-03 23:45:29 +0000107// Unpacking into arbitrary tuple.
108
109// Recursion guard.
110template <int N, typename TupleT>
111typename std::enable_if<N == std::tuple_size<TupleT>::value, bool>::type
112UnpackImpl(const uint8_t *Data, size_t Size, TupleT *Tuple) {
113 return true;
114}
115
116// Unpack tuple elements starting from Nth.
117template <int N, typename TupleT>
118typename std::enable_if<N < std::tuple_size<TupleT>::value, bool>::type
119UnpackImpl(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.
129template <typename... Args>
130bool 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
136template <int...> struct Seq {};
137
138template <int N, int... S> struct GenSeq : GenSeq<N - 1, N - 1, S...> {};
139
140// GenSeq<N>::type is Seq<0, 1, ..., N-1>
141template <int... S> struct GenSeq<0, S...> { typedef Seq<S...> type; };
142
143// Function signature introspection.
144
145template <typename T> struct FnTraits {};
146
147template <typename ReturnType, typename... Args>
148struct 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
155template <typename Fn, int... S>
156void ApplyImpl(Fn F, const typename FnTraits<Fn>::ArgsTupleT &Params,
157 Seq<S...>) {
158 F(std::get<S>(Params)...);
159}
160
161template <typename Fn>
162void 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.
169template <typename Fn>
170bool 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
181template <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