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: Arbitrary variable facts #3165

Merged
merged 2 commits into from
Jan 30, 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
45 changes: 44 additions & 1 deletion source/fuzz/fact_manager.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -859,11 +859,43 @@ bool FactManager::LivesafeFunctionFacts::FunctionIsLivesafe(
// End of livesafe function facts
//==============================

//==============================
// Arbitrarily-valued variable facts

// The purpose of this class is to group the fields and data used to represent
// facts about livesafe functions.
class FactManager::ArbitrarilyValuedVaribleFacts {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Typo: Varible

public:
// See method in FactManager which delegates to this method.
void AddFact(const protobufs::FactValueOfVariableIsArbitrary& fact);

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

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

void FactManager::ArbitrarilyValuedVaribleFacts::AddFact(
const protobufs::FactValueOfVariableIsArbitrary& fact) {
arbitrary_valued_varible_ids_.insert(fact.variable_id());
}

bool FactManager::ArbitrarilyValuedVaribleFacts::VariableValueIsArbitrary(
uint32_t variable_id) const {
return arbitrary_valued_varible_ids_.count(variable_id) != 0;
}

// End of arbitrarily-valued variable facts
//==============================

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

FactManager::~FactManager() = default;

Expand Down Expand Up @@ -985,5 +1017,16 @@ void FactManager::AddFactFunctionIsLivesafe(uint32_t function_id) {
livesafe_function_facts_->AddFact(fact);
}

bool FactManager::VariableValueIsArbitrary(uint32_t variable_id) const {
return arbitrarily_valued_variable_facts_->VariableValueIsArbitrary(
variable_id);
}

void FactManager::AddFactValueOfVariableIsArbitrary(uint32_t variable_id) {
protobufs::FactValueOfVariableIsArbitrary fact;
fact.set_variable_id(variable_id);
arbitrarily_valued_variable_facts_->AddFact(fact);
}

} // namespace fuzz
} // namespace spvtools
20 changes: 19 additions & 1 deletion source/fuzz/fact_manager.h
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,10 @@ class FactManager {
// Records the fact that |function_id| is livesafe.
void AddFactFunctionIsLivesafe(uint32_t function_id);

// Records the fact that |variable_id| has an arbitrary value and can thus be
// stored to without affecting the module's behaviour.
void AddFactValueOfVariableIsArbitrary(uint32_t variable_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 @@ -153,7 +157,16 @@ class FactManager {
// to be livesafe.
bool FunctionIsLivesafe(uint32_t function_id) const;

// End of dead block facts
// End of dead livesafe function facts
//==============================

//==============================
// Querying facts about arbitrarily-valued variables

// Returns true if and ony if |variable_id| is arbitrarily-valued.
bool VariableValueIsArbitrary(uint32_t variable_id) const;

// End of arbitrarily-valued variable facts
//==============================

private:
Expand All @@ -177,6 +190,11 @@ class FactManager {
// function facts.
std::unique_ptr<LivesafeFunctionFacts>
livesafe_function_facts_; // Unique pointer to internal data.

class ArbitrarilyValuedVaribleFacts; // Opaque class for management of
// facts about variables whose values should be expected to be arbitrary.
std::unique_ptr<ArbitrarilyValuedVaribleFacts>
arbitrarily_valued_variable_facts_; // Unique pointer to internal data.
};

} // namespace fuzz
Expand Down
8 changes: 7 additions & 1 deletion source/fuzz/fuzzer_pass_donate_modules.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -396,14 +396,20 @@ void FuzzerPassDonateModules::HandleTypesAndValues(
// have storage class private. As a result, we simply add a Private
// storage class global variable, using remapped versions of the result
// type and initializer ids for the global variable in the donor.
//
// We regard the added variable as having an arbitrary value. This
// means that future passes can add stores to the variable in any
// way they wish, and pass them as pointer parameters to functions
// without worrying about whether their data might get modified.
new_result_id = GetFuzzerContext()->GetFreshId();
ApplyTransformation(TransformationAddGlobalVariable(
new_result_id,
original_id_to_donated_id->at(type_or_value.type_id()),
type_or_value.NumInOperands() == 1
? 0
: original_id_to_donated_id->at(
type_or_value.GetSingleWordInOperand(1))));
type_or_value.GetSingleWordInOperand(1)),
true));
} break;
case SpvOpUndef: {
// It is fine to have multiple Undef instructions of the same type, so
Expand Down
21 changes: 21 additions & 0 deletions source/fuzz/protobufs/spvtoolsfuzz.proto
Original file line number Diff line number Diff line change
Expand Up @@ -168,6 +168,7 @@ message Fact {
FactDataSynonym data_synonym_fact = 2;
FactBlockIsDead block_is_dead_fact = 3;
FactFunctionIsLivesafe function_is_livesafe_fact = 4;
FactValueOfVariableIsArbitrary value_of_variable_is_arbitrary = 5;
}
}

