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