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