1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
|
#pragma once
#ifdef __GNUC__
#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Wunused-parameter"
#endif
//===-- IRMutator.h - Mutation engine for fuzzing IR ------------*- C++ -*-===//
//
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
// See https://llvm.org/LICENSE.txt for license information.
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//
//
// Provides the IRMutator class, which drives mutations on IR based on a
// configurable set of strategies. Some common strategies are also included
// here.
//
// Fuzzer-friendly (de)serialization functions are also provided, as these
// are usually needed when mutating IR.
//
//===----------------------------------------------------------------------===//
#ifndef LLVM_FUZZMUTATE_IRMUTATOR_H
#define LLVM_FUZZMUTATE_IRMUTATOR_H
#include "llvm/FuzzMutate/OpDescriptor.h"
#include "llvm/Support/ErrorHandling.h"
#include <optional>
namespace llvm {
class BasicBlock;
class Function;
class Instruction;
class Module;
struct RandomIRBuilder;
/// Base class for describing how to mutate a module. mutation functions for
/// each IR unit forward to the contained unit.
class IRMutationStrategy {
public:
virtual ~IRMutationStrategy() = default;
/// Provide a weight to bias towards choosing this strategy for a mutation.
///
/// The value of the weight is arbitrary, but a good default is "the number of
/// distinct ways in which this strategy can mutate a unit". This can also be
/// used to prefer strategies that shrink the overall size of the result when
/// we start getting close to \c MaxSize.
virtual uint64_t getWeight(size_t CurrentSize, size_t MaxSize,
uint64_t CurrentWeight) = 0;
/// @{
/// Mutators for each IR unit. By default these forward to a contained
/// instance of the next smaller unit.
virtual void mutate(Module &M, RandomIRBuilder &IB);
virtual void mutate(Function &F, RandomIRBuilder &IB);
virtual void mutate(BasicBlock &BB, RandomIRBuilder &IB);
virtual void mutate(Instruction &I, RandomIRBuilder &IB) {
llvm_unreachable("Strategy does not implement any mutators");
}
/// @}
};
using TypeGetter = std::function<Type *(LLVMContext &)>;
/// Entry point for configuring and running IR mutations.
class IRMutator {
std::vector<TypeGetter> AllowedTypes;
std::vector<std::unique_ptr<IRMutationStrategy>> Strategies;
public:
IRMutator(std::vector<TypeGetter> &&AllowedTypes,
std::vector<std::unique_ptr<IRMutationStrategy>> &&Strategies)
: AllowedTypes(std::move(AllowedTypes)),
Strategies(std::move(Strategies)) {}
void mutateModule(Module &M, int Seed, size_t CurSize, size_t MaxSize);
};
/// Strategy that injects operations into the function.
class InjectorIRStrategy : public IRMutationStrategy {
std::vector<fuzzerop::OpDescriptor> Operations;
std::optional<fuzzerop::OpDescriptor> chooseOperation(Value *Src,
RandomIRBuilder &IB);
public:
InjectorIRStrategy(std::vector<fuzzerop::OpDescriptor> &&Operations)
: Operations(std::move(Operations)) {}
static std::vector<fuzzerop::OpDescriptor> getDefaultOps();
uint64_t getWeight(size_t CurrentSize, size_t MaxSize,
uint64_t CurrentWeight) override {
return Operations.size();
}
using IRMutationStrategy::mutate;
void mutate(Function &F, RandomIRBuilder &IB) override;
void mutate(BasicBlock &BB, RandomIRBuilder &IB) override;
};
/// Strategy that deletes instructions when the Module is too large.
class InstDeleterIRStrategy : public IRMutationStrategy {
public:
uint64_t getWeight(size_t CurrentSize, size_t MaxSize,
uint64_t CurrentWeight) override;
using IRMutationStrategy::mutate;
void mutate(Function &F, RandomIRBuilder &IB) override;
void mutate(Instruction &Inst, RandomIRBuilder &IB) override;
};
/// Strategy that modifies instruction attributes and operands.
class InstModificationIRStrategy : public IRMutationStrategy {
public:
uint64_t getWeight(size_t CurrentSize, size_t MaxSize,
uint64_t CurrentWeight) override {
return 4;
}
using IRMutationStrategy::mutate;
void mutate(Instruction &Inst, RandomIRBuilder &IB) override;
};
/// Strategy to split a random block and insert a random CFG in between.
class InsertCFGStrategy : public IRMutationStrategy {
private:
uint64_t MaxNumCases;
enum CFGToSink { Return, DirectSink, SinkOrSelfLoop, EndOfCFGToLink };
public:
InsertCFGStrategy(uint64_t MNC = 8) : MaxNumCases(MNC){};
uint64_t getWeight(size_t CurrentSize, size_t MaxSize,
uint64_t CurrentWeight) override {
return 5;
}
void mutate(BasicBlock &BB, RandomIRBuilder &IB) override;
private:
void connectBlocksToSink(ArrayRef<BasicBlock *> Blocks, BasicBlock *Sink,
RandomIRBuilder &IB);
};
/// Strategy to insert PHI Nodes at the head of each basic block.
class InsertPHIStrategy : public IRMutationStrategy {
public:
uint64_t getWeight(size_t CurrentSize, size_t MaxSize,
uint64_t CurrentWeight) override {
return 2;
}
void mutate(BasicBlock &BB, RandomIRBuilder &IB) override;
};
/// Strategy to select a random instruction and add a new sink (user) to it to
/// increate data dependency.
class SinkInstructionStrategy : public IRMutationStrategy {
public:
uint64_t getWeight(size_t CurrentSize, size_t MaxSize,
uint64_t CurrentWeight) override {
return 2;
}
void mutate(Function &F, RandomIRBuilder &IB) override;
void mutate(BasicBlock &BB, RandomIRBuilder &IB) override;
};
/// Strategy to randomly select a block and shuffle the operations without
/// affecting data dependency.
class ShuffleBlockStrategy : public IRMutationStrategy {
public:
uint64_t getWeight(size_t CurrentSize, size_t MaxSize,
uint64_t CurrentWeight) override {
return 2;
}
void mutate(BasicBlock &BB, RandomIRBuilder &IB) override;
};
/// Fuzzer friendly interface for the llvm bitcode parser.
///
/// \param Data Bitcode we are going to parse
/// \param Size Size of the 'Data' in bytes
/// \return New module or nullptr in case of error
std::unique_ptr<Module> parseModule(const uint8_t *Data, size_t Size,
LLVMContext &Context);
/// Fuzzer friendly interface for the llvm bitcode printer.
///
/// \param M Module to print
/// \param Dest Location to store serialized module
/// \param MaxSize Size of the destination buffer
/// \return Number of bytes that were written. When module size exceeds MaxSize
/// returns 0 and leaves Dest unchanged.
size_t writeModule(const Module &M, uint8_t *Dest, size_t MaxSize);
/// Try to parse module and verify it. May output verification errors to the
/// errs().
/// \return New module or nullptr in case of error.
std::unique_ptr<Module> parseAndVerify(const uint8_t *Data, size_t Size,
LLVMContext &Context);
} // namespace llvm
#endif // LLVM_FUZZMUTATE_IRMUTATOR_H
#ifdef __GNUC__
#pragma GCC diagnostic pop
#endif
|