Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

spirv-fuzz: Make functions "livesafe" during donation #3146

Merged
merged 7 commits into from
Jan 29, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
48 changes: 46 additions & 2 deletions source/fuzz/fact_manager.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -801,7 +801,7 @@ bool FactManager::DataSynonymFacts::IsSynonymous(
//==============================

//==============================
// Dead id facts
// Dead block facts

// The purpose of this class is to group the fields and data used to represent
// facts about data blocks.
Expand Down Expand Up @@ -829,10 +829,41 @@ bool FactManager::DeadBlockFacts::BlockIsDead(uint32_t block_id) const {
// End of dead block facts
//==============================

//==============================
// Livesafe function facts

// The purpose of this class is to group the fields and data used to represent
// facts about livesafe functions.
class FactManager::LivesafeFunctionFacts {
public:
// See method in FactManager which delegates to this method.
void AddFact(const protobufs::FactFunctionIsLivesafe& fact);

// See method in FactManager which delegates to this method.
bool FunctionIsLivesafe(uint32_t function_id) const;

private:
std::set<uint32_t> livesafe_function_ids_;
};

void FactManager::LivesafeFunctionFacts::AddFact(
const protobufs::FactFunctionIsLivesafe& fact) {
livesafe_function_ids_.insert(fact.function_id());
}

bool FactManager::LivesafeFunctionFacts::FunctionIsLivesafe(
uint32_t function_id) const {
return livesafe_function_ids_.count(function_id) != 0;
}

// End of livesafe function facts
//==============================

FactManager::FactManager()
: uniform_constant_facts_(MakeUnique<ConstantUniformFacts>()),
data_synonym_facts_(MakeUnique<DataSynonymFacts>()),
dead_block_facts_(MakeUnique<DeadBlockFacts>()) {}
dead_block_facts_(MakeUnique<DeadBlockFacts>()),
livesafe_function_facts_(MakeUnique<LivesafeFunctionFacts>()) {}

FactManager::~FactManager() = default;

Expand Down Expand Up @@ -860,6 +891,9 @@ bool FactManager::AddFact(const fuzz::protobufs::Fact& fact,
case protobufs::Fact::kBlockIsDeadFact:
dead_block_facts_->AddFact(fact.block_is_dead_fact());
return true;
case protobufs::Fact::kFunctionIsLivesafeFact:
livesafe_function_facts_->AddFact(fact.function_is_livesafe_fact());
return true;
default:
assert(false && "Unknown fact type.");
return false;
Expand Down Expand Up @@ -941,5 +975,15 @@ void FactManager::AddFactBlockIsDead(uint32_t block_id) {
dead_block_facts_->AddFact(fact);
}

bool FactManager::FunctionIsLivesafe(uint32_t function_id) const {
return livesafe_function_facts_->FunctionIsLivesafe(function_id);
}

void FactManager::AddFactFunctionIsLivesafe(uint32_t function_id) {
protobufs::FactFunctionIsLivesafe fact;
fact.set_function_id(function_id);
livesafe_function_facts_->AddFact(fact);
}

} // namespace fuzz
} // namespace spvtools
18 changes: 18 additions & 0 deletions source/fuzz/fact_manager.h
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,9 @@ class FactManager {
// Records the fact that |block_id| is dead.
void AddFactBlockIsDead(uint32_t block_id);

// Records the fact that |function_id| is livesafe.
void AddFactFunctionIsLivesafe(uint32_t function_id);

// The fact manager is responsible for managing a few distinct categories of
// facts. In principle there could be different fact managers for each kind
// of fact, but in practice providing one 'go to' place for facts is
Expand Down Expand Up @@ -143,6 +146,16 @@ class FactManager {
// End of dead block facts
//==============================

//==============================
// Querying facts about livesafe function

// Returns true if and ony if |function_id| is the id of a function known
// to be livesafe.
bool FunctionIsLivesafe(uint32_t function_id) const;

// End of dead block facts
//==============================

private:
// For each distinct kind of fact to be managed, we use a separate opaque
// class type.
Expand All @@ -159,6 +172,11 @@ class FactManager {
class DeadBlockFacts; // Opaque class for management of dead block facts.
std::unique_ptr<DeadBlockFacts>
dead_block_facts_; // Unique pointer to internal data.

class LivesafeFunctionFacts; // Opaque class for management of livesafe
// function facts.
std::unique_ptr<LivesafeFunctionFacts>
livesafe_function_facts_; // Unique pointer to internal data.
};

} // namespace fuzz
Expand Down
5 changes: 5 additions & 0 deletions source/fuzz/fuzzer_context.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,7 @@ const std::pair<uint32_t, uint32_t> kChanceOfAdjustingSelectionControl = {20,
const std::pair<uint32_t, uint32_t> kChanceOfConstructingComposite = {20, 50};
const std::pair<uint32_t, uint32_t> kChanceOfCopyingObject = {20, 50};
const std::pair<uint32_t, uint32_t> kChanceOfDonatingAdditionalModule = {5, 50};
const std::pair<uint32_t, uint32_t> kChanceOfMakingDonorLivesafe = {40, 60};
const std::pair<uint32_t, uint32_t> kChanceOfMergingBlocks = {20, 95};
const std::pair<uint32_t, uint32_t> kChanceOfMovingBlockDown = {20, 50};
const std::pair<uint32_t, uint32_t> kChanceOfObfuscatingConstant = {10, 90};
Expand All @@ -49,6 +50,7 @@ const std::pair<uint32_t, uint32_t> kChanceOfSplittingBlock = {40, 95};
// Keep them in alphabetical order.
const uint32_t kDefaultMaxLoopControlPartialCount = 100;
const uint32_t kDefaultMaxLoopControlPeelCount = 100;
const uint32_t kDefaultMaxLoopLimit = 20;

// Default functions for controlling how deep to go during recursive
// generation/transformation. Keep them in alphabetical order.
Expand Down Expand Up @@ -89,6 +91,8 @@ FuzzerContext::FuzzerContext(RandomGenerator* random_generator,
chance_of_copying_object_ = ChooseBetweenMinAndMax(kChanceOfCopyingObject);
chance_of_donating_additional_module_ =
ChooseBetweenMinAndMax(kChanceOfDonatingAdditionalModule);
chance_of_making_donor_livesafe_ =
ChooseBetweenMinAndMax(kChanceOfMakingDonorLivesafe);
chance_of_merging_blocks_ = ChooseBetweenMinAndMax(kChanceOfMergingBlocks);
chance_of_moving_block_down_ =
ChooseBetweenMinAndMax(kChanceOfMovingBlockDown);
Expand All @@ -101,6 +105,7 @@ FuzzerContext::FuzzerContext(RandomGenerator* random_generator,
chance_of_splitting_block_ = ChooseBetweenMinAndMax(kChanceOfSplittingBlock);
max_loop_control_partial_count_ = kDefaultMaxLoopControlPartialCount;
max_loop_control_peel_count_ = kDefaultMaxLoopControlPeelCount;
max_loop_limit_ = kDefaultMaxLoopLimit;
}

FuzzerContext::~FuzzerContext() = default;
Expand Down
8 changes: 8 additions & 0 deletions source/fuzz/fuzzer_context.h
Original file line number Diff line number Diff line change
Expand Up @@ -85,6 +85,9 @@ class FuzzerContext {
uint32_t GetChanceOfDonatingAdditionalModule() {
return chance_of_donating_additional_module_;
}
uint32_t ChanceOfMakingDonorLivesafe() {
return chance_of_making_donor_livesafe_;
}
uint32_t GetChanceOfMergingBlocks() { return chance_of_merging_blocks_; }
uint32_t GetChanceOfMovingBlockDown() { return chance_of_moving_block_down_; }
uint32_t GetChanceOfObfuscatingConstant() {
Expand All @@ -103,6 +106,9 @@ class FuzzerContext {
uint32_t GetRandomLoopControlPartialCount() {
return random_generator_->RandomUint32(max_loop_control_partial_count_);
}
uint32_t GetRandomLoopLimit() {
return random_generator_->RandomUint32(max_loop_limit_);
}

// Functions to control how deeply to recurse.
// Keep them in alphabetical order.
Expand All @@ -129,6 +135,7 @@ class FuzzerContext {
uint32_t chance_of_constructing_composite_;
uint32_t chance_of_copying_object_;
uint32_t chance_of_donating_additional_module_;
uint32_t chance_of_making_donor_livesafe_;
uint32_t chance_of_merging_blocks_;
uint32_t chance_of_moving_block_down_;
uint32_t chance_of_obfuscating_constant_;
Expand All @@ -141,6 +148,7 @@ class FuzzerContext {
// Keep them in alphabetical order.
uint32_t max_loop_control_partial_count_;
uint32_t max_loop_control_peel_count_;
uint32_t max_loop_limit_;

// Functions to determine with what probability to go deeper when generating
// or mutating constructs recursively.
Expand Down
73 changes: 73 additions & 0 deletions source/fuzz/fuzzer_pass.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,11 @@
#include "source/fuzz/fuzzer_pass.h"

#include "source/fuzz/instruction_descriptor.h"
#include "source/fuzz/transformation_add_constant_scalar.h"
#include "source/fuzz/transformation_add_global_undef.h"
#include "source/fuzz/transformation_add_type_boolean.h"
#include "source/fuzz/transformation_add_type_int.h"
#include "source/fuzz/transformation_add_type_pointer.h"

namespace spvtools {
namespace fuzz {
Expand Down Expand Up @@ -128,5 +133,73 @@ void FuzzerPass::MaybeAddTransformationBeforeEachInstruction(
}
}

uint32_t FuzzerPass::FindOrCreateBoolType() {
opt::analysis::Bool bool_type;
auto existing_id = GetIRContext()->get_type_mgr()->GetId(&bool_type);
if (existing_id) {
return existing_id;
}
auto result = GetFuzzerContext()->GetFreshId();
ApplyTransformation(TransformationAddTypeBoolean(result));
return result;
}

uint32_t FuzzerPass::FindOrCreate32BitIntegerType(bool is_signed) {
opt::analysis::Integer int_type(32, is_signed);
auto existing_id = GetIRContext()->get_type_mgr()->GetId(&int_type);
if (existing_id) {
return existing_id;
}
auto result = GetFuzzerContext()->GetFreshId();
ApplyTransformation(TransformationAddTypeInt(result, 32, is_signed));
return result;
}

uint32_t FuzzerPass::FindOrCreatePointerTo32BitIntegerType(
bool is_signed, SpvStorageClass storage_class) {
auto uint32_type_id = FindOrCreate32BitIntegerType(is_signed);
opt::analysis::Pointer pointer_type(
GetIRContext()->get_type_mgr()->GetType(uint32_type_id), storage_class);
auto existing_id = GetIRContext()->get_type_mgr()->GetId(&pointer_type);
if (existing_id) {
return existing_id;
}
auto result = GetFuzzerContext()->GetFreshId();
ApplyTransformation(
TransformationAddTypePointer(result, storage_class, uint32_type_id));
return result;
}

uint32_t FuzzerPass::FindOrCreate32BitIntegerConstant(uint32_t word,
bool is_signed) {
auto uint32_type_id = FindOrCreate32BitIntegerType(is_signed);
opt::analysis::IntConstant int_constant(
GetIRContext()->get_type_mgr()->GetType(uint32_type_id)->AsInteger(),
{word});
auto existing_constant =
GetIRContext()->get_constant_mgr()->FindConstant(&int_constant);
if (existing_constant) {
return GetIRContext()
->get_constant_mgr()
->GetDefiningInstruction(existing_constant)
->result_id();
}
auto result = GetFuzzerContext()->GetFreshId();
ApplyTransformation(
TransformationAddConstantScalar(result, uint32_type_id, {word}));
return result;
}

uint32_t FuzzerPass::FindOrCreateGlobalUndef(uint32_t type_id) {
for (auto& inst : GetIRContext()->types_values()) {
if (inst.opcode() == SpvOpUndef && inst.type_id() == type_id) {
return inst.result_id();
}
}
auto result = GetFuzzerContext()->GetFreshId();
ApplyTransformation(TransformationAddGlobalUndef(result, type_id));
return result;
}

} // namespace fuzz
} // namespace spvtools
29 changes: 28 additions & 1 deletion source/fuzz/fuzzer_pass.h
Original file line number Diff line number Diff line change
Expand Up @@ -89,7 +89,7 @@ class FuzzerPass {
const protobufs::InstructionDescriptor& instruction_descriptor)>
maybe_apply_transformation);

// A generic helper for applying a transforamtion that should be appplicable
// A generic helper for applying a transformation that should be applicable
// by construction, and adding it to the sequence of applied transformations.
template <typename TransformationType>
void ApplyTransformation(const TransformationType& transformation) {
Expand All @@ -99,6 +99,33 @@ class FuzzerPass {
*GetTransformations()->add_transformation() = transformation.ToMessage();
}

// Returns the id of an OpTypeBool instruction. If such an instruction does
// not exist, a transformation is applied to add it.
uint32_t FindOrCreateBoolType();

// Returns the id of an OpTypeInt instruction, with width 32 and signedness
// specified by |is_signed|. If such an instruction does not exist, a
// transformation is applied to add it.
uint32_t FindOrCreate32BitIntegerType(bool is_signed);

// Returns the id of an OpTypePointer instruction, with a 32-bit integer base
// type of signedness specified by |is_signed|. If the pointer type or
// required integer base type do not exist, transformations are applied to add
// them.
uint32_t FindOrCreatePointerTo32BitIntegerType(bool is_signed,
SpvStorageClass storage_class);

// Returns the id of an OpConstant instruction, with 32-bit integer type of
// signedness specified by |is_signed|, with |word| as its value. If either
// the required integer type or the constant do not exist, transformations are
// applied to add them.
uint32_t FindOrCreate32BitIntegerConstant(uint32_t word, bool is_signed);

// Returns the result id of an instruction of the form:
// %id = OpUndef %|type_id|
// If no such instruction exists, a transformation is applied to add it.
uint32_t FindOrCreateGlobalUndef(uint32_t type_id);

private:
opt::IRContext* ir_context_;
FactManager* fact_manager_;
Expand Down
Loading