xref: /freebsd/contrib/llvm-project/llvm/include/llvm/FuzzMutate/IRMutator.h (revision 700637cbb5e582861067a11aaca4d053546871d2)
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 
21 #include "llvm/FuzzMutate/OpDescriptor.h"
22 #include "llvm/Support/Compiler.h"
23 #include "llvm/Support/ErrorHandling.h"
24 #include <optional>
25 
26 namespace llvm {
27 class BasicBlock;
28 class Function;
29 class Instruction;
30 class Module;
31 
32 struct RandomIRBuilder;
33 
34 /// Base class for describing how to mutate a module. mutation functions for
35 /// each IR unit forward to the contained unit.
36 class LLVM_ABI IRMutationStrategy {
37 public:
38   virtual ~IRMutationStrategy() = default;
39 
40   /// Provide a weight to bias towards choosing this strategy for a mutation.
41   ///
42   /// The value of the weight is arbitrary, but a good default is "the number of
43   /// distinct ways in which this strategy can mutate a unit". This can also be
44   /// used to prefer strategies that shrink the overall size of the result when
45   /// we start getting close to \c MaxSize.
46   virtual uint64_t getWeight(size_t CurrentSize, size_t MaxSize,
47                              uint64_t CurrentWeight) = 0;
48 
49   /// @{
50   /// Mutators for each IR unit. By default these forward to a contained
51   /// instance of the next smaller unit.
52   virtual void mutate(Module &M, RandomIRBuilder &IB);
53   virtual void mutate(Function &F, RandomIRBuilder &IB);
54   virtual void mutate(BasicBlock &BB, RandomIRBuilder &IB);
mutate(Instruction & I,RandomIRBuilder & IB)55   virtual void mutate(Instruction &I, RandomIRBuilder &IB) {
56     llvm_unreachable("Strategy does not implement any mutators");
57   }
58   /// @}
59 };
60 
61 using TypeGetter = std::function<Type *(LLVMContext &)>;
62 
63 /// Entry point for configuring and running IR mutations.
64 class IRMutator {
65   std::vector<TypeGetter> AllowedTypes;
66   std::vector<std::unique_ptr<IRMutationStrategy>> Strategies;
67 
68 public:
IRMutator(std::vector<TypeGetter> && AllowedTypes,std::vector<std::unique_ptr<IRMutationStrategy>> && Strategies)69   IRMutator(std::vector<TypeGetter> &&AllowedTypes,
70             std::vector<std::unique_ptr<IRMutationStrategy>> &&Strategies)
71       : AllowedTypes(std::move(AllowedTypes)),
72         Strategies(std::move(Strategies)) {}
73 
74   /// Calculate the size of module as the number of objects in it, i.e.
75   /// instructions, basic blocks, functions, and aliases.
76   ///
77   /// \param M module
78   /// \return number of objects in module
79   LLVM_ABI static size_t getModuleSize(const Module &M);
80 
81   /// Mutate given module. No change will be made if no strategy is selected.
82   ///
83   /// \param M  module to mutate
84   /// \param Seed seed for random mutation
85   /// \param MaxSize max module size (see getModuleSize)
86   LLVM_ABI void mutateModule(Module &M, int Seed, size_t MaxSize);
87 };
88 
89 /// Strategy that injects operations into the function.
90 class LLVM_ABI InjectorIRStrategy : public IRMutationStrategy {
91   std::vector<fuzzerop::OpDescriptor> Operations;
92 
93   std::optional<fuzzerop::OpDescriptor> chooseOperation(Value *Src,
94                                                         RandomIRBuilder &IB);
95 
96 public:
InjectorIRStrategy()97   InjectorIRStrategy() : Operations(getDefaultOps()) {}
InjectorIRStrategy(std::vector<fuzzerop::OpDescriptor> && Operations)98   InjectorIRStrategy(std::vector<fuzzerop::OpDescriptor> &&Operations)
99       : Operations(std::move(Operations)) {}
100   static std::vector<fuzzerop::OpDescriptor> getDefaultOps();
101 
getWeight(size_t CurrentSize,size_t MaxSize,uint64_t CurrentWeight)102   uint64_t getWeight(size_t CurrentSize, size_t MaxSize,
103                      uint64_t CurrentWeight) override {
104     return Operations.size();
105   }
106 
107   using IRMutationStrategy::mutate;
108   void mutate(Function &F, RandomIRBuilder &IB) override;
109   void mutate(BasicBlock &BB, RandomIRBuilder &IB) override;
110 };
111 
112 /// Strategy that deletes instructions when the Module is too large.
113 class LLVM_ABI InstDeleterIRStrategy : public IRMutationStrategy {
114 public:
115   uint64_t getWeight(size_t CurrentSize, size_t MaxSize,
116                      uint64_t CurrentWeight) override;
117 
118   using IRMutationStrategy::mutate;
119   void mutate(Function &F, RandomIRBuilder &IB) override;
120   void mutate(Instruction &Inst, RandomIRBuilder &IB) override;
121 };
122 
123 /// Strategy that modifies instruction attributes and operands.
124 class LLVM_ABI InstModificationIRStrategy : public IRMutationStrategy {
125 public:
getWeight(size_t CurrentSize,size_t MaxSize,uint64_t CurrentWeight)126   uint64_t getWeight(size_t CurrentSize, size_t MaxSize,
127                      uint64_t CurrentWeight) override {
128     return 4;
129   }
130 
131   using IRMutationStrategy::mutate;
132   void mutate(Instruction &Inst, RandomIRBuilder &IB) override;
133 };
134 
135 /// Strategy that generates new function calls and inserts function signatures
136 /// to the modules. If any signatures are present in the module it will be
137 /// called.
138 class LLVM_ABI InsertFunctionStrategy : public IRMutationStrategy {
139 public:
getWeight(size_t CurrentSize,size_t MaxSize,uint64_t CurrentWeight)140   uint64_t getWeight(size_t CurrentSize, size_t MaxSize,
141                      uint64_t CurrentWeight) override {
142     return 10;
143   }
144 
145   using IRMutationStrategy::mutate;
146   void mutate(BasicBlock &BB, RandomIRBuilder &IB) override;
147 };
148 
149 /// Strategy to split a random block and insert a random CFG in between.
150 class LLVM_ABI InsertCFGStrategy : public IRMutationStrategy {
151 private:
152   uint64_t MaxNumCases;
153   enum CFGToSink { Return, DirectSink, SinkOrSelfLoop, EndOfCFGToLink };
154 
155 public:
MaxNumCases(MNC)156   InsertCFGStrategy(uint64_t MNC = 8) : MaxNumCases(MNC){};
getWeight(size_t CurrentSize,size_t MaxSize,uint64_t CurrentWeight)157   uint64_t getWeight(size_t CurrentSize, size_t MaxSize,
158                      uint64_t CurrentWeight) override {
159     return 5;
160   }
161 
162   void mutate(BasicBlock &BB, RandomIRBuilder &IB) override;
163 
164 private:
165   void connectBlocksToSink(ArrayRef<BasicBlock *> Blocks, BasicBlock *Sink,
166                            RandomIRBuilder &IB);
167 };
168 
169 /// Strategy to insert PHI Nodes at the head of each basic block.
170 class LLVM_ABI InsertPHIStrategy : public IRMutationStrategy {
171 public:
getWeight(size_t CurrentSize,size_t MaxSize,uint64_t CurrentWeight)172   uint64_t getWeight(size_t CurrentSize, size_t MaxSize,
173                      uint64_t CurrentWeight) override {
174     return 2;
175   }
176 
177   void mutate(BasicBlock &BB, RandomIRBuilder &IB) override;
178 };
179 
180 /// Strategy to select a random instruction and add a new sink (user) to it to
181 /// increate data dependency.
182 class LLVM_ABI SinkInstructionStrategy : public IRMutationStrategy {
183 public:
getWeight(size_t CurrentSize,size_t MaxSize,uint64_t CurrentWeight)184   uint64_t getWeight(size_t CurrentSize, size_t MaxSize,
185                      uint64_t CurrentWeight) override {
186     return 2;
187   }
188 
189   void mutate(Function &F, RandomIRBuilder &IB) override;
190   void mutate(BasicBlock &BB, RandomIRBuilder &IB) override;
191 };
192 
193 /// Strategy to randomly select a block and shuffle the operations without
194 /// affecting data dependency.
195 class LLVM_ABI ShuffleBlockStrategy : public IRMutationStrategy {
196 public:
getWeight(size_t CurrentSize,size_t MaxSize,uint64_t CurrentWeight)197   uint64_t getWeight(size_t CurrentSize, size_t MaxSize,
198                      uint64_t CurrentWeight) override {
199     return 2;
200   }
201 
202   void mutate(BasicBlock &BB, RandomIRBuilder &IB) override;
203 };
204 
205 /// Fuzzer friendly interface for the llvm bitcode parser.
206 ///
207 /// \param Data Bitcode we are going to parse
208 /// \param Size Size of the 'Data' in bytes
209 /// \return New module or nullptr in case of error
210 LLVM_ABI std::unique_ptr<Module> parseModule(const uint8_t *Data, size_t Size,
211                                              LLVMContext &Context);
212 
213 /// Fuzzer friendly interface for the llvm bitcode printer.
214 ///
215 /// \param M Module to print
216 /// \param Dest Location to store serialized module
217 /// \param MaxSize Size of the destination buffer
218 /// \return Number of bytes that were written. When module size exceeds MaxSize
219 ///         returns 0 and leaves Dest unchanged.
220 LLVM_ABI size_t writeModule(const Module &M, uint8_t *Dest, size_t MaxSize);
221 
222 /// Try to parse module and verify it. May output verification errors to the
223 /// errs().
224 /// \return New module or nullptr in case of error.
225 LLVM_ABI std::unique_ptr<Module>
226 parseAndVerify(const uint8_t *Data, size_t Size, LLVMContext &Context);
227 
228 } // namespace llvm
229 
230 #endif // LLVM_FUZZMUTATE_IRMUTATOR_H
231