1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193
|
// 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_
|