xref: /freebsd/contrib/llvm-project/llvm/include/llvm/FuzzMutate/IRMutator.h (revision 43e29d03f416d7dda52112a29600a7c82ee1a91e)
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/ErrorHandling.h"
23 #include <optional>
24 
25 namespace llvm {
26 class BasicBlock;
27 class Function;
28 class Instruction;
29 class Module;
30 
31 struct RandomIRBuilder;
32 
33 /// Base class for describing how to mutate a module. mutation functions for
34 /// each IR unit forward to the contained unit.
35 class IRMutationStrategy {
36 public:
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 
60 using TypeGetter = std::function<Type *(LLVMContext &)>;
61 
62 /// Entry point for configuring and running IR mutations.
63 class IRMutator {
64   std::vector<TypeGetter> AllowedTypes;
65   std::vector<std::unique_ptr<IRMutationStrategy>> Strategies;
66 
67 public:
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   void mutateModule(Module &M, int Seed, size_t CurSize, size_t MaxSize);
74 };
75 
76 /// Strategy that injects operations into the function.
77 class InjectorIRStrategy : public IRMutationStrategy {
78   std::vector<fuzzerop::OpDescriptor> Operations;
79 
80   std::optional<fuzzerop::OpDescriptor> chooseOperation(Value *Src,
81                                                         RandomIRBuilder &IB);
82 
83 public:
84   InjectorIRStrategy(std::vector<fuzzerop::OpDescriptor> &&Operations)
85       : Operations(std::move(Operations)) {}
86   static std::vector<fuzzerop::OpDescriptor> getDefaultOps();
87 
88   uint64_t getWeight(size_t CurrentSize, size_t MaxSize,
89                      uint64_t CurrentWeight) override {
90     return Operations.size();
91   }
92 
93   using IRMutationStrategy::mutate;
94   void mutate(Function &F, RandomIRBuilder &IB) override;
95   void mutate(BasicBlock &BB, RandomIRBuilder &IB) override;
96 };
97 
98 /// Strategy that deletes instructions when the Module is too large.
99 class InstDeleterIRStrategy : public IRMutationStrategy {
100 public:
101   uint64_t getWeight(size_t CurrentSize, size_t MaxSize,
102                      uint64_t CurrentWeight) override;
103 
104   using IRMutationStrategy::mutate;
105   void mutate(Function &F, RandomIRBuilder &IB) override;
106   void mutate(Instruction &Inst, RandomIRBuilder &IB) override;
107 };
108 
109 /// Strategy that modifies instruction attributes and operands.
110 class InstModificationIRStrategy : public IRMutationStrategy {
111 public:
112   uint64_t getWeight(size_t CurrentSize, size_t MaxSize,
113                      uint64_t CurrentWeight) override {
114     return 4;
115   }
116 
117   using IRMutationStrategy::mutate;
118   void mutate(Instruction &Inst, RandomIRBuilder &IB) override;
119 };
120 
121 /// Strategy to split a random block and insert a random CFG in between.
122 class InsertCFGStrategy : public IRMutationStrategy {
123 private:
124   uint64_t MaxNumCases;
125   enum CFGToSink { Return, DirectSink, SinkOrSelfLoop, EndOfCFGToLink };
126 
127 public:
128   InsertCFGStrategy(uint64_t MNC = 8) : MaxNumCases(MNC){};
129   uint64_t getWeight(size_t CurrentSize, size_t MaxSize,
130                      uint64_t CurrentWeight) override {
131     return 5;
132   }
133 
134   void mutate(BasicBlock &BB, RandomIRBuilder &IB) override;
135 
136 private:
137   void connectBlocksToSink(ArrayRef<BasicBlock *> Blocks, BasicBlock *Sink,
138                            RandomIRBuilder &IB);
139 };
140 
141 /// Strategy to insert PHI Nodes at the head of each basic block.
142 class InsertPHIStrategy : public IRMutationStrategy {
143 public:
144   uint64_t getWeight(size_t CurrentSize, size_t MaxSize,
145                      uint64_t CurrentWeight) override {
146     return 2;
147   }
148 
149   void mutate(BasicBlock &BB, RandomIRBuilder &IB) override;
150 };
151 
152 /// Strategy to select a random instruction and add a new sink (user) to it to
153 /// increate data dependency.
154 class SinkInstructionStrategy : public IRMutationStrategy {
155 public:
156   uint64_t getWeight(size_t CurrentSize, size_t MaxSize,
157                      uint64_t CurrentWeight) override {
158     return 2;
159   }
160 
161   void mutate(Function &F, RandomIRBuilder &IB) override;
162   void mutate(BasicBlock &BB, RandomIRBuilder &IB) override;
163 };
164 
165 /// Strategy to randomly select a block and shuffle the operations without
166 /// affecting data dependency.
167 class ShuffleBlockStrategy : public IRMutationStrategy {
168 public:
169   uint64_t getWeight(size_t CurrentSize, size_t MaxSize,
170                      uint64_t CurrentWeight) override {
171     return 2;
172   }
173 
174   void mutate(BasicBlock &BB, RandomIRBuilder &IB) override;
175 };
176 
177 /// Fuzzer friendly interface for the llvm bitcode parser.
178 ///
179 /// \param Data Bitcode we are going to parse
180 /// \param Size Size of the 'Data' in bytes
181 /// \return New module or nullptr in case of error
182 std::unique_ptr<Module> parseModule(const uint8_t *Data, size_t Size,
183                                     LLVMContext &Context);
184 
185 /// Fuzzer friendly interface for the llvm bitcode printer.
186 ///
187 /// \param M Module to print
188 /// \param Dest Location to store serialized module
189 /// \param MaxSize Size of the destination buffer
190 /// \return Number of bytes that were written. When module size exceeds MaxSize
191 ///         returns 0 and leaves Dest unchanged.
192 size_t writeModule(const Module &M, uint8_t *Dest, size_t MaxSize);
193 
194 /// Try to parse module and verify it. May output verification errors to the
195 /// errs().
196 /// \return New module or nullptr in case of error.
197 std::unique_ptr<Module> parseAndVerify(const uint8_t *Data, size_t Size,
198                                        LLVMContext &Context);
199 
200 } // namespace llvm
201 
202 #endif // LLVM_FUZZMUTATE_IRMUTATOR_H
203