• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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_manager.h"
27 #include "source/fuzz/pass_management/repeated_pass_recommender.h"
28 #include "source/fuzz/protobufs/spirvfuzz_protobufs.h"
29 #include "source/fuzz/random_generator.h"
30 #include "source/opt/ir_context.h"
31 #include "spirv-tools/libspirv.hpp"
32 
33 namespace spvtools {
34 namespace fuzz {
35 
36 // Transforms a SPIR-V module into a semantically equivalent SPIR-V module by
37 // running a number of randomized fuzzer passes.
38 class Fuzzer {
39  public:
40   // Possible statuses that can result from running the fuzzer.
41   enum class Status {
42     kComplete,
43     kModuleTooBig,
44     kTransformationLimitReached,
45     kFuzzerStuck,
46     kFuzzerPassLedToInvalidModule,
47   };
48 
49   struct Result {
50     // Status of the fuzzing session.
51     Status status;
52 
53     // Equals to true if new transformations were applied during the previous
54     // fuzzing session.
55     bool is_changed;
56   };
57 
58   Fuzzer(std::unique_ptr<opt::IRContext> ir_context,
59          std::unique_ptr<TransformationContext> transformation_context,
60          std::unique_ptr<FuzzerContext> fuzzer_context,
61          MessageConsumer consumer,
62          const std::vector<fuzzerutil::ModuleSupplier>& donor_suppliers,
63          bool enable_all_passes, RepeatedPassStrategy repeated_pass_strategy,
64          bool validate_after_each_fuzzer_pass,
65          spv_validator_options validator_options,
66          bool ignore_inapplicable_transformations = true);
67 
68   // Disables copy/move constructor/assignment operations.
69   Fuzzer(const Fuzzer&) = delete;
70   Fuzzer(Fuzzer&&) = delete;
71   Fuzzer& operator=(const Fuzzer&) = delete;
72   Fuzzer& operator=(Fuzzer&&) = delete;
73 
74   ~Fuzzer();
75 
76   // Transforms |ir_context_| by running a number of randomized fuzzer passes.
77   // Initial facts about the input binary and the context in which it will be
78   // executed are provided with |transformation_context_|.
79   // |num_of_transformations| is equal to the maximum number of transformations
80   // applied in a single call to this method. This parameter is ignored if its
81   // value is equal to 0. Because fuzzing cannot stop mid way through a fuzzer
82   // pass, fuzzing will stop after the fuzzer pass that exceeds
83   // |num_of_transformations| has completed, so that the total number of
84   // transformations may be somewhat larger than this number.
85   Result Run(uint32_t num_of_transformations_to_apply);
86 
87   // Returns the current IR context. It may be invalid if the Run method
88   // returned Status::kFuzzerPassLedToInvalidModule previously.
89   opt::IRContext* GetIRContext();
90 
91   // Returns the sequence of applied transformations.
92   const protobufs::TransformationSequence& GetTransformationSequence() const;
93 
94  private:
95   // A convenience method to add a repeated fuzzer pass to |pass_instances| with
96   // probability |percentage_chance_of_adding_pass|%, or with probability 100%
97   // if |enable_all_passes_| is true.
98   //
99   // All fuzzer passes take members |ir_context_|, |transformation_context_|,
100   // |fuzzer_context_| and |transformation_sequence_out_| as parameters.  Extra
101   // arguments can be provided via |extra_args|.
102   template <typename FuzzerPassT, typename... Args>
103   void MaybeAddRepeatedPass(uint32_t percentage_chance_of_adding_pass,
104                             RepeatedPassInstances* pass_instances,
105                             Args&&... extra_args);
106 
107   // The same as the above, with |percentage_chance_of_adding_pass| == 50%.
108   template <typename FuzzerPassT, typename... Args>
MaybeAddRepeatedPass(RepeatedPassInstances * pass_instances,Args &&...extra_args)109   void MaybeAddRepeatedPass(RepeatedPassInstances* pass_instances,
110                             Args&&... extra_args) {
111     MaybeAddRepeatedPass<FuzzerPassT>(50, pass_instances,
112                                       std::forward<Args>(extra_args)...);
113   }
114 
115   // A convenience method to add a final fuzzer pass to |passes| with
116   // probability 50%, or with probability 100% if |enable_all_passes_| is true.
117   //
118   // All fuzzer passes take members |ir_context_|, |transformation_context_|,
119   // |fuzzer_context_| and |transformation_sequence_out_| as parameters.  Extra
120   // arguments can be provided via |extra_args|.
121   template <typename FuzzerPassT, typename... Args>
122   void MaybeAddFinalPass(std::vector<std::unique_ptr<FuzzerPass>>* passes,
123                          Args&&... extra_args);
124 
125   // Decides whether to apply more repeated passes. The probability decreases as
126   // the number of transformations that have been applied increases.
127   // The described probability is only applied if
128   // |continue_fuzzing_probabilistically| is true.
129   bool ShouldContinueRepeatedPasses(bool continue_fuzzing_probabilistically);
130 
131   // Applies |pass|, which must be a pass constructed with |ir_context|.
132   // If |validate_after_each_fuzzer_pass_| is not set, true is always returned.
133   // Otherwise, true is returned if and only if |ir_context| passes validation,
134   // every block has its enclosing function as its parent, and every
135   // instruction has a distinct unique id.
136   bool ApplyPassAndCheckValidity(FuzzerPass* pass) const;
137 
138   // Message consumer that will be invoked once for each message communicated
139   // from the library.
140   const MessageConsumer consumer_;
141 
142   // Determines whether all passes should be enabled, vs. having passes be
143   // probabilistically enabled.
144   const bool enable_all_passes_;
145 
146   // Determines whether the validator should be invoked after every fuzzer pass.
147   const bool validate_after_each_fuzzer_pass_;
148 
149   // Options to control validation.
150   const spv_validator_options validator_options_;
151 
152   // The number of repeated fuzzer passes that have been applied is kept track
153   // of, in order to enforce a hard limit on the number of times such passes
154   // can be applied.
155   uint32_t num_repeated_passes_applied_;
156 
157   // We use this to determine whether we can continue fuzzing incrementally
158   // since the previous call to the Run method could've returned
159   // kFuzzerPassLedToInvalidModule.
160   bool is_valid_;
161 
162   // Intermediate representation for the module being fuzzed, which gets
163   // mutated as fuzzing proceeds.
164   std::unique_ptr<opt::IRContext> ir_context_;
165 
166   // Contextual information that is required in order to apply
167   // transformations.
168   std::unique_ptr<TransformationContext> transformation_context_;
169 
170   // Provides probabilities that control the fuzzing process.
171   std::unique_ptr<FuzzerContext> fuzzer_context_;
172 
173   // The sequence of transformations that have been applied during fuzzing. It
174   // is initially empty and grows as fuzzer passes are applied.
175   protobufs::TransformationSequence transformation_sequence_out_;
176 
177   // This object contains instances of all fuzzer passes that will participate
178   // in the fuzzing.
179   RepeatedPassInstances pass_instances_;
180 
181   // This object defines the recommendation logic for fuzzer passes.
182   std::unique_ptr<RepeatedPassRecommender> repeated_pass_recommender_;
183 
184   // This object manager a list of fuzzer pass and their available
185   // recommendations.
186   std::unique_ptr<RepeatedPassManager> repeated_pass_manager_;
187 
188   // Some passes that it does not make sense to apply repeatedly, as they do not
189   // unlock other passes.
190   std::vector<std::unique_ptr<FuzzerPass>> final_passes_;
191 
192   // When set, this flag causes inapplicable transformations that should be
193   // applicable by construction to be ignored. This is useful when the fuzzer
194   // is being deployed at scale to test a SPIR-V processing tool, and where it
195   // is desirable to ignore bugs in the fuzzer itself.
196   const bool ignore_inapplicable_transformations_;
197 };
198 
199 }  // namespace fuzz
200 }  // namespace spvtools
201 
202 #endif  // SOURCE_FUZZ_FUZZER_H_
203