added_function_reducer.h 8.6 KB

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