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