Expand Down Expand Up @@ -222,6 +223,19 @@ message FactFunctionIsLivesafe {
uint32 function_id = 1;
}

message FactValueOfVariableIsArbitrary {

// Records the fact that the value stored in the variable or function
// parameter with the given id can be arbitrary: the module's observable
// behaviour does not depend on it. This means that arbitrary stores can be
// made to the variable, and that nothing can be guaranteed about values
// loaded from the variable.

// The result id of an OpVariable instruction, or an OpFunctionParameter
// instruction with pointer type
uint32 variable_id = 1;
}

message AccessChainClampingInfo {

// When making a function livesafe it is necessary to clamp the indices that
Expand Down Expand Up @@ -493,6 +507,13 @@ message TransformationAddGlobalVariable {
// Optional initializer; 0 if there is no initializer
uint32 initializer_id = 3;

// True if and only if the value of the variable should be regarded, in
// general, as totally unknown and subject to change (even if, due to an
// initializer, the original value is known). This is the case for variables
// added when a module is donated, for example, and means that stores to such
// variables can be performed in an arbitrary fashion.
bool value_is_arbitrary = 4;

}

message TransformationAddNoContractionDecoration {
Expand Down
24 changes: 24 additions & 0 deletions source/fuzz/transformation_add_function.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -154,6 +154,30 @@ void TransformationAddFunction::Apply(
(void)(success); // Keep release builds happy (otherwise they may complain
// that |success| is not used).

// Record the fact that all pointer parameters and variables declared in the
// function should be regarded as having arbitrary values. This allows other
// passes to store arbitrarily to such variables, and to pass them freely as
// parameters to other functions knowing that it is OK if they get
// over-written.
for (auto& instruction : message_.instruction()) {
switch (instruction.opcode()) {
case SpvOpFunctionParameter:
if (context->get_def_use_mgr()
->GetDef(instruction.result_type_id())
->opcode() == SpvOpTypePointer) {
fact_manager->AddFactValueOfVariableIsArbitrary(
instruction.result_id());
}
break;
case SpvOpVariable:
fact_manager->AddFactValueOfVariableIsArbitrary(
instruction.result_id());
break;
default:
break;
}
}

if (message_.is_livesafe()) {
// Make the function livesafe, which also should succeed.
success = TryToMakeFunctionLivesafe(context, *fact_manager);
Expand Down
10 changes: 8 additions & 2 deletions source/fuzz/transformation_add_global_variable.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -24,10 +24,12 @@ TransformationAddGlobalVariable::TransformationAddGlobalVariable(
: message_(message) {}

TransformationAddGlobalVariable::TransformationAddGlobalVariable(
uint32_t fresh_id, uint32_t type_id, uint32_t initializer_id) {
uint32_t fresh_id, uint32_t type_id, uint32_t initializer_id,
bool value_is_arbitrary) {
message_.set_fresh_id(fresh_id);
message_.set_type_id(type_id);
message_.set_initializer_id(initializer_id);
message_.set_value_is_arbitrary(value_is_arbitrary);
}

bool TransformationAddGlobalVariable::IsApplicable(
Expand Down Expand Up @@ -71,7 +73,7 @@ bool TransformationAddGlobalVariable::IsApplicable(
}

void TransformationAddGlobalVariable::Apply(
opt::IRContext* context, spvtools::fuzz::FactManager* /*unused*/) const {
opt::IRContext* context, spvtools::fuzz::FactManager* fact_manager) const {
opt::Instruction::OperandList input_operands;
input_operands.push_back(
{SPV_OPERAND_TYPE_STORAGE_CLASS, {SpvStorageClassPrivate}});
Expand Down Expand Up @@ -99,6 +101,10 @@ void TransformationAddGlobalVariable::Apply(
}
}

if (message_.value_is_arbitrary()) {
fact_manager->AddFactValueOfVariableIsArbitrary(message_.fresh_id());
}

// We have added an instruction to the module, so need to be careful about the
// validity of existing analyses.
context->InvalidateAnalysesExceptFor(opt::IRContext::Analysis::kAnalysisNone);
Expand Down
3 changes: 2 additions & 1 deletion source/fuzz/transformation_add_global_variable.h
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,8 @@ class TransformationAddGlobalVariable : public Transformation {
const protobufs::TransformationAddGlobalVariable& message);

TransformationAddGlobalVariable(uint32_t fresh_id, uint32_t type_id,
uint32_t initializer_id);
uint32_t initializer_id,
bool value_is_arbitrary);

// - |message_.fresh_id| must be fresh
// - |message_.type_id| must be the id of a pointer type with Private storage
Expand Down
23 changes: 19 additions & 4 deletions source/fuzz/transformation_outline_function.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -340,8 +340,16 @@ void TransformationOutlineFunction::Apply(

// Make a function prototype for the outlined function, which involves
// figuring out its required type.
std::unique_ptr<opt::Function> outlined_function = PrepareFunctionPrototype(
context, region_input_ids, region_output_ids, input_id_to_fresh_id_map);
std::unique_ptr<opt::Function> outlined_function =
PrepareFunctionPrototype(region_input_ids, region_output_ids,
input_id_to_fresh_id_map, context, fact_manager);

// If the original function was livesafe, the new function should also be
// livesafe.
if (fact_manager->FunctionIsLivesafe(
original_region_entry_block->GetParent()->result_id())) {
fact_manager->AddFactFunctionIsLivesafe(message_.new_function_id());
}

// Adapt the region to be outlined so that its input ids are replaced with the
// ids of the outlined function's input parameters, and so that output ids
Expand Down Expand Up @@ -524,9 +532,10 @@ std::set<opt::BasicBlock*> TransformationOutlineFunction::GetRegionBlocks(

std::unique_ptr<opt::Function>
TransformationOutlineFunction::PrepareFunctionPrototype(
opt::IRContext* context, const std::vector<uint32_t>& region_input_ids,
const std::vector<uint32_t>& region_input_ids,
const std::vector<uint32_t>& region_output_ids,
const std::map<uint32_t, uint32_t>& input_id_to_fresh_id_map) const {
const std::map<uint32_t, uint32_t>& input_id_to_fresh_id_map,
opt::IRContext* context, FactManager* fact_manager) const {
uint32_t return_type_id = 0;
uint32_t function_type_id = 0;

Expand Down Expand Up @@ -608,6 +617,12 @@ TransformationOutlineFunction::PrepareFunctionPrototype(
context, SpvOpFunctionParameter,
context->get_def_use_mgr()->GetDef(id)->type_id(),
input_id_to_fresh_id_map.at(id), opt::Instruction::OperandList()));
// If the input id is an arbitrary-valued variable, the same should be true
// of the corresponding parameter.
if (fact_manager->VariableValueIsArbitrary(id)) {
fact_manager->AddFactValueOfVariableIsArbitrary(
input_id_to_fresh_id_map.at(id));
}
}

return outlined_function;
Expand Down
8 changes: 6 additions & 2 deletions source/fuzz/transformation_outline_function.h
Original file line number Diff line number Diff line change
Expand Up @@ -158,10 +158,14 @@ class TransformationOutlineFunction : public Transformation {
// A new struct type to represent the function return type, and a new function
// type for the function, will be added to the module (unless suitable types
// are already present).
//
// Facts about the function containing the outlined region that are relevant
// to the new function are propagated via |fact_manager|.
std::unique_ptr<opt::Function> PrepareFunctionPrototype(
opt::IRContext* context, const std::vector<uint32_t>& region_input_ids,
const std::vector<uint32_t>& region_input_ids,
const std::vector<uint32_t>& region_output_ids,
const std::map<uint32_t, uint32_t>& input_id_to_fresh_id_map) const;
const std::map<uint32_t, uint32_t>& input_id_to_fresh_id_map,
opt::IRContext* context, FactManager* fact_manager) const;

// Creates the body of the outlined function by cloning blocks from the
// original region, given by |region_blocks|, adapting the cloned version
Expand Down
Loading