17d449d31SJustin Bogner //===-- IRMutator.h - Mutation engine for fuzzing IR ------------*- C++ -*-===// 27d449d31SJustin Bogner // 32946cd70SChandler Carruth // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. 42946cd70SChandler Carruth // See https://llvm.org/LICENSE.txt for license information. 52946cd70SChandler Carruth // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception 67d449d31SJustin Bogner // 77d449d31SJustin Bogner //===----------------------------------------------------------------------===// 87d449d31SJustin Bogner // 97d449d31SJustin Bogner // Provides the IRMutator class, which drives mutations on IR based on a 107d449d31SJustin Bogner // configurable set of strategies. Some common strategies are also included 117d449d31SJustin Bogner // here. 127d449d31SJustin Bogner // 130a83ff83SSam McCall // Fuzzer-friendly (de)serialization functions are also provided, as these 140a83ff83SSam McCall // are usually needed when mutating IR. 150a83ff83SSam McCall // 167d449d31SJustin Bogner //===----------------------------------------------------------------------===// 177d449d31SJustin Bogner 187d449d31SJustin Bogner #ifndef LLVM_FUZZMUTATE_IRMUTATOR_H 197d449d31SJustin Bogner #define LLVM_FUZZMUTATE_IRMUTATOR_H 207d449d31SJustin Bogner 217d449d31SJustin Bogner #include "llvm/FuzzMutate/OpDescriptor.h" 227d449d31SJustin Bogner #include "llvm/Support/ErrorHandling.h" 23da6642a1SKazu Hirata #include <optional> 247d449d31SJustin Bogner 257d449d31SJustin Bogner namespace llvm { 267d449d31SJustin Bogner class BasicBlock; 277d449d31SJustin Bogner class Function; 287d449d31SJustin Bogner class Instruction; 297d449d31SJustin Bogner class Module; 307d449d31SJustin Bogner 317d449d31SJustin Bogner struct RandomIRBuilder; 327d449d31SJustin Bogner 337d449d31SJustin Bogner /// Base class for describing how to mutate a module. mutation functions for 347d449d31SJustin Bogner /// each IR unit forward to the contained unit. 357d449d31SJustin Bogner class IRMutationStrategy { 367d449d31SJustin Bogner public: 377d449d31SJustin Bogner virtual ~IRMutationStrategy() = default; 387d449d31SJustin Bogner 397d449d31SJustin Bogner /// Provide a weight to bias towards choosing this strategy for a mutation. 407d449d31SJustin Bogner /// 417d449d31SJustin Bogner /// The value of the weight is arbitrary, but a good default is "the number of 427d449d31SJustin Bogner /// distinct ways in which this strategy can mutate a unit". This can also be 437d449d31SJustin Bogner /// used to prefer strategies that shrink the overall size of the result when 447d449d31SJustin Bogner /// we start getting close to \c MaxSize. 457d449d31SJustin Bogner virtual uint64_t getWeight(size_t CurrentSize, size_t MaxSize, 467d449d31SJustin Bogner uint64_t CurrentWeight) = 0; 477d449d31SJustin Bogner 487d449d31SJustin Bogner /// @{ 497d449d31SJustin Bogner /// Mutators for each IR unit. By default these forward to a contained 507d449d31SJustin Bogner /// instance of the next smaller unit. 517d449d31SJustin Bogner virtual void mutate(Module &M, RandomIRBuilder &IB); 527d449d31SJustin Bogner virtual void mutate(Function &F, RandomIRBuilder &IB); 537d449d31SJustin Bogner virtual void mutate(BasicBlock &BB, RandomIRBuilder &IB); mutate(Instruction & I,RandomIRBuilder & IB)547d449d31SJustin Bogner virtual void mutate(Instruction &I, RandomIRBuilder &IB) { 557d449d31SJustin Bogner llvm_unreachable("Strategy does not implement any mutators"); 567d449d31SJustin Bogner } 577d449d31SJustin Bogner /// @} 587d449d31SJustin Bogner }; 597d449d31SJustin Bogner 607d449d31SJustin Bogner using TypeGetter = std::function<Type *(LLVMContext &)>; 617d449d31SJustin Bogner 627d449d31SJustin Bogner /// Entry point for configuring and running IR mutations. 637d449d31SJustin Bogner class IRMutator { 647d449d31SJustin Bogner std::vector<TypeGetter> AllowedTypes; 657d449d31SJustin Bogner std::vector<std::unique_ptr<IRMutationStrategy>> Strategies; 667d449d31SJustin Bogner 677d449d31SJustin Bogner public: IRMutator(std::vector<TypeGetter> && AllowedTypes,std::vector<std::unique_ptr<IRMutationStrategy>> && Strategies)687d449d31SJustin Bogner IRMutator(std::vector<TypeGetter> &&AllowedTypes, 697d449d31SJustin Bogner std::vector<std::unique_ptr<IRMutationStrategy>> &&Strategies) 707d449d31SJustin Bogner : AllowedTypes(std::move(AllowedTypes)), 717d449d31SJustin Bogner Strategies(std::move(Strategies)) {} 727d449d31SJustin Bogner 73*39b6a7f0SZhenkai Weng /// Calculate the size of module as the number of objects in it, i.e. 74*39b6a7f0SZhenkai Weng /// instructions, basic blocks, functions, and aliases. 75*39b6a7f0SZhenkai Weng /// 76*39b6a7f0SZhenkai Weng /// \param M module 77*39b6a7f0SZhenkai Weng /// \return number of objects in module 78*39b6a7f0SZhenkai Weng static size_t getModuleSize(const Module &M); 79*39b6a7f0SZhenkai Weng 80*39b6a7f0SZhenkai Weng /// Mutate given module. No change will be made if no strategy is selected. 81*39b6a7f0SZhenkai Weng /// 82*39b6a7f0SZhenkai Weng /// \param M module to mutate 83*39b6a7f0SZhenkai Weng /// \param Seed seed for random mutation 84*39b6a7f0SZhenkai Weng /// \param MaxSize max module size (see getModuleSize) 85*39b6a7f0SZhenkai Weng void mutateModule(Module &M, int Seed, size_t MaxSize); 867d449d31SJustin Bogner }; 877d449d31SJustin Bogner 887d449d31SJustin Bogner /// Strategy that injects operations into the function. 897d449d31SJustin Bogner class InjectorIRStrategy : public IRMutationStrategy { 907d449d31SJustin Bogner std::vector<fuzzerop::OpDescriptor> Operations; 917d449d31SJustin Bogner 9277c90c8cSKazu Hirata std::optional<fuzzerop::OpDescriptor> chooseOperation(Value *Src, 93ce6f2d01SIgor Laevsky RandomIRBuilder &IB); 947d449d31SJustin Bogner 957d449d31SJustin Bogner public: InjectorIRStrategy()9666892f25SHenry Yu InjectorIRStrategy() : Operations(getDefaultOps()) {} InjectorIRStrategy(std::vector<fuzzerop::OpDescriptor> && Operations)977d449d31SJustin Bogner InjectorIRStrategy(std::vector<fuzzerop::OpDescriptor> &&Operations) 987d449d31SJustin Bogner : Operations(std::move(Operations)) {} 997d449d31SJustin Bogner static std::vector<fuzzerop::OpDescriptor> getDefaultOps(); 1007d449d31SJustin Bogner getWeight(size_t CurrentSize,size_t MaxSize,uint64_t CurrentWeight)1017d449d31SJustin Bogner uint64_t getWeight(size_t CurrentSize, size_t MaxSize, 1027d449d31SJustin Bogner uint64_t CurrentWeight) override { 1037d449d31SJustin Bogner return Operations.size(); 1047d449d31SJustin Bogner } 1057d449d31SJustin Bogner 1067d449d31SJustin Bogner using IRMutationStrategy::mutate; 1077d449d31SJustin Bogner void mutate(Function &F, RandomIRBuilder &IB) override; 1087d449d31SJustin Bogner void mutate(BasicBlock &BB, RandomIRBuilder &IB) override; 1097d449d31SJustin Bogner }; 1107d449d31SJustin Bogner 111db2aa9f2SPeter Rong /// Strategy that deletes instructions when the Module is too large. 1127d449d31SJustin Bogner class InstDeleterIRStrategy : public IRMutationStrategy { 1137d449d31SJustin Bogner public: 1147d449d31SJustin Bogner uint64_t getWeight(size_t CurrentSize, size_t MaxSize, 1157d449d31SJustin Bogner uint64_t CurrentWeight) override; 1167d449d31SJustin Bogner 1177d449d31SJustin Bogner using IRMutationStrategy::mutate; 1187d449d31SJustin Bogner void mutate(Function &F, RandomIRBuilder &IB) override; 1197d449d31SJustin Bogner void mutate(Instruction &Inst, RandomIRBuilder &IB) override; 1207d449d31SJustin Bogner }; 1217d449d31SJustin Bogner 122db2aa9f2SPeter Rong /// Strategy that modifies instruction attributes and operands. 123166d40f2SFlorian Hahn class InstModificationIRStrategy : public IRMutationStrategy { 124166d40f2SFlorian Hahn public: getWeight(size_t CurrentSize,size_t MaxSize,uint64_t CurrentWeight)125166d40f2SFlorian Hahn uint64_t getWeight(size_t CurrentSize, size_t MaxSize, 126166d40f2SFlorian Hahn uint64_t CurrentWeight) override { 127166d40f2SFlorian Hahn return 4; 128166d40f2SFlorian Hahn } 129166d40f2SFlorian Hahn 130166d40f2SFlorian Hahn using IRMutationStrategy::mutate; 131166d40f2SFlorian Hahn void mutate(Instruction &Inst, RandomIRBuilder &IB) override; 132166d40f2SFlorian Hahn }; 133166d40f2SFlorian Hahn 1346998b34cSPeter Rong /// Strategy that generates new function calls and inserts function signatures 1356998b34cSPeter Rong /// to the modules. If any signatures are present in the module it will be 1366998b34cSPeter Rong /// called. 1376998b34cSPeter Rong class InsertFunctionStrategy : public IRMutationStrategy { 1386998b34cSPeter Rong public: getWeight(size_t CurrentSize,size_t MaxSize,uint64_t CurrentWeight)1396998b34cSPeter Rong uint64_t getWeight(size_t CurrentSize, size_t MaxSize, 1406998b34cSPeter Rong uint64_t CurrentWeight) override { 1416998b34cSPeter Rong return 10; 1426998b34cSPeter Rong } 1436998b34cSPeter Rong 1446998b34cSPeter Rong using IRMutationStrategy::mutate; 1456998b34cSPeter Rong void mutate(BasicBlock &BB, RandomIRBuilder &IB) override; 1466998b34cSPeter Rong }; 1476998b34cSPeter Rong 148bc277eb1SPeter Rong /// Strategy to split a random block and insert a random CFG in between. 149bc277eb1SPeter Rong class InsertCFGStrategy : public IRMutationStrategy { 150bc277eb1SPeter Rong private: 151bc277eb1SPeter Rong uint64_t MaxNumCases; 152bc277eb1SPeter Rong enum CFGToSink { Return, DirectSink, SinkOrSelfLoop, EndOfCFGToLink }; 153bc277eb1SPeter Rong 154bc277eb1SPeter Rong public: MaxNumCases(MNC)155bc277eb1SPeter Rong InsertCFGStrategy(uint64_t MNC = 8) : MaxNumCases(MNC){}; getWeight(size_t CurrentSize,size_t MaxSize,uint64_t CurrentWeight)156bc277eb1SPeter Rong uint64_t getWeight(size_t CurrentSize, size_t MaxSize, 157bc277eb1SPeter Rong uint64_t CurrentWeight) override { 158bc277eb1SPeter Rong return 5; 159bc277eb1SPeter Rong } 160bc277eb1SPeter Rong 161bc277eb1SPeter Rong void mutate(BasicBlock &BB, RandomIRBuilder &IB) override; 162bc277eb1SPeter Rong 163bc277eb1SPeter Rong private: 164bc277eb1SPeter Rong void connectBlocksToSink(ArrayRef<BasicBlock *> Blocks, BasicBlock *Sink, 165bc277eb1SPeter Rong RandomIRBuilder &IB); 166bc277eb1SPeter Rong }; 167bc277eb1SPeter Rong 1684be08734SPeter Rong /// Strategy to insert PHI Nodes at the head of each basic block. 1694be08734SPeter Rong class InsertPHIStrategy : public IRMutationStrategy { 1704be08734SPeter Rong public: getWeight(size_t CurrentSize,size_t MaxSize,uint64_t CurrentWeight)1714be08734SPeter Rong uint64_t getWeight(size_t CurrentSize, size_t MaxSize, 1724be08734SPeter Rong uint64_t CurrentWeight) override { 1734be08734SPeter Rong return 2; 1744be08734SPeter Rong } 1754be08734SPeter Rong 1764be08734SPeter Rong void mutate(BasicBlock &BB, RandomIRBuilder &IB) override; 1774be08734SPeter Rong }; 1784be08734SPeter Rong 17943db7cb4SPeter Rong /// Strategy to select a random instruction and add a new sink (user) to it to 18043db7cb4SPeter Rong /// increate data dependency. 18143db7cb4SPeter Rong class SinkInstructionStrategy : public IRMutationStrategy { 18243db7cb4SPeter Rong public: getWeight(size_t CurrentSize,size_t MaxSize,uint64_t CurrentWeight)18343db7cb4SPeter Rong uint64_t getWeight(size_t CurrentSize, size_t MaxSize, 18443db7cb4SPeter Rong uint64_t CurrentWeight) override { 18543db7cb4SPeter Rong return 2; 18643db7cb4SPeter Rong } 18743db7cb4SPeter Rong 18843db7cb4SPeter Rong void mutate(Function &F, RandomIRBuilder &IB) override; 18943db7cb4SPeter Rong void mutate(BasicBlock &BB, RandomIRBuilder &IB) override; 19043db7cb4SPeter Rong }; 19143db7cb4SPeter Rong 192a7def9f7SPeter Rong /// Strategy to randomly select a block and shuffle the operations without 193a7def9f7SPeter Rong /// affecting data dependency. 194a7def9f7SPeter Rong class ShuffleBlockStrategy : public IRMutationStrategy { 195a7def9f7SPeter Rong public: getWeight(size_t CurrentSize,size_t MaxSize,uint64_t CurrentWeight)196a7def9f7SPeter Rong uint64_t getWeight(size_t CurrentSize, size_t MaxSize, 197a7def9f7SPeter Rong uint64_t CurrentWeight) override { 198a7def9f7SPeter Rong return 2; 199a7def9f7SPeter Rong } 200a7def9f7SPeter Rong 201a7def9f7SPeter Rong void mutate(BasicBlock &BB, RandomIRBuilder &IB) override; 202a7def9f7SPeter Rong }; 203a7def9f7SPeter Rong 2040a83ff83SSam McCall /// Fuzzer friendly interface for the llvm bitcode parser. 2050a83ff83SSam McCall /// 2060a83ff83SSam McCall /// \param Data Bitcode we are going to parse 2070a83ff83SSam McCall /// \param Size Size of the 'Data' in bytes 2080a83ff83SSam McCall /// \return New module or nullptr in case of error 2090a83ff83SSam McCall std::unique_ptr<Module> parseModule(const uint8_t *Data, size_t Size, 2100a83ff83SSam McCall LLVMContext &Context); 2110a83ff83SSam McCall 2120a83ff83SSam McCall /// Fuzzer friendly interface for the llvm bitcode printer. 2130a83ff83SSam McCall /// 2140a83ff83SSam McCall /// \param M Module to print 2150a83ff83SSam McCall /// \param Dest Location to store serialized module 2160a83ff83SSam McCall /// \param MaxSize Size of the destination buffer 2170a83ff83SSam McCall /// \return Number of bytes that were written. When module size exceeds MaxSize 2180a83ff83SSam McCall /// returns 0 and leaves Dest unchanged. 2190a83ff83SSam McCall size_t writeModule(const Module &M, uint8_t *Dest, size_t MaxSize); 2200a83ff83SSam McCall 2210a83ff83SSam McCall /// Try to parse module and verify it. May output verification errors to the 2220a83ff83SSam McCall /// errs(). 2230a83ff83SSam McCall /// \return New module or nullptr in case of error. 2240a83ff83SSam McCall std::unique_ptr<Module> parseAndVerify(const uint8_t *Data, size_t Size, 2250a83ff83SSam McCall LLVMContext &Context); 2260a83ff83SSam McCall 227db2aa9f2SPeter Rong } // namespace llvm 2287d449d31SJustin Bogner 2297d449d31SJustin Bogner #endif // LLVM_FUZZMUTATE_IRMUTATOR_H 230