2019-05-27 13:34:55 +00:00
|
|
|
// Copyright (c) 2019 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_FUZZER_H_
|
|
|
|
#define SOURCE_FUZZ_FUZZER_H_
|
|
|
|
|
|
|
|
#include <memory>
|
2020-10-01 08:07:06 +00:00
|
|
|
#include <utility>
|
2019-05-27 13:34:55 +00:00
|
|
|
#include <vector>
|
|
|
|
|
2020-09-18 14:51:35 +00:00
|
|
|
#include "source/fuzz/fuzzer_context.h"
|
2020-08-28 12:30:06 +00:00
|
|
|
#include "source/fuzz/fuzzer_pass.h"
|
2020-01-07 08:39:55 +00:00
|
|
|
#include "source/fuzz/fuzzer_util.h"
|
2020-09-18 14:51:35 +00:00
|
|
|
#include "source/fuzz/pass_management/repeated_pass_instances.h"
|
2021-03-03 15:34:53 +00:00
|
|
|
#include "source/fuzz/pass_management/repeated_pass_manager.h"
|
2020-09-18 14:51:35 +00:00
|
|
|
#include "source/fuzz/pass_management/repeated_pass_recommender.h"
|
2019-05-27 13:34:55 +00:00
|
|
|
#include "source/fuzz/protobufs/spirvfuzz_protobufs.h"
|
2020-09-22 09:07:58 +00:00
|
|
|
#include "source/fuzz/random_generator.h"
|
|
|
|
#include "source/opt/ir_context.h"
|
2019-05-27 13:34:55 +00:00
|
|
|
#include "spirv-tools/libspirv.hpp"
|
|
|
|
|
|
|
|
namespace spvtools {
|
|
|
|
namespace fuzz {
|
|
|
|
|
|
|
|
// Transforms a SPIR-V module into a semantically equivalent SPIR-V module by
|
|
|
|
// running a number of randomized fuzzer passes.
|
|
|
|
class Fuzzer {
|
|
|
|
public:
|
|
|
|
// Possible statuses that can result from running the fuzzer.
|
2021-03-03 15:34:53 +00:00
|
|
|
enum class Status {
|
2019-05-27 13:34:55 +00:00
|
|
|
kComplete,
|
2021-03-03 15:34:53 +00:00
|
|
|
kModuleTooBig,
|
|
|
|
kTransformationLimitReached,
|
|
|
|
kFuzzerStuck,
|
2019-11-27 18:05:56 +00:00
|
|
|
kFuzzerPassLedToInvalidModule,
|
2019-05-27 13:34:55 +00:00
|
|
|
};
|
|
|
|
|
2021-03-03 15:34:53 +00:00
|
|
|
struct Result {
|
|
|
|
// Status of the fuzzing session.
|
|
|
|
Status status;
|
2020-09-22 09:07:58 +00:00
|
|
|
|
2021-03-03 15:34:53 +00:00
|
|
|
// Equals to true if new transformations were applied during the previous
|
|
|
|
// fuzzing session.
|
|
|
|
bool is_changed;
|
2020-09-18 14:51:35 +00:00
|
|
|
};
|
|
|
|
|
2021-03-03 15:34:53 +00:00
|
|
|
Fuzzer(std::unique_ptr<opt::IRContext> ir_context,
|
|
|
|
std::unique_ptr<TransformationContext> transformation_context,
|
|
|
|
std::unique_ptr<FuzzerContext> fuzzer_context,
|
|
|
|
MessageConsumer consumer,
|
2020-09-22 09:07:58 +00:00
|
|
|
const std::vector<fuzzerutil::ModuleSupplier>& donor_suppliers,
|
|
|
|
bool enable_all_passes, RepeatedPassStrategy repeated_pass_strategy,
|
2020-04-02 13:37:59 +00:00
|
|
|
bool validate_after_each_fuzzer_pass,
|
2021-07-28 21:59:37 +00:00
|
|
|
spv_validator_options validator_options,
|
2021-08-02 12:37:37 +00:00
|
|
|
bool ignore_inapplicable_transformations = true);
|
2019-05-27 13:34:55 +00:00
|
|
|
|
|
|
|
// Disables copy/move constructor/assignment operations.
|
|
|
|
Fuzzer(const Fuzzer&) = delete;
|
|
|
|
Fuzzer(Fuzzer&&) = delete;
|
|
|
|
Fuzzer& operator=(const Fuzzer&) = delete;
|
|
|
|
Fuzzer& operator=(Fuzzer&&) = delete;
|
|
|
|
|
|
|
|
~Fuzzer();
|
|
|
|
|
2021-03-03 15:34:53 +00:00
|
|
|
// Transforms |ir_context_| by running a number of randomized fuzzer passes.
|
|
|
|
// Initial facts about the input binary and the context in which it will be
|
|
|
|
// executed are provided with |transformation_context_|.
|
|
|
|
// |num_of_transformations| is equal to the maximum number of transformations
|
|
|
|
// applied in a single call to this method. This parameter is ignored if its
|
|
|
|
// value is equal to 0. Because fuzzing cannot stop mid way through a fuzzer
|
|
|
|
// pass, fuzzing will stop after the fuzzer pass that exceeds
|
|
|
|
// |num_of_transformations| has completed, so that the total number of
|
|
|
|
// transformations may be somewhat larger than this number.
|
|
|
|
Result Run(uint32_t num_of_transformations_to_apply);
|
|
|
|
|
|
|
|
// Returns the current IR context. It may be invalid if the Run method
|
|
|
|
// returned Status::kFuzzerPassLedToInvalidModule previously.
|
|
|
|
opt::IRContext* GetIRContext();
|
|
|
|
|
|
|
|
// Returns the sequence of applied transformations.
|
|
|
|
const protobufs::TransformationSequence& GetTransformationSequence() const;
|
2019-05-27 13:34:55 +00:00
|
|
|
|
|
|
|
private:
|
2020-09-18 14:51:35 +00:00
|
|
|
// A convenience method to add a repeated fuzzer pass to |pass_instances| with
|
2020-10-01 08:07:06 +00:00
|
|
|
// probability |percentage_chance_of_adding_pass|%, or with probability 100%
|
|
|
|
// if |enable_all_passes_| is true.
|
2020-09-18 14:51:35 +00:00
|
|
|
//
|
2020-09-22 09:07:58 +00:00
|
|
|
// All fuzzer passes take members |ir_context_|, |transformation_context_|,
|
|
|
|
// |fuzzer_context_| and |transformation_sequence_out_| as parameters. Extra
|
2020-09-18 14:51:35 +00:00
|
|
|
// arguments can be provided via |extra_args|.
|
|
|
|
template <typename FuzzerPassT, typename... Args>
|
2020-10-01 08:07:06 +00:00
|
|
|
void MaybeAddRepeatedPass(uint32_t percentage_chance_of_adding_pass,
|
|
|
|
RepeatedPassInstances* pass_instances,
|
2020-09-22 09:07:58 +00:00
|
|
|
Args&&... extra_args);
|
2020-09-18 14:51:35 +00:00
|
|
|
|
2020-10-01 08:07:06 +00:00
|
|
|
// The same as the above, with |percentage_chance_of_adding_pass| == 50%.
|
|
|
|
template <typename FuzzerPassT, typename... Args>
|
|
|
|
void MaybeAddRepeatedPass(RepeatedPassInstances* pass_instances,
|
|
|
|
Args&&... extra_args) {
|
|
|
|
MaybeAddRepeatedPass<FuzzerPassT>(50, pass_instances,
|
|
|
|
std::forward<Args>(extra_args)...);
|
|
|
|
}
|
|
|
|
|
2020-09-18 14:51:35 +00:00
|
|
|
// A convenience method to add a final fuzzer pass to |passes| with
|
2020-10-01 08:07:06 +00:00
|
|
|
// probability 50%, or with probability 100% if |enable_all_passes_| is true.
|
2020-09-18 14:51:35 +00:00
|
|
|
//
|
2020-09-22 09:07:58 +00:00
|
|
|
// All fuzzer passes take members |ir_context_|, |transformation_context_|,
|
|
|
|
// |fuzzer_context_| and |transformation_sequence_out_| as parameters. Extra
|
2020-09-18 14:51:35 +00:00
|
|
|
// arguments can be provided via |extra_args|.
|
|
|
|
template <typename FuzzerPassT, typename... Args>
|
2020-09-22 09:07:58 +00:00
|
|
|
void MaybeAddFinalPass(std::vector<std::unique_ptr<FuzzerPass>>* passes,
|
|
|
|
Args&&... extra_args);
|
2020-09-18 14:51:35 +00:00
|
|
|
|
|
|
|
// Decides whether to apply more repeated passes. The probability decreases as
|
|
|
|
// the number of transformations that have been applied increases.
|
2021-03-03 15:34:53 +00:00
|
|
|
// The described probability is only applied if
|
|
|
|
// |continue_fuzzing_probabilistically| is true.
|
|
|
|
bool ShouldContinueRepeatedPasses(bool continue_fuzzing_probabilistically);
|
2020-09-18 14:51:35 +00:00
|
|
|
|
2020-10-13 18:48:18 +00:00
|
|
|
// Applies |pass|, which must be a pass constructed with |ir_context|.
|
|
|
|
// If |validate_after_each_fuzzer_pass_| is not set, true is always returned.
|
|
|
|
// Otherwise, true is returned if and only if |ir_context| passes validation,
|
2020-10-16 21:58:09 +00:00
|
|
|
// every block has its enclosing function as its parent, and every
|
|
|
|
// instruction has a distinct unique id.
|
|
|
|
bool ApplyPassAndCheckValidity(FuzzerPass* pass) const;
|
2020-08-28 12:30:06 +00:00
|
|
|
|
2020-09-22 09:07:58 +00:00
|
|
|
// Message consumer that will be invoked once for each message communicated
|
|
|
|
// from the library.
|
2021-03-03 15:34:53 +00:00
|
|
|
const MessageConsumer consumer_;
|
2020-08-28 12:30:06 +00:00
|
|
|
|
2020-09-18 14:51:35 +00:00
|
|
|
// Determines whether all passes should be enabled, vs. having passes be
|
|
|
|
// probabilistically enabled.
|
2021-03-03 15:34:53 +00:00
|
|
|
const bool enable_all_passes_;
|
2020-09-18 14:51:35 +00:00
|
|
|
|
2020-08-28 12:30:06 +00:00
|
|
|
// Determines whether the validator should be invoked after every fuzzer pass.
|
2021-03-03 15:34:53 +00:00
|
|
|
const bool validate_after_each_fuzzer_pass_;
|
2020-08-28 12:30:06 +00:00
|
|
|
|
|
|
|
// Options to control validation.
|
2021-03-03 15:34:53 +00:00
|
|
|
const spv_validator_options validator_options_;
|
2020-09-18 14:51:35 +00:00
|
|
|
|
|
|
|
// The number of repeated fuzzer passes that have been applied is kept track
|
|
|
|
// of, in order to enforce a hard limit on the number of times such passes
|
|
|
|
// can be applied.
|
|
|
|
uint32_t num_repeated_passes_applied_;
|
2020-09-22 09:07:58 +00:00
|
|
|
|
2021-03-03 15:34:53 +00:00
|
|
|
// We use this to determine whether we can continue fuzzing incrementally
|
|
|
|
// since the previous call to the Run method could've returned
|
|
|
|
// kFuzzerPassLedToInvalidModule.
|
|
|
|
bool is_valid_;
|
|
|
|
|
2020-09-22 09:07:58 +00:00
|
|
|
// Intermediate representation for the module being fuzzed, which gets
|
|
|
|
// mutated as fuzzing proceeds.
|
|
|
|
std::unique_ptr<opt::IRContext> ir_context_;
|
|
|
|
|
2021-03-03 15:34:53 +00:00
|
|
|
// Contextual information that is required in order to apply
|
|
|
|
// transformations.
|
|
|
|
std::unique_ptr<TransformationContext> transformation_context_;
|
|
|
|
|
2020-09-22 09:07:58 +00:00
|
|
|
// Provides probabilities that control the fuzzing process.
|
|
|
|
std::unique_ptr<FuzzerContext> fuzzer_context_;
|
|
|
|
|
2021-03-03 15:34:53 +00:00
|
|
|
// The sequence of transformations that have been applied during fuzzing. It
|
2020-09-22 09:07:58 +00:00
|
|
|
// is initially empty and grows as fuzzer passes are applied.
|
|
|
|
protobufs::TransformationSequence transformation_sequence_out_;
|
2021-03-03 15:34:53 +00:00
|
|
|
|
|
|
|
// This object contains instances of all fuzzer passes that will participate
|
|
|
|
// in the fuzzing.
|
|
|
|
RepeatedPassInstances pass_instances_;
|
|
|
|
|
|
|
|
// This object defines the recommendation logic for fuzzer passes.
|
|
|
|
std::unique_ptr<RepeatedPassRecommender> repeated_pass_recommender_;
|
|
|
|
|
|
|
|
// This object manager a list of fuzzer pass and their available
|
|
|
|
// recommendations.
|
|
|
|
std::unique_ptr<RepeatedPassManager> repeated_pass_manager_;
|
|
|
|
|
|
|
|
// Some passes that it does not make sense to apply repeatedly, as they do not
|
|
|
|
// unlock other passes.
|
|
|
|
std::vector<std::unique_ptr<FuzzerPass>> final_passes_;
|
2021-07-28 21:59:37 +00:00
|
|
|
|
|
|
|
// When set, this flag causes inapplicable transformations that should be
|
|
|
|
// applicable by construction to be ignored. This is useful when the fuzzer
|
|
|
|
// is being deployed at scale to test a SPIR-V processing tool, and where it
|
|
|
|
// is desirable to ignore bugs in the fuzzer itself.
|
|
|
|
const bool ignore_inapplicable_transformations_;
|
2019-05-27 13:34:55 +00:00
|
|
|
};
|
|
|
|
|
|
|
|
} // namespace fuzz
|
|
|
|
} // namespace spvtools
|
|
|
|
|
|
|
|
#endif // SOURCE_FUZZ_FUZZER_H_
|