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 67 // Disables copy/move constructor/assignment operations. 68 Fuzzer(const Fuzzer&) = delete; 69 Fuzzer(Fuzzer&&) = delete; 70 Fuzzer& operator=(const Fuzzer&) = delete; 71 Fuzzer& operator=(Fuzzer&&) = delete; 72 73 ~Fuzzer(); 74 75 // Transforms |ir_context_| by running a number of randomized fuzzer passes. 76 // Initial facts about the input binary and the context in which it will be 77 // executed are provided with |transformation_context_|. 78 // |num_of_transformations| is equal to the maximum number of transformations 79 // applied in a single call to this method. This parameter is ignored if its 80 // value is equal to 0. Because fuzzing cannot stop mid way through a fuzzer 81 // pass, fuzzing will stop after the fuzzer pass that exceeds 82 // |num_of_transformations| has completed, so that the total number of 83 // transformations may be somewhat larger than this number. 84 Result Run(uint32_t num_of_transformations_to_apply); 85 86 // Returns the current IR context. It may be invalid if the Run method 87 // returned Status::kFuzzerPassLedToInvalidModule previously. 88 opt::IRContext* GetIRContext(); 89 90 // Returns the sequence of applied transformations. 91 const protobufs::TransformationSequence& GetTransformationSequence() const; 92 93 private: 94 // A convenience method to add a repeated fuzzer pass to |pass_instances| with 95 // probability |percentage_chance_of_adding_pass|%, or with probability 100% 96 // if |enable_all_passes_| is true. 97 // 98 // All fuzzer passes take members |ir_context_|, |transformation_context_|, 99 // |fuzzer_context_| and |transformation_sequence_out_| as parameters. Extra 100 // arguments can be provided via |extra_args|. 101 template <typename FuzzerPassT, typename... Args> 102 void MaybeAddRepeatedPass(uint32_t percentage_chance_of_adding_pass, 103 RepeatedPassInstances* pass_instances, 104 Args&&... extra_args); 105 106 // The same as the above, with |percentage_chance_of_adding_pass| == 50%. 107 template <typename FuzzerPassT, typename... Args> MaybeAddRepeatedPass(RepeatedPassInstances * pass_instances,Args &&...extra_args)108 void MaybeAddRepeatedPass(RepeatedPassInstances* pass_instances, 109 Args&&... extra_args) { 110 MaybeAddRepeatedPass<FuzzerPassT>(50, pass_instances, 111 std::forward<Args>(extra_args)...); 112 } 113 114 // A convenience method to add a final fuzzer pass to |passes| with 115 // probability 50%, or with probability 100% if |enable_all_passes_| is true. 116 // 117 // All fuzzer passes take members |ir_context_|, |transformation_context_|, 118 // |fuzzer_context_| and |transformation_sequence_out_| as parameters. Extra 119 // arguments can be provided via |extra_args|. 120 template <typename FuzzerPassT, typename... Args> 121 void MaybeAddFinalPass(std::vector<std::unique_ptr<FuzzerPass>>* passes, 122 Args&&... extra_args); 123 124 // Decides whether to apply more repeated passes. The probability decreases as 125 // the number of transformations that have been applied increases. 126 // The described probability is only applied if 127 // |continue_fuzzing_probabilistically| is true. 128 bool ShouldContinueRepeatedPasses(bool continue_fuzzing_probabilistically); 129 130 // Applies |pass|, which must be a pass constructed with |ir_context|. 131 // If |validate_after_each_fuzzer_pass_| is not set, true is always returned. 132 // Otherwise, true is returned if and only if |ir_context| passes validation, 133 // every block has its enclosing function as its parent, and every 134 // instruction has a distinct unique id. 135 bool ApplyPassAndCheckValidity(FuzzerPass* pass) const; 136 137 // Message consumer that will be invoked once for each message communicated 138 // from the library. 139 const MessageConsumer consumer_; 140 141 // Determines whether all passes should be enabled, vs. having passes be 142 // probabilistically enabled. 143 const bool enable_all_passes_; 144 145 // Determines whether the validator should be invoked after every fuzzer pass. 146 const bool validate_after_each_fuzzer_pass_; 147 148 // Options to control validation. 149 const spv_validator_options validator_options_; 150 151 // The number of repeated fuzzer passes that have been applied is kept track 152 // of, in order to enforce a hard limit on the number of times such passes 153 // can be applied. 154 uint32_t num_repeated_passes_applied_; 155 156 // We use this to determine whether we can continue fuzzing incrementally 157 // since the previous call to the Run method could've returned 158 // kFuzzerPassLedToInvalidModule. 159 bool is_valid_; 160 161 // Intermediate representation for the module being fuzzed, which gets 162 // mutated as fuzzing proceeds. 163 std::unique_ptr<opt::IRContext> ir_context_; 164 165 // Contextual information that is required in order to apply 166 // transformations. 167 std::unique_ptr<TransformationContext> transformation_context_; 168 169 // Provides probabilities that control the fuzzing process. 170 std::unique_ptr<FuzzerContext> fuzzer_context_; 171 172 // The sequence of transformations that have been applied during fuzzing. It 173 // is initially empty and grows as fuzzer passes are applied. 174 protobufs::TransformationSequence transformation_sequence_out_; 175 176 // This object contains instances of all fuzzer passes that will participate 177 // in the fuzzing. 178 RepeatedPassInstances pass_instances_; 179 180 // This object defines the recommendation logic for fuzzer passes. 181 std::unique_ptr<RepeatedPassRecommender> repeated_pass_recommender_; 182 183 // This object manager a list of fuzzer pass and their available 184 // recommendations. 185 std::unique_ptr<RepeatedPassManager> repeated_pass_manager_; 186 187 // Some passes that it does not make sense to apply repeatedly, as they do not 188 // unlock other passes. 189 std::vector<std::unique_ptr<FuzzerPass>> final_passes_; 190 }; 191 192 } // namespace fuzz 193 } // namespace spvtools 194 195 #endif // SOURCE_FUZZ_FUZZER_H_ 196