Subversion Repositories QNX 8.QNX8 LLVM/Clang compiler suite

Rev

Blame | Last modification | View Log | Download | RSS feed

  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.