1 // Copyright (c) 2019 Google LLC
2 //
3 // Licensed under the Apache License, Version 2.0 (the "License");
4 // you may not use this file except in compliance with the License.
5 // You may obtain a copy of the License at
6 //
7 //     http://www.apache.org/licenses/LICENSE-2.0
8 //
9 // Unless required by applicable law or agreed to in writing, software
10 // distributed under the License is distributed on an "AS IS" BASIS,
11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 // See the License for the specific language governing permissions and
13 // limitations under the License.
14 
15 #ifndef SOURCE_FUZZ_FUZZER_H_
16 #define SOURCE_FUZZ_FUZZER_H_
17 
18 #include <memory>
19 #include <utility>
20 #include <vector>
21 
22 #include "source/fuzz/fuzzer_context.h"
23 #include "source/fuzz/fuzzer_pass.h"
24 #include "source/fuzz/fuzzer_util.h"
25 #include "source/fuzz/pass_management/repeated_pass_instances.h"
26 #include "source/fuzz/pass_management/repeated_pass_recommender.h"
27 #include "source/fuzz/protobufs/spirvfuzz_protobufs.h"
28 #include "source/fuzz/random_generator.h"
29 #include "source/opt/ir_context.h"
30 #include "spirv-tools/libspirv.hpp"
31 
32 namespace spvtools {
33 namespace fuzz {
34 
35 // Transforms a SPIR-V module into a semantically equivalent SPIR-V module by
36 // running a number of randomized fuzzer passes.
37 class Fuzzer {
38  public:
39   // Possible statuses that can result from running the fuzzer.
40   enum class FuzzerResultStatus {
41     kComplete,
42     kFailedToCreateSpirvToolsInterface,
43     kFuzzerPassLedToInvalidModule,
44     kInitialBinaryInvalid,
45   };
46 
47   struct FuzzerResult {
48     FuzzerResultStatus status;
49     std::vector<uint32_t> transformed_binary;
50     protobufs::TransformationSequence applied_transformations;
51   };
52 
53   // Each field of this enum corresponds to an available repeated pass
54   // strategy, and is used to decide which kind of RepeatedPassManager object
55   // to create.
56   enum class RepeatedPassStrategy {
57     kSimple,
58     kRandomWithRecommendations,
59     kLoopedWithRecommendations
60   };
61 
62   Fuzzer(spv_target_env target_env, MessageConsumer consumer,
63          const std::vector<uint32_t>& binary_in,
64          const protobufs::FactSequence& initial_facts,
65          const std::vector<fuzzerutil::ModuleSupplier>& donor_suppliers,
66          std::unique_ptr<RandomGenerator> random_generator,
67          bool enable_all_passes, RepeatedPassStrategy repeated_pass_strategy,
68          bool validate_after_each_fuzzer_pass,
69          spv_validator_options validator_options);
70 
71   // Disables copy/move constructor/assignment operations.
72   Fuzzer(const Fuzzer&) = delete;
73   Fuzzer(Fuzzer&&) = delete;
74   Fuzzer& operator=(const Fuzzer&) = delete;
75   Fuzzer& operator=(Fuzzer&&) = delete;
76 
77   ~Fuzzer();
78 
79   // Transforms |binary_in_| by running a number of randomized fuzzer passes.
80   // Initial facts about the input binary and the context in which it will
81   // execute are provided via |initial_facts_|.  A source of donor modules to be
82   // used by transformations is provided via |donor_suppliers_|.  On success,
83   // returns a successful result status together with the transformed binary and
84   // the sequence of transformations that were applied.  Otherwise, returns an
85   // appropriate result status together with an empty binary and empty
86   // transformation sequence.
87   FuzzerResult Run();
88 
89  private:
90   // A convenience method to add a repeated fuzzer pass to |pass_instances| with
91   // probability |percentage_chance_of_adding_pass|%, or with probability 100%
92   // if |enable_all_passes_| is true.
93   //
94   // All fuzzer passes take members |ir_context_|, |transformation_context_|,
95   // |fuzzer_context_| and |transformation_sequence_out_| as parameters.  Extra
96   // arguments can be provided via |extra_args|.
97   template <typename FuzzerPassT, typename... Args>
98   void MaybeAddRepeatedPass(uint32_t percentage_chance_of_adding_pass,
99                             RepeatedPassInstances* pass_instances,
100                             Args&&... extra_args);
101 
102   // The same as the above, with |percentage_chance_of_adding_pass| == 50%.
103   template <typename FuzzerPassT, typename... Args>
MaybeAddRepeatedPass(RepeatedPassInstances * pass_instances,Args &&...extra_args)104   void MaybeAddRepeatedPass(RepeatedPassInstances* pass_instances,
105                             Args&&... extra_args) {
106     MaybeAddRepeatedPass<FuzzerPassT>(50, pass_instances,
107                                       std::forward<Args>(extra_args)...);
108   }
109 
110   // A convenience method to add a final fuzzer pass to |passes| with
111   // probability 50%, or with probability 100% if |enable_all_passes_| is true.
112   //
113   // All fuzzer passes take members |ir_context_|, |transformation_context_|,
114   // |fuzzer_context_| and |transformation_sequence_out_| as parameters.  Extra
115   // arguments can be provided via |extra_args|.
116   template <typename FuzzerPassT, typename... Args>
117   void MaybeAddFinalPass(std::vector<std::unique_ptr<FuzzerPass>>* passes,
118                          Args&&... extra_args);
119 
120   // Decides whether to apply more repeated passes. The probability decreases as
121   // the number of transformations that have been applied increases.
122   bool ShouldContinueFuzzing();
123 
124   // Applies |pass|, which must be a pass constructed with |ir_context|.
125   // If |validate_after_each_fuzzer_pass_| is not set, true is always returned.
126   // Otherwise, true is returned if and only if |ir_context| passes validation,
127   // every block has its enclosing function as its parent, and every
128   // instruction has a distinct unique id.
129   bool ApplyPassAndCheckValidity(FuzzerPass* pass) const;
130 
131   // Target environment.
132   const spv_target_env target_env_;
133 
134   // Message consumer that will be invoked once for each message communicated
135   // from the library.
136   MessageConsumer consumer_;
137 
138   // The initial binary to which fuzzing should be applied.
139   const std::vector<uint32_t>& binary_in_;
140 
141   // Initial facts known to hold in advance of applying any transformations.
142   const protobufs::FactSequence& initial_facts_;
143 
144   // A source of modules whose contents can be donated into the module being
145   // fuzzed.
146   const std::vector<fuzzerutil::ModuleSupplier>& donor_suppliers_;
147 
148   // Random number generator to control decision making during fuzzing.
149   std::unique_ptr<RandomGenerator> random_generator_;
150 
151   // Determines whether all passes should be enabled, vs. having passes be
152   // probabilistically enabled.
153   bool enable_all_passes_;
154 
155   // Controls which type of RepeatedPassManager object to create.
156   RepeatedPassStrategy repeated_pass_strategy_;
157 
158   // Determines whether the validator should be invoked after every fuzzer pass.
159   bool validate_after_each_fuzzer_pass_;
160 
161   // Options to control validation.
162   spv_validator_options validator_options_;
163 
164   // The number of repeated fuzzer passes that have been applied is kept track
165   // of, in order to enforce a hard limit on the number of times such passes
166   // can be applied.
167   uint32_t num_repeated_passes_applied_;
168 
169   // Intermediate representation for the module being fuzzed, which gets
170   // mutated as fuzzing proceeds.
171   std::unique_ptr<opt::IRContext> ir_context_;
172 
173   // Provides probabilities that control the fuzzing process.
174   std::unique_ptr<FuzzerContext> fuzzer_context_;
175 
176   // Contextual information that is required in order to apply transformations.
177   std::unique_ptr<TransformationContext> transformation_context_;
178 
179   // The sequence of transformations that have been applied during fuzzing.  It
180   // is initially empty and grows as fuzzer passes are applied.
181   protobufs::TransformationSequence transformation_sequence_out_;
182 };
183 
184 }  // namespace fuzz
185 }  // namespace spvtools
186 
187 #endif  // SOURCE_FUZZ_FUZZER_H_
188