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/ADT/Optional.h"
22 #include "llvm/FuzzMutate/OpDescriptor.h"
23 #include "llvm/Support/ErrorHandling.h"
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   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 class InstDeleterIRStrategy : public IRMutationStrategy {
99 public:
100   uint64_t getWeight(size_t CurrentSize, size_t MaxSize,
101                      uint64_t CurrentWeight) override;
102 
103   using IRMutationStrategy::mutate;
104   void mutate(Function &F, RandomIRBuilder &IB) override;
105   void mutate(Instruction &Inst, RandomIRBuilder &IB) override;
106 };
107 
108 class InstModificationIRStrategy : public IRMutationStrategy {
109 public:
110   uint64_t getWeight(size_t CurrentSize, size_t MaxSize,
111                      uint64_t CurrentWeight) override {
112     return 4;
113   }
114 
115   using IRMutationStrategy::mutate;
116   void mutate(Instruction &Inst, RandomIRBuilder &IB) override;
117 };
118 
119 /// Fuzzer friendly interface for the llvm bitcode parser.
120 ///
121 /// \param Data Bitcode we are going to parse
122 /// \param Size Size of the 'Data' in bytes
123 /// \return New module or nullptr in case of error
124 std::unique_ptr<Module> parseModule(const uint8_t *Data, size_t Size,
125                                     LLVMContext &Context);
126 
127 /// Fuzzer friendly interface for the llvm bitcode printer.
128 ///
129 /// \param M Module to print
130 /// \param Dest Location to store serialized module
131 /// \param MaxSize Size of the destination buffer
132 /// \return Number of bytes that were written. When module size exceeds MaxSize
133 ///         returns 0 and leaves Dest unchanged.
134 size_t writeModule(const Module &M, uint8_t *Dest, size_t MaxSize);
135 
136 /// Try to parse module and verify it. May output verification errors to the
137 /// errs().
138 /// \return New module or nullptr in case of error.
139 std::unique_ptr<Module> parseAndVerify(const uint8_t *Data, size_t Size,
140                                        LLVMContext &Context);
141 
142 } // end llvm namespace
143 
144 #endif // LLVM_FUZZMUTATE_IRMUTATOR_H
145