| // Copyright (c) 2020 Google LLC | |
| // | |
| // Licensed under the Apache License, Version 2.0 (the "License"); | |
| // you may not use this file except in compliance with the License. | |
| // You may obtain a copy of the License at | |
| // | |
| // http://www.apache.org/licenses/LICENSE-2.0 | |
| // | |
| // Unless required by applicable law or agreed to in writing, software | |
| // distributed under the License is distributed on an "AS IS" BASIS, | |
| // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | |
| // See the License for the specific language governing permissions and | |
| // limitations under the License. | |
| #ifndef SOURCE_FUZZ_ADDED_FUNCTION_REDUCER_H_ | |
| #define SOURCE_FUZZ_ADDED_FUNCTION_REDUCER_H_ | |
| #include <unordered_set> | |
| #include <vector> | |
| #include "source/fuzz/protobufs/spirvfuzz_protobufs.h" | |
| #include "source/fuzz/shrinker.h" | |
| #include "spirv-tools/libspirv.hpp" | |
| namespace spvtools { | |
| namespace fuzz { | |
| // An auxiliary class used by Shrinker, this class takes care of using | |
| // spirv-reduce to reduce the body of a function encoded in an AddFunction | |
| // transformation, in case a smaller, simpler function can be added instead. | |
| class AddedFunctionReducer { | |
| public: | |
| // Possible statuses that can result from running the shrinker. | |
| enum class AddedFunctionReducerResultStatus { | |
| kComplete, | |
| kReductionFailed, | |
| }; | |
| struct AddedFunctionReducerResult { | |
| AddedFunctionReducerResultStatus status; | |
| std::vector<uint32_t> transformed_binary; | |
| protobufs::TransformationSequence applied_transformations; | |
| uint32_t num_reduction_attempts; | |
| }; | |
| AddedFunctionReducer( | |
| spv_target_env target_env, MessageConsumer consumer, | |
| const std::vector<uint32_t>& binary_in, | |
| const protobufs::FactSequence& initial_facts, | |
| const protobufs::TransformationSequence& transformation_sequence_in, | |
| uint32_t index_of_add_function_transformation, | |
| const Shrinker::InterestingnessFunction& | |
| shrinker_interestingness_function, | |
| bool validate_during_replay, spv_validator_options validator_options, | |
| uint32_t shrinker_step_limit, uint32_t num_existing_shrink_attempts); | |
| // Disables copy/move constructor/assignment operations. | |
| AddedFunctionReducer(const AddedFunctionReducer&) = delete; | |
| AddedFunctionReducer(AddedFunctionReducer&&) = delete; | |
| AddedFunctionReducer& operator=(const AddedFunctionReducer&) = delete; | |
| AddedFunctionReducer& operator=(AddedFunctionReducer&&) = delete; | |
| ~AddedFunctionReducer(); | |
| // Invokes spirv-reduce on the function in the AddFunction transformation | |
| // identified by |index_of_add_function_transformation|. Returns a sequence | |
| // of transformations identical to |transformation_sequence_in|, except that | |
| // the AddFunction transformation at |index_of_add_function_transformation| | |
| // might have been simplified. The binary associated with applying the | |
| // resulting sequence of transformations to |binary_in| is also returned, as | |
| // well as the number of reduction steps that spirv-reduce made. | |
| // | |
| // On failure, an empty transformation sequence and binary are returned, | |
| // with a placeholder value of 0 for the number of reduction attempts. | |
| AddedFunctionReducerResult Run(); | |
| private: | |
| // Yields, via |binary_out|, the binary obtained by applying transformations | |
| // [0, |index_of_added_function_| - 1] from |transformations_in_| to | |
| // |binary_in_|, and then adding the raw function encoded in | |
| // |transformations_in_[index_of_added_function_]| (without adapting that | |
| // function to make it livesafe). This function has |added_function_id_| as | |
| // its result id. | |
| // | |
| // The ids associated with all global variables in |binary_out| that had the | |
| // "irrelevant pointee value" fact are also returned via | |
| // |irrelevant_pointee_global_variables|. | |
| // | |
| // The point of this function is that spirv-reduce can subsequently be applied | |
| // to function |added_function_id_| in |binary_out|. By construction, | |
| // |added_function_id_| should originally manipulate globals for which | |
| // "irrelevant pointee value" facts hold. The set | |
| // |irrelevant_pointee_global_variables| can be used to force spirv-reduce | |
| // to preserve this, to avoid the reduced function ending up manipulating | |
| // other global variables of the SPIR-V module, potentially changing their | |
| // value and thus changing the semantics of the module. | |
| void ReplayPrefixAndAddFunction( | |
| std::vector<uint32_t>* binary_out, | |
| std::unordered_set<uint32_t>* irrelevant_pointee_global_variables) const; | |
| // This is the interestingness function that will be used by spirv-reduce | |
| // when shrinking the added function. | |
| // | |
| // For |binary_under_reduction| to be deemed interesting, the following | |
| // conditions must hold: | |
| // - The function with id |added_function_id_| in |binary_under_reduction| | |
| // must only reference global variables in | |
| // |irrelevant_pointee_global_variables|. This avoids the reduced function | |
| // changing the semantics of the original SPIR-V module. | |
| // - It must be possible to successfully replay the transformations in | |
| // |transformation_sequence_in_|, adapted so that the function added by the | |
| // transformation at |index_of_add_function_transformation_| is replaced by | |
| // the function with id |added_function_id_| in |binary_under_reduction|, | |
| // to |binary_in| (starting with initial facts |initial_facts_|). | |
| // - All the transformations in this sequence must be successfully applied | |
| // during replay. | |
| // - The resulting binary must be interesting according to | |
| // |shrinker_interestingness_function_|. | |
| bool InterestingnessFunctionForReducingAddedFunction( | |
| const std::vector<uint32_t>& binary_under_reduction, | |
| const std::unordered_set<uint32_t>& irrelevant_pointee_global_variables); | |
| // Starting with |binary_in_| and |initial_facts_|, the transformations in | |
| // |transformation_sequence_in_| are replayed. However, the transformation | |
| // at index |index_of_add_function_transformation_| of | |
| // |transformation_sequence_in_| -- which is guaranteed to be an AddFunction | |
| // transformation -- is adapted so that the function to be added is replaced | |
| // with the function in |binary_under_reduction| with id |added_function_id_|. | |
| // | |
| // The binary resulting from this replay is returned via |binary_out|, and the | |
| // adapted transformation sequence via |transformation_sequence_out|. | |
| void ReplayAdaptedTransformations( | |
| const std::vector<uint32_t>& binary_under_reduction, | |
| std::vector<uint32_t>* binary_out, | |
| protobufs::TransformationSequence* transformation_sequence_out) const; | |
| // Returns the id of the function to be added by the AddFunction | |
| // transformation at | |
| // |transformation_sequence_in_[index_of_add_function_transformation_]|. | |
| uint32_t GetAddedFunctionId() const; | |
| // Target environment. | |
| const spv_target_env target_env_; | |
| // Message consumer. | |
| MessageConsumer consumer_; | |
| // The initial binary to which transformations are applied -- i.e., the | |
| // binary to which spirv-fuzz originally applied transformations. | |
| const std::vector<uint32_t>& binary_in_; | |
| // Initial facts about |binary_in_|. | |
| const protobufs::FactSequence& initial_facts_; | |
| // A set of transformations that can be successfully applied to |binary_in_|. | |
| const protobufs::TransformationSequence& transformation_sequence_in_; | |
| // An index into |transformation_sequence_in_| referring to an AddFunction | |
| // transformation. This is the transformation to be simplified using | |
| // spirv-reduce. | |
| const uint32_t index_of_add_function_transformation_; | |
| // The interestingness function that has been provided to guide the | |
| // overall shrinking process. The AddFunction transformation being simplified | |
| // by this class should still -- when applied in conjunction with the other | |
| // transformations in |transformation_sequence_in_| -- lead to a binary that | |
| // is deemed interesting by this function. | |
| const Shrinker::InterestingnessFunction& shrinker_interestingness_function_; | |
| // Determines whether to check for validity during the replaying of | |
| // transformations. | |
| const bool validate_during_replay_; | |
| // Options to control validation. | |
| spv_validator_options validator_options_; | |
| // The step limit associated with the overall shrinking process. | |
| const uint32_t shrinker_step_limit_; | |
| // The number of shrink attempts that had been applied prior to invoking this | |
| // AddedFunctionReducer instance. | |
| const uint32_t num_existing_shrink_attempts_; | |
| // Tracks the number of attempts that spirv-reduce has invoked its | |
| // interestingness function, which it does once at the start of reduction, | |
| // and then once more each time it makes a reduction step. | |
| uint32_t num_reducer_interestingness_function_invocations_; | |
| }; | |
| } // namespace fuzz | |
| } // namespace spvtools | |
| #endif // SOURCE_FUZZ_ADDED_FUNCTION_REDUCER_H_ |