LLVM 19.0.0git
IRMutator.h
Go to the documentation of this file.
1//===-- IRMutator.h - Mutation engine for fuzzing IR ------------*- C++ -*-===//
2//
3// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4// See https://llvm.org/LICENSE.txt for license information.
5// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6//
7//===----------------------------------------------------------------------===//
8//
9// Provides the IRMutator class, which drives mutations on IR based on a
10// configurable set of strategies. Some common strategies are also included
11// here.
12//
13// Fuzzer-friendly (de)serialization functions are also provided, as these
14// are usually needed when mutating IR.
15//
16//===----------------------------------------------------------------------===//
17
18#ifndef LLVM_FUZZMUTATE_IRMUTATOR_H
19#define LLVM_FUZZMUTATE_IRMUTATOR_H
20
23#include <optional>
24
25namespace llvm {
26class BasicBlock;
27class Function;
28class Instruction;
29class Module;
30
31struct RandomIRBuilder;
32
33/// Base class for describing how to mutate a module. mutation functions for
34/// each IR unit forward to the contained unit.
36public:
37 virtual ~IRMutationStrategy() = default;
38
39 /// Provide a weight to bias towards choosing this strategy for a mutation.
40 ///
41 /// The value of the weight is arbitrary, but a good default is "the number of
42 /// distinct ways in which this strategy can mutate a unit". This can also be
43 /// used to prefer strategies that shrink the overall size of the result when
44 /// we start getting close to \c MaxSize.
45 virtual uint64_t getWeight(size_t CurrentSize, size_t MaxSize,
46 uint64_t CurrentWeight) = 0;
47
48 /// @{
49 /// Mutators for each IR unit. By default these forward to a contained
50 /// instance of the next smaller unit.
51 virtual void mutate(Module &M, RandomIRBuilder &IB);
52 virtual void mutate(Function &F, RandomIRBuilder &IB);
53 virtual void mutate(BasicBlock &BB, RandomIRBuilder &IB);
54 virtual void mutate(Instruction &I, RandomIRBuilder &IB) {
55 llvm_unreachable("Strategy does not implement any mutators");
56 }
57 /// @}
58};
59
60using TypeGetter = std::function<Type *(LLVMContext &)>;
61
62/// Entry point for configuring and running IR mutations.
63class IRMutator {
64 std::vector<TypeGetter> AllowedTypes;
65 std::vector<std::unique_ptr<IRMutationStrategy>> Strategies;
66
67public:
68 IRMutator(std::vector<TypeGetter> &&AllowedTypes,
69 std::vector<std::unique_ptr<IRMutationStrategy>> &&Strategies)
70 : AllowedTypes(std::move(AllowedTypes)),
71 Strategies(std::move(Strategies)) {}
72
73 /// Calculate the size of module as the number of objects in it, i.e.
74 /// instructions, basic blocks, functions, and aliases.
75 ///
76 /// \param M module
77 /// \return number of objects in module
78 static size_t getModuleSize(const Module &M);
79
80 /// Mutate given module. No change will be made if no strategy is selected.
81 ///
82 /// \param M module to mutate
83 /// \param Seed seed for random mutation
84 /// \param MaxSize max module size (see getModuleSize)
85 void mutateModule(Module &M, int Seed, size_t MaxSize);
86};
87
88/// Strategy that injects operations into the function.
90 std::vector<fuzzerop::OpDescriptor> Operations;
91
92 std::optional<fuzzerop::OpDescriptor> chooseOperation(Value *Src,
93 RandomIRBuilder &IB);
94
95public:
96 InjectorIRStrategy() : Operations(getDefaultOps()) {}
97 InjectorIRStrategy(std::vector<fuzzerop::OpDescriptor> &&Operations)
98 : Operations(std::move(Operations)) {}
99 static std::vector<fuzzerop::OpDescriptor> getDefaultOps();
100
101 uint64_t getWeight(size_t CurrentSize, size_t MaxSize,
102 uint64_t CurrentWeight) override {
103 return Operations.size();
104 }
105
107 void mutate(Function &F, RandomIRBuilder &IB) override;
108 void mutate(BasicBlock &BB, RandomIRBuilder &IB) override;
109};
110
111/// Strategy that deletes instructions when the Module is too large.
113public:
114 uint64_t getWeight(size_t CurrentSize, size_t MaxSize,
115 uint64_t CurrentWeight) override;
116
118 void mutate(Function &F, RandomIRBuilder &IB) override;
119 void mutate(Instruction &Inst, RandomIRBuilder &IB) override;
120};
121
122/// Strategy that modifies instruction attributes and operands.
124public:
125 uint64_t getWeight(size_t CurrentSize, size_t MaxSize,
126 uint64_t CurrentWeight) override {
127 return 4;
128 }
129
131 void mutate(Instruction &Inst, RandomIRBuilder &IB) override;
132};
133
134/// Strategy that generates new function calls and inserts function signatures
135/// to the modules. If any signatures are present in the module it will be
136/// called.
138public:
139 uint64_t getWeight(size_t CurrentSize, size_t MaxSize,
140 uint64_t CurrentWeight) override {
141 return 10;
142 }
143
145 void mutate(BasicBlock &BB, RandomIRBuilder &IB) override;
146};
147
148/// Strategy to split a random block and insert a random CFG in between.
150private:
151 uint64_t MaxNumCases;
152 enum CFGToSink { Return, DirectSink, SinkOrSelfLoop, EndOfCFGToLink };
153
154public:
155 InsertCFGStrategy(uint64_t MNC = 8) : MaxNumCases(MNC){};
156 uint64_t getWeight(size_t CurrentSize, size_t MaxSize,
157 uint64_t CurrentWeight) override {
158 return 5;
159 }
160
161 void mutate(BasicBlock &BB, RandomIRBuilder &IB) override;
162
163private:
164 void connectBlocksToSink(ArrayRef<BasicBlock *> Blocks, BasicBlock *Sink,
165 RandomIRBuilder &IB);
166};
167
168/// Strategy to insert PHI Nodes at the head of each basic block.
170public:
171 uint64_t getWeight(size_t CurrentSize, size_t MaxSize,
172 uint64_t CurrentWeight) override {
173 return 2;
174 }
175
176 void mutate(BasicBlock &BB, RandomIRBuilder &IB) override;
177};
178
179/// Strategy to select a random instruction and add a new sink (user) to it to
180/// increate data dependency.
182public:
183 uint64_t getWeight(size_t CurrentSize, size_t MaxSize,
184 uint64_t CurrentWeight) override {
185 return 2;
186 }
187
188 void mutate(Function &F, RandomIRBuilder &IB) override;
189 void mutate(BasicBlock &BB, RandomIRBuilder &IB) override;
190};
191
192/// Strategy to randomly select a block and shuffle the operations without
193/// affecting data dependency.
195public:
196 uint64_t getWeight(size_t CurrentSize, size_t MaxSize,
197 uint64_t CurrentWeight) override {
198 return 2;
199 }
200
201 void mutate(BasicBlock &BB, RandomIRBuilder &IB) override;
202};
203
204/// Fuzzer friendly interface for the llvm bitcode parser.
205///
206/// \param Data Bitcode we are going to parse
207/// \param Size Size of the 'Data' in bytes
208/// \return New module or nullptr in case of error
209std::unique_ptr<Module> parseModule(const uint8_t *Data, size_t Size,
210 LLVMContext &Context);
211
212/// Fuzzer friendly interface for the llvm bitcode printer.
213///
214/// \param M Module to print
215/// \param Dest Location to store serialized module
216/// \param MaxSize Size of the destination buffer
217/// \return Number of bytes that were written. When module size exceeds MaxSize
218/// returns 0 and leaves Dest unchanged.
219size_t writeModule(const Module &M, uint8_t *Dest, size_t MaxSize);
220
221/// Try to parse module and verify it. May output verification errors to the
222/// errs().
223/// \return New module or nullptr in case of error.
224std::unique_ptr<Module> parseAndVerify(const uint8_t *Data, size_t Size,
225 LLVMContext &Context);
226
227} // namespace llvm
228
229#endif // LLVM_FUZZMUTATE_IRMUTATOR_H
uint64_t Size
DenseMap< Block *, BlockRelaxAux > Blocks
Definition: ELF_riscv.cpp:507
#define F(x, y, z)
Definition: MD5.cpp:55
#define I(x, y, z)
Definition: MD5.cpp:58
Machine Check Debug Module
static ManagedStatic< cl::opt< uint64_t >, CreateSeed > Seed
ArrayRef - Represent a constant reference to an array (0 or more elements consecutively in memory),...
Definition: ArrayRef.h:41
LLVM Basic Block Representation.
Definition: BasicBlock.h:60
Base class for describing how to mutate a module.
Definition: IRMutator.h:35
virtual void mutate(Instruction &I, RandomIRBuilder &IB)
Definition: IRMutator.h:54
virtual uint64_t getWeight(size_t CurrentSize, size_t MaxSize, uint64_t CurrentWeight)=0
Provide a weight to bias towards choosing this strategy for a mutation.
virtual ~IRMutationStrategy()=default
virtual void mutate(Module &M, RandomIRBuilder &IB)
Definition: IRMutator.cpp:35
Entry point for configuring and running IR mutations.
Definition: IRMutator.h:63
IRMutator(std::vector< TypeGetter > &&AllowedTypes, std::vector< std::unique_ptr< IRMutationStrategy > > &&Strategies)
Definition: IRMutator.h:68
void mutateModule(Module &M, int Seed, size_t MaxSize)
Mutate given module.
Definition: IRMutator.cpp:63
static size_t getModuleSize(const Module &M)
Calculate the size of module as the number of objects in it, i.e.
Definition: IRMutator.cpp:59
Strategy that injects operations into the function.
Definition: IRMutator.h:89
static std::vector< fuzzerop::OpDescriptor > getDefaultOps()
Definition: IRMutator.cpp:95
uint64_t getWeight(size_t CurrentSize, size_t MaxSize, uint64_t CurrentWeight) override
Provide a weight to bias towards choosing this strategy for a mutation.
Definition: IRMutator.h:101
void mutate(Function &F, RandomIRBuilder &IB) override
Definition: IRMutator.cpp:90
InjectorIRStrategy(std::vector< fuzzerop::OpDescriptor > &&Operations)
Definition: IRMutator.h:97
Strategy to split a random block and insert a random CFG in between.
Definition: IRMutator.h:149
void mutate(BasicBlock &BB, RandomIRBuilder &IB) override
Definition: IRMutator.cpp:422
uint64_t getWeight(size_t CurrentSize, size_t MaxSize, uint64_t CurrentWeight) override
Provide a weight to bias towards choosing this strategy for a mutation.
Definition: IRMutator.h:156
InsertCFGStrategy(uint64_t MNC=8)
Definition: IRMutator.h:155
Strategy that generates new function calls and inserts function signatures to the modules.
Definition: IRMutator.h:137
uint64_t getWeight(size_t CurrentSize, size_t MaxSize, uint64_t CurrentWeight) override
Provide a weight to bias towards choosing this strategy for a mutation.
Definition: IRMutator.h:139
void mutate(BasicBlock &BB, RandomIRBuilder &IB) override
Definition: IRMutator.cpp:359
Strategy to insert PHI Nodes at the head of each basic block.
Definition: IRMutator.h:169
uint64_t getWeight(size_t CurrentSize, size_t MaxSize, uint64_t CurrentWeight) override
Provide a weight to bias towards choosing this strategy for a mutation.
Definition: IRMutator.h:171
void mutate(BasicBlock &BB, RandomIRBuilder &IB) override
Definition: IRMutator.cpp:540
Strategy that deletes instructions when the Module is too large.
Definition: IRMutator.h:112
uint64_t getWeight(size_t CurrentSize, size_t MaxSize, uint64_t CurrentWeight) override
Provide a weight to bias towards choosing this strategy for a mutation.
Definition: IRMutator.cpp:156
void mutate(Function &F, RandomIRBuilder &IB) override
Definition: IRMutator.cpp:173
Strategy that modifies instruction attributes and operands.
Definition: IRMutator.h:123
void mutate(Instruction &Inst, RandomIRBuilder &IB) override
Definition: IRMutator.cpp:221
uint64_t getWeight(size_t CurrentSize, size_t MaxSize, uint64_t CurrentWeight) override
Provide a weight to bias towards choosing this strategy for a mutation.
Definition: IRMutator.h:125
This is an important class for using LLVM in a threaded context.
Definition: LLVMContext.h:67
A Module instance is used to store all the information related to an LLVM module.
Definition: Module.h:65
Strategy to randomly select a block and shuffle the operations without affecting data dependency.
Definition: IRMutator.h:194
uint64_t getWeight(size_t CurrentSize, size_t MaxSize, uint64_t CurrentWeight) override
Provide a weight to bias towards choosing this strategy for a mutation.
Definition: IRMutator.h:196
void mutate(BasicBlock &BB, RandomIRBuilder &IB) override
Definition: IRMutator.cpp:592
Strategy to select a random instruction and add a new sink (user) to it to increate data dependency.
Definition: IRMutator.h:181
void mutate(Function &F, RandomIRBuilder &IB) override
Definition: IRMutator.cpp:569
uint64_t getWeight(size_t CurrentSize, size_t MaxSize, uint64_t CurrentWeight) override
Provide a weight to bias towards choosing this strategy for a mutation.
Definition: IRMutator.h:183
The instances of the Type class are immutable: once they are created, they are never changed.
Definition: Type.h:45
LLVM Value Representation.
Definition: Value.h:74
#define llvm_unreachable(msg)
Marks that the current location is not supposed to be reachable.
@ BasicBlock
Various leaf nodes.
Definition: ISDOpcodes.h:71
This is an optimization pass for GlobalISel generic memory operations.
Definition: AddressRanges.h:18
std::unique_ptr< Module > parseAndVerify(const uint8_t *Data, size_t Size, LLVMContext &Context)
Try to parse module and verify it.
Definition: IRMutator.cpp:696
std::function< Type *(LLVMContext &)> TypeGetter
Definition: IRMutator.h:60
std::unique_ptr< Module > parseModule(const uint8_t *Data, size_t Size, LLVMContext &Context)
Fuzzer friendly interface for the llvm bitcode parser.
Definition: IRMutator.cpp:664
size_t writeModule(const Module &M, uint8_t *Dest, size_t MaxSize)
Fuzzer friendly interface for the llvm bitcode printer.
Definition: IRMutator.cpp:684
OutputIt move(R &&Range, OutputIt Out)
Provide wrappers to std::move which take ranges instead of having to pass begin/end explicitly.
Definition: STLExtras.h:1858
Implement std::hash so that hash_code can be used in STL containers.
Definition: BitVector.h:858