1 // Copyright (c) 2020 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_ADDED_FUNCTION_REDUCER_H_ 16 #define SOURCE_FUZZ_ADDED_FUNCTION_REDUCER_H_ 17 18 #include <unordered_set> 19 #include <vector> 20 21 #include "source/fuzz/protobufs/spirvfuzz_protobufs.h" 22 #include "source/fuzz/shrinker.h" 23 #include "spirv-tools/libspirv.hpp" 24 25 namespace spvtools { 26 namespace fuzz { 27 28 // An auxiliary class used by Shrinker, this class takes care of using 29 // spirv-reduce to reduce the body of a function encoded in an AddFunction 30 // transformation, in case a smaller, simpler function can be added instead. 31 class AddedFunctionReducer { 32 public: 33 // Possible statuses that can result from running the shrinker. 34 enum class AddedFunctionReducerResultStatus { 35 kComplete, 36 kReductionFailed, 37 }; 38 39 struct AddedFunctionReducerResult { 40 AddedFunctionReducerResultStatus status; 41 std::vector<uint32_t> transformed_binary; 42 protobufs::TransformationSequence applied_transformations; 43 uint32_t num_reduction_attempts; 44 }; 45 46 AddedFunctionReducer( 47 spv_target_env target_env, MessageConsumer consumer, 48 const std::vector<uint32_t>& binary_in, 49 const protobufs::FactSequence& initial_facts, 50 const protobufs::TransformationSequence& transformation_sequence_in, 51 uint32_t index_of_add_function_transformation, 52 const Shrinker::InterestingnessFunction& 53 shrinker_interestingness_function, 54 bool validate_during_replay, spv_validator_options validator_options, 55 uint32_t shrinker_step_limit, uint32_t num_existing_shrink_attempts); 56 57 // Disables copy/move constructor/assignment operations. 58 AddedFunctionReducer(const AddedFunctionReducer&) = delete; 59 AddedFunctionReducer(AddedFunctionReducer&&) = delete; 60 AddedFunctionReducer& operator=(const AddedFunctionReducer&) = delete; 61 AddedFunctionReducer& operator=(AddedFunctionReducer&&) = delete; 62 63 ~AddedFunctionReducer(); 64 65 // Invokes spirv-reduce on the function in the AddFunction transformation 66 // identified by |index_of_add_function_transformation|. Returns a sequence 67 // of transformations identical to |transformation_sequence_in|, except that 68 // the AddFunction transformation at |index_of_add_function_transformation| 69 // might have been simplified. The binary associated with applying the 70 // resulting sequence of transformations to |binary_in| is also returned, as 71 // well as the number of reduction steps that spirv-reduce made. 72 // 73 // On failure, an empty transformation sequence and binary are returned, 74 // with a placeholder value of 0 for the number of reduction attempts. 75 AddedFunctionReducerResult Run(); 76 77 private: 78 // Yields, via |binary_out|, the binary obtained by applying transformations 79 // [0, |index_of_added_function_| - 1] from |transformations_in_| to 80 // |binary_in_|, and then adding the raw function encoded in 81 // |transformations_in_[index_of_added_function_]| (without adapting that 82 // function to make it livesafe). This function has |added_function_id_| as 83 // its result id. 84 // 85 // The ids associated with all global variables in |binary_out| that had the 86 // "irrelevant pointee value" fact are also returned via 87 // |irrelevant_pointee_global_variables|. 88 // 89 // The point of this function is that spirv-reduce can subsequently be applied 90 // to function |added_function_id_| in |binary_out|. By construction, 91 // |added_function_id_| should originally manipulate globals for which 92 // "irrelevant pointee value" facts hold. The set 93 // |irrelevant_pointee_global_variables| can be used to force spirv-reduce 94 // to preserve this, to avoid the reduced function ending up manipulating 95 // other global variables of the SPIR-V module, potentially changing their 96 // value and thus changing the semantics of the module. 97 void ReplayPrefixAndAddFunction( 98 std::vector<uint32_t>* binary_out, 99 std::unordered_set<uint32_t>* irrelevant_pointee_global_variables) const; 100 101 // This is the interestingness function that will be used by spirv-reduce 102 // when shrinking the added function. 103 // 104 // For |binary_under_reduction| to be deemed interesting, the following 105 // conditions must hold: 106 // - The function with id |added_function_id_| in |binary_under_reduction| 107 // must only reference global variables in 108 // |irrelevant_pointee_global_variables|. This avoids the reduced function 109 // changing the semantics of the original SPIR-V module. 110 // - It must be possible to successfully replay the transformations in 111 // |transformation_sequence_in_|, adapted so that the function added by the 112 // transformation at |index_of_add_function_transformation_| is replaced by 113 // the function with id |added_function_id_| in |binary_under_reduction|, 114 // to |binary_in| (starting with initial facts |initial_facts_|). 115 // - All the transformations in this sequence must be successfully applied 116 // during replay. 117 // - The resulting binary must be interesting according to 118 // |shrinker_interestingness_function_|. 119 bool InterestingnessFunctionForReducingAddedFunction( 120 const std::vector<uint32_t>& binary_under_reduction, 121 const std::unordered_set<uint32_t>& irrelevant_pointee_global_variables); 122 123 // Starting with |binary_in_| and |initial_facts_|, the transformations in 124 // |transformation_sequence_in_| are replayed. However, the transformation 125 // at index |index_of_add_function_transformation_| of 126 // |transformation_sequence_in_| -- which is guaranteed to be an AddFunction 127 // transformation -- is adapted so that the function to be added is replaced 128 // with the function in |binary_under_reduction| with id |added_function_id_|. 129 // 130 // The binary resulting from this replay is returned via |binary_out|, and the 131 // adapted transformation sequence via |transformation_sequence_out|. 132 void ReplayAdaptedTransformations( 133 const std::vector<uint32_t>& binary_under_reduction, 134 std::vector<uint32_t>* binary_out, 135 protobufs::TransformationSequence* transformation_sequence_out) const; 136 137 // Returns the id of the function to be added by the AddFunction 138 // transformation at 139 // |transformation_sequence_in_[index_of_add_function_transformation_]|. 140 uint32_t GetAddedFunctionId() const; 141 142 // Target environment. 143 const spv_target_env target_env_; 144 145 // Message consumer. 146 MessageConsumer consumer_; 147 148 // The initial binary to which transformations are applied -- i.e., the 149 // binary to which spirv-fuzz originally applied transformations. 150 const std::vector<uint32_t>& binary_in_; 151 152 // Initial facts about |binary_in_|. 153 const protobufs::FactSequence& initial_facts_; 154 155 // A set of transformations that can be successfully applied to |binary_in_|. 156 const protobufs::TransformationSequence& transformation_sequence_in_; 157 158 // An index into |transformation_sequence_in_| referring to an AddFunction 159 // transformation. This is the transformation to be simplified using 160 // spirv-reduce. 161 const uint32_t index_of_add_function_transformation_; 162 163 // The interestingness function that has been provided to guide the 164 // overall shrinking process. The AddFunction transformation being simplified 165 // by this class should still -- when applied in conjunction with the other 166 // transformations in |transformation_sequence_in_| -- lead to a binary that 167 // is deemed interesting by this function. 168 const Shrinker::InterestingnessFunction& shrinker_interestingness_function_; 169 170 // Determines whether to check for validity during the replaying of 171 // transformations. 172 const bool validate_during_replay_; 173 174 // Options to control validation. 175 spv_validator_options validator_options_; 176 177 // The step limit associated with the overall shrinking process. 178 const uint32_t shrinker_step_limit_; 179 180 // The number of shrink attempts that had been applied prior to invoking this 181 // AddedFunctionReducer instance. 182 const uint32_t num_existing_shrink_attempts_; 183 184 // Tracks the number of attempts that spirv-reduce has invoked its 185 // interestingness function, which it does once at the start of reduction, 186 // and then once more each time it makes a reduction step. 187 uint32_t num_reducer_interestingness_function_invocations_; 188 }; 189 190 } // namespace fuzz 191 } // namespace spvtools 192 193 #endif // SOURCE_FUZZ_ADDED_FUNCTION_REDUCER_H_ 194