spirv-fuzz: Remove FuzzerPassAddUsefulConstructs (#3341)

Fixes #3318.
This commit is contained in:
Vasyl Teliman 2020-05-19 17:54:55 +03:00 committed by GitHub
parent 522561619a
commit 2f69ea849a
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
14 changed files with 131 additions and 705 deletions

View File

@ -49,7 +49,6 @@ if(SPIRV_BUILD_FUZZER)
fuzzer_pass_add_local_variables.h
fuzzer_pass_add_no_contraction_decorations.h
fuzzer_pass_add_stores.h
fuzzer_pass_add_useful_constructs.h
fuzzer_pass_adjust_branch_weights.h
fuzzer_pass_adjust_function_controls.h
fuzzer_pass_adjust_loop_controls.h
@ -146,7 +145,6 @@ if(SPIRV_BUILD_FUZZER)
fuzzer_pass_add_local_variables.cpp
fuzzer_pass_add_no_contraction_decorations.cpp
fuzzer_pass_add_stores.cpp
fuzzer_pass_add_useful_constructs.cpp
fuzzer_pass_adjust_branch_weights.cpp
fuzzer_pass_adjust_function_controls.cpp
fuzzer_pass_adjust_loop_controls.cpp

View File

@ -33,7 +33,6 @@
#include "source/fuzz/fuzzer_pass_add_local_variables.h"
#include "source/fuzz/fuzzer_pass_add_no_contraction_decorations.h"
#include "source/fuzz/fuzzer_pass_add_stores.h"
#include "source/fuzz/fuzzer_pass_add_useful_constructs.h"
#include "source/fuzz/fuzzer_pass_adjust_branch_weights.h"
#include "source/fuzz/fuzzer_pass_adjust_function_controls.h"
#include "source/fuzz/fuzzer_pass_adjust_loop_controls.h"
@ -187,16 +186,6 @@ Fuzzer::FuzzerResultStatus Fuzzer::Run(
TransformationContext transformation_context(&fact_manager,
impl_->validator_options);
// Add some essential ingredients to the module if they are not already
// present, such as boolean constants.
FuzzerPassAddUsefulConstructs add_useful_constructs(
ir_context.get(), &transformation_context, &fuzzer_context,
transformation_sequence_out);
if (!impl_->ApplyPassAndCheckValidity(&add_useful_constructs, *ir_context,
tools)) {
return Fuzzer::FuzzerResultStatus::kFuzzerPassLedToInvalidModule;
}
// Apply some semantics-preserving passes.
std::vector<std::unique_ptr<FuzzerPass>> passes;
while (passes.empty()) {

View File

@ -319,6 +319,35 @@ uint32_t FuzzerPass::FindOrCreateBoolConstant(bool value) {
return result;
}
uint32_t FuzzerPass::FindOrCreateConstant(const std::vector<uint32_t>& words,
uint32_t type_id) {
assert(type_id && "Constant's type id can't be 0.");
const auto* type = GetIRContext()->get_type_mgr()->GetType(type_id);
assert(type && "Type does not exist.");
if (type->AsBool()) {
assert(words.size() == 1);
return FindOrCreateBoolConstant(words[0]);
} else if (const auto* integer = type->AsInteger()) {
assert(integer->width() == 32 && words.size() == 1 &&
"Integer must have 32-bit width");
return FindOrCreate32BitIntegerConstant(words[0], integer->IsSigned());
} else if (const auto* floating = type->AsFloat()) {
// Assertions are not evaluated in release builds so |floating|
// variable will be unused.
(void)floating;
assert(floating->width() == 32 && words.size() == 1 &&
"Floating point number must have 32-bit width");
return FindOrCreate32BitFloatConstant(words[0]);
}
// This assertion will fail in debug build but not in release build
// so we return 0 to make compiler happy.
assert(false && "Constant type is not supported");
return 0;
}
uint32_t FuzzerPass::FindOrCreateGlobalUndef(uint32_t type_id) {
for (auto& inst : GetIRContext()->types_values()) {
if (inst.opcode() == SpvOpUndef && inst.type_id() == type_id) {

View File

@ -164,6 +164,14 @@ class FuzzerPass {
// type do not exist, transformations are applied to add them.
uint32_t FindOrCreateBoolConstant(bool value);
// Returns the id of an OpConstant instruction of type with |type_id|
// that consists of |words|. If that instruction doesn't exist,
// transformations are applied to add it. |type_id| must be a valid
// result id of either scalar or boolean OpType* instruction that exists
// in the module.
uint32_t FindOrCreateConstant(const std::vector<uint32_t>& words,
uint32_t type_id);
// 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.

View File

@ -41,6 +41,12 @@ void FuzzerPassAddDeadBlocks::Apply() {
GetFuzzerContext()->GetChanceOfAddingDeadBlock())) {
continue;
}
// Make sure the module contains a boolean constant equal to
// |condition_value|.
bool condition_value = GetFuzzerContext()->ChooseEven();
FindOrCreateBoolConstant(condition_value);
// We speculatively create a transformation, and then apply it (below) if
// it turns out to be applicable. This avoids duplicating the logic for
// applicability checking.
@ -48,8 +54,7 @@ void FuzzerPassAddDeadBlocks::Apply() {
// It means that fresh ids for transformations that turn out not to be
// applicable end up being unused.
candidate_transformations.emplace_back(TransformationAddDeadBlock(
GetFuzzerContext()->GetFreshId(), block.id(),
GetFuzzerContext()->ChooseEven()));
GetFuzzerContext()->GetFreshId(), block.id(), condition_value));
}
}
// Apply all those transformations that are in fact applicable.

View File

@ -77,9 +77,13 @@ void FuzzerPassAddDeadBreaks::Apply() {
});
}
// Make sure the module has a required boolean constant to be used in
// OpBranchConditional instruction.
auto break_condition = GetFuzzerContext()->ChooseEven();
FindOrCreateBoolConstant(break_condition);
auto candidate_transformation = TransformationAddDeadBreak(
block.id(), merge_block->id(), GetFuzzerContext()->ChooseEven(),
std::move(phi_ids));
block.id(), merge_block->id(), break_condition, std::move(phi_ids));
if (candidate_transformation.IsApplicable(
GetIRContext(), *GetTransformationContext())) {
// Only consider a transformation as a candidate if it is applicable.

View File

@ -68,11 +68,16 @@ void FuzzerPassAddDeadContinues::Apply() {
});
}
// Make sure the module contains a boolean constant equal to
// |condition_value|.
bool condition_value = GetFuzzerContext()->ChooseEven();
FindOrCreateBoolConstant(condition_value);
// Make a transformation to add a dead continue from this node; if the
// node turns out to be inappropriate (e.g. by not being in a loop) the
// precondition for the transformation will fail and it will be ignored.
auto candidate_transformation = TransformationAddDeadContinue(
block.id(), GetFuzzerContext()->ChooseEven(), std::move(phi_ids));
block.id(), condition_value, std::move(phi_ids));
// Probabilistically decide whether to apply the transformation in the
// case that it is applicable.
if (candidate_transformation.IsApplicable(GetIRContext(),

View File

@ -1,222 +0,0 @@
// 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.
#include "source/fuzz/fuzzer_pass_add_useful_constructs.h"
#include "source/fuzz/transformation_add_constant_boolean.h"
#include "source/fuzz/transformation_add_constant_scalar.h"
#include "source/fuzz/transformation_add_type_boolean.h"
#include "source/fuzz/transformation_add_type_float.h"
#include "source/fuzz/transformation_add_type_int.h"
#include "source/fuzz/transformation_add_type_pointer.h"
namespace spvtools {
namespace fuzz {
FuzzerPassAddUsefulConstructs::FuzzerPassAddUsefulConstructs(
opt::IRContext* ir_context, TransformationContext* transformation_context,
FuzzerContext* fuzzer_context,
protobufs::TransformationSequence* transformations)
: FuzzerPass(ir_context, transformation_context, fuzzer_context,
transformations) {}
FuzzerPassAddUsefulConstructs::~FuzzerPassAddUsefulConstructs() = default;
void FuzzerPassAddUsefulConstructs::MaybeAddIntConstant(
uint32_t width, bool is_signed, std::vector<uint32_t> data) const {
opt::analysis::Integer temp_int_type(width, is_signed);
assert(GetIRContext()->get_type_mgr()->GetId(&temp_int_type) &&
"int type should already be registered.");
auto registered_int_type = GetIRContext()
->get_type_mgr()
->GetRegisteredType(&temp_int_type)
->AsInteger();
auto int_type_id = GetIRContext()->get_type_mgr()->GetId(registered_int_type);
assert(int_type_id &&
"The relevant int type should have been added to the module already.");
opt::analysis::IntConstant int_constant(registered_int_type, data);
if (!GetIRContext()->get_constant_mgr()->FindConstant(&int_constant)) {
TransformationAddConstantScalar add_constant_int =
TransformationAddConstantScalar(GetFuzzerContext()->GetFreshId(),
int_type_id, data);
assert(add_constant_int.IsApplicable(GetIRContext(),
*GetTransformationContext()) &&
"Should be applicable by construction.");
add_constant_int.Apply(GetIRContext(), GetTransformationContext());
*GetTransformations()->add_transformation() = add_constant_int.ToMessage();
}
}
void FuzzerPassAddUsefulConstructs::MaybeAddFloatConstant(
uint32_t width, std::vector<uint32_t> data) const {
opt::analysis::Float temp_float_type(width);
assert(GetIRContext()->get_type_mgr()->GetId(&temp_float_type) &&
"float type should already be registered.");
auto registered_float_type = GetIRContext()
->get_type_mgr()
->GetRegisteredType(&temp_float_type)
->AsFloat();
auto float_type_id =
GetIRContext()->get_type_mgr()->GetId(registered_float_type);
assert(
float_type_id &&
"The relevant float type should have been added to the module already.");
opt::analysis::FloatConstant float_constant(registered_float_type, data);
if (!GetIRContext()->get_constant_mgr()->FindConstant(&float_constant)) {
TransformationAddConstantScalar add_constant_float =
TransformationAddConstantScalar(GetFuzzerContext()->GetFreshId(),
float_type_id, data);
assert(add_constant_float.IsApplicable(GetIRContext(),
*GetTransformationContext()) &&
"Should be applicable by construction.");
add_constant_float.Apply(GetIRContext(), GetTransformationContext());
*GetTransformations()->add_transformation() =
add_constant_float.ToMessage();
}
}
void FuzzerPassAddUsefulConstructs::Apply() {
{
// Add boolean type if not present.
opt::analysis::Bool temp_bool_type;
if (!GetIRContext()->get_type_mgr()->GetId(&temp_bool_type)) {
auto add_type_boolean =
TransformationAddTypeBoolean(GetFuzzerContext()->GetFreshId());
assert(add_type_boolean.IsApplicable(GetIRContext(),
*GetTransformationContext()) &&
"Should be applicable by construction.");
add_type_boolean.Apply(GetIRContext(), GetTransformationContext());
*GetTransformations()->add_transformation() =
add_type_boolean.ToMessage();
}
}
{
// Add signed and unsigned 32-bit integer types if not present.
for (auto is_signed : {true, false}) {
opt::analysis::Integer temp_int_type(32, is_signed);
if (!GetIRContext()->get_type_mgr()->GetId(&temp_int_type)) {
TransformationAddTypeInt add_type_int = TransformationAddTypeInt(
GetFuzzerContext()->GetFreshId(), 32, is_signed);
assert(add_type_int.IsApplicable(GetIRContext(),
*GetTransformationContext()) &&
"Should be applicable by construction.");
add_type_int.Apply(GetIRContext(), GetTransformationContext());
*GetTransformations()->add_transformation() = add_type_int.ToMessage();
}
}
}
{
// Add 32-bit float type if not present.
opt::analysis::Float temp_float_type(32);
if (!GetIRContext()->get_type_mgr()->GetId(&temp_float_type)) {
TransformationAddTypeFloat add_type_float =
TransformationAddTypeFloat(GetFuzzerContext()->GetFreshId(), 32);
assert(add_type_float.IsApplicable(GetIRContext(),
*GetTransformationContext()) &&
"Should be applicable by construction.");
add_type_float.Apply(GetIRContext(), GetTransformationContext());
*GetTransformations()->add_transformation() = add_type_float.ToMessage();
}
}
// Add boolean constants true and false if not present.
opt::analysis::Bool temp_bool_type;
auto bool_type = GetIRContext()
->get_type_mgr()
->GetRegisteredType(&temp_bool_type)
->AsBool();
for (auto boolean_value : {true, false}) {
// Add OpConstantTrue/False if not already there.
opt::analysis::BoolConstant bool_constant(bool_type, boolean_value);
if (!GetIRContext()->get_constant_mgr()->FindConstant(&bool_constant)) {
TransformationAddConstantBoolean add_constant_boolean(
GetFuzzerContext()->GetFreshId(), boolean_value);
assert(add_constant_boolean.IsApplicable(GetIRContext(),
*GetTransformationContext()) &&
"Should be applicable by construction.");
add_constant_boolean.Apply(GetIRContext(), GetTransformationContext());
*GetTransformations()->add_transformation() =
add_constant_boolean.ToMessage();
}
}
// Add signed and unsigned 32-bit integer constants 0 and 1 if not present.
for (auto is_signed : {true, false}) {
for (auto value : {0u, 1u}) {
MaybeAddIntConstant(32, is_signed, {value});
}
}
// Add 32-bit float constants 0.0 and 1.0 if not present.
uint32_t uint_data[2];
float float_data[2] = {0.0, 1.0};
memcpy(uint_data, float_data, sizeof(float_data));
for (unsigned int& datum : uint_data) {
MaybeAddFloatConstant(32, {datum});
}
// For every known-to-be-constant uniform, make sure we have instructions
// declaring:
// - a pointer type with uniform storage class, whose pointee type is the type
// of the element
// - a signed integer constant for each index required to access the element
// - a constant for the constant value itself
for (auto& fact_and_type_id : GetTransformationContext()
->GetFactManager()
->GetConstantUniformFactsAndTypes()) {
uint32_t element_type_id = fact_and_type_id.second;
assert(element_type_id);
auto element_type =
GetIRContext()->get_type_mgr()->GetType(element_type_id);
assert(element_type &&
"If the constant uniform fact is well-formed, the module must "
"already have a declaration of the type for the uniform element.");
opt::analysis::Pointer uniform_pointer(element_type,
SpvStorageClassUniform);
if (!GetIRContext()->get_type_mgr()->GetId(&uniform_pointer)) {
auto add_pointer =
TransformationAddTypePointer(GetFuzzerContext()->GetFreshId(),
SpvStorageClassUniform, element_type_id);
assert(add_pointer.IsApplicable(GetIRContext(),
*GetTransformationContext()) &&
"Should be applicable by construction.");
add_pointer.Apply(GetIRContext(), GetTransformationContext());
*GetTransformations()->add_transformation() = add_pointer.ToMessage();
}
std::vector<uint32_t> words;
for (auto word : fact_and_type_id.first.constant_word()) {
words.push_back(word);
}
// We get the element type again as the type manager may have been
// invalidated since we last retrieved it.
element_type = GetIRContext()->get_type_mgr()->GetType(element_type_id);
if (element_type->AsInteger()) {
MaybeAddIntConstant(element_type->AsInteger()->width(),
element_type->AsInteger()->IsSigned(), words);
} else {
assert(element_type->AsFloat() &&
"Known uniform values must be integer or floating-point.");
MaybeAddFloatConstant(element_type->AsFloat()->width(), words);
}
for (auto index :
fact_and_type_id.first.uniform_buffer_element_descriptor().index()) {
MaybeAddIntConstant(32, true, {index});
}
}
}
} // namespace fuzz
} // namespace spvtools

View File

@ -1,46 +0,0 @@
// 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_PASS_ADD_USEFUL_CONSTRUCTS_
#define SOURCE_FUZZ_FUZZER_PASS_ADD_USEFUL_CONSTRUCTS_
#include "source/fuzz/fuzzer_pass.h"
namespace spvtools {
namespace fuzz {
// An initial pass for adding useful ingredients to the module, such as boolean
// constants, if they are not present.
class FuzzerPassAddUsefulConstructs : public FuzzerPass {
public:
FuzzerPassAddUsefulConstructs(
opt::IRContext* ir_context, TransformationContext* transformation_context,
FuzzerContext* fuzzer_context,
protobufs::TransformationSequence* transformations);
~FuzzerPassAddUsefulConstructs() override;
void Apply() override;
private:
void MaybeAddIntConstant(uint32_t width, bool is_signed,
std::vector<uint32_t> data) const;
void MaybeAddFloatConstant(uint32_t width, std::vector<uint32_t> data) const;
};
} // namespace fuzz
} // namespace spvtools
#endif // SOURCE_FUZZ_FUZZER_PASS_ADD_USEFUL_CONSTRUCTS_

View File

@ -14,11 +14,14 @@
#include "source/fuzz/fuzzer_pass_obfuscate_constants.h"
#include <algorithm>
#include <cmath>
#include "source/fuzz/fuzzer_util.h"
#include "source/fuzz/instruction_descriptor.h"
#include "source/fuzz/transformation_replace_boolean_constant_with_constant_binary.h"
#include "source/fuzz/transformation_replace_constant_with_uniform.h"
#include "source/fuzz/uniform_buffer_element_descriptor.h"
#include "source/opt/ir_context.h"
namespace spvtools {
@ -240,6 +243,29 @@ void FuzzerPassObfuscateConstants::
first_constant_is_larger);
}
std::vector<std::vector<uint32_t>>
FuzzerPassObfuscateConstants::GetConstantWordsFromUniformsForType(
uint32_t type_id) {
assert(type_id && "Type id can't be 0");
std::vector<std::vector<uint32_t>> result;
for (const auto& facts_and_types : GetTransformationContext()
->GetFactManager()
->GetConstantUniformFactsAndTypes()) {
if (facts_and_types.second != type_id) {
continue;
}
std::vector<uint32_t> words(facts_and_types.first.constant_word().begin(),
facts_and_types.first.constant_word().end());
if (std::find(result.begin(), result.end(), words) == result.end()) {
result.push_back(std::move(words));
}
}
return result;
}
void FuzzerPassObfuscateConstants::ObfuscateBoolConstant(
uint32_t depth, const protobufs::IdUseDescriptor& constant_use) {
// We want to replace the boolean constant use with a binary expression over
@ -258,11 +284,9 @@ void FuzzerPassObfuscateConstants::ObfuscateBoolConstant(
auto chosen_type_id =
available_types_with_uniforms[GetFuzzerContext()->RandomIndex(
available_types_with_uniforms)];
auto available_constants = GetTransformationContext()
->GetFactManager()
->GetConstantsAvailableFromUniformsForType(
GetIRContext(), chosen_type_id);
if (available_constants.size() == 1) {
auto available_constant_words =
GetConstantWordsFromUniformsForType(chosen_type_id);
if (available_constant_words.size() == 1) {
// TODO(afd): for now we only obfuscate a boolean if there are at least
// two constants available from uniforms, so that we can do a
// comparison between them. It would be good to be able to do the
@ -271,18 +295,25 @@ void FuzzerPassObfuscateConstants::ObfuscateBoolConstant(
return;
}
assert(!available_constant_words.empty() &&
"There exists a fact but no constants - impossible");
// We know we have at least two known-to-be-constant uniforms of the chosen
// type. Pick one of them at random.
auto constant_index_1 = GetFuzzerContext()->RandomIndex(available_constants);
auto constant_index_1 =
GetFuzzerContext()->RandomIndex(available_constant_words);
uint32_t constant_index_2;
// Now choose another one distinct from the first one.
do {
constant_index_2 = GetFuzzerContext()->RandomIndex(available_constants);
constant_index_2 =
GetFuzzerContext()->RandomIndex(available_constant_words);
} while (constant_index_1 == constant_index_2);
auto constant_id_1 = available_constants[constant_index_1];
auto constant_id_2 = available_constants[constant_index_2];
auto constant_id_1 = FindOrCreateConstant(
available_constant_words[constant_index_1], chosen_type_id);
auto constant_id_2 = FindOrCreateConstant(
available_constant_words[constant_index_2], chosen_type_id);
assert(constant_id_1 != 0 && constant_id_2 != 0 &&
"We should not find an available constant with an id of 0.");
@ -324,18 +355,39 @@ void FuzzerPassObfuscateConstants::ObfuscateScalarConstant(
}
// Choose a random available uniform known to be equal to the constant.
protobufs::UniformBufferElementDescriptor uniform_descriptor =
const auto& uniform_descriptor =
uniform_descriptors[GetFuzzerContext()->RandomIndex(uniform_descriptors)];
// Make sure the module has OpConstant instructions for each index used to
// access a uniform.
for (auto index : uniform_descriptor.index()) {
FindOrCreate32BitIntegerConstant(index, true);
}
// Make sure the module has OpTypePointer that points to the element type of
// the uniform.
const auto* uniform_variable_instr =
FindUniformVariable(uniform_descriptor, GetIRContext(), true);
assert(uniform_variable_instr &&
"Uniform variable does not exist or not unique.");
const auto* uniform_variable_type_intr =
GetIRContext()->get_def_use_mgr()->GetDef(
uniform_variable_instr->type_id());
assert(uniform_variable_type_intr && "Uniform variable has invalid type");
auto element_type_id = fuzzerutil::WalkCompositeTypeIndices(
GetIRContext(), uniform_variable_type_intr->GetSingleWordInOperand(1),
uniform_descriptor.index());
assert(element_type_id && "Type of uniform variable is invalid");
FindOrCreatePointerType(element_type_id, SpvStorageClassUniform);
// Create, apply and record a transformation to replace the constant use with
// the result of a load from the chosen uniform.
auto transformation = TransformationReplaceConstantWithUniform(
ApplyTransformation(TransformationReplaceConstantWithUniform(
constant_use, uniform_descriptor, GetFuzzerContext()->GetFreshId(),
GetFuzzerContext()->GetFreshId());
// Transformation should be applicable by construction.
assert(
transformation.IsApplicable(GetIRContext(), *GetTransformationContext()));
transformation.Apply(GetIRContext(), GetTransformationContext());
*GetTransformations()->add_transformation() = transformation.ToMessage();
GetFuzzerContext()->GetFreshId()));
}
void FuzzerPassObfuscateConstants::ObfuscateConstant(

View File

@ -99,6 +99,11 @@ class FuzzerPassObfuscateConstants : public FuzzerPass {
uint32_t base_instruction_result_id,
const std::map<SpvOp, uint32_t>& skipped_opcode_count,
std::vector<protobufs::IdUseDescriptor>* constant_uses);
// Returns a vector of unique words that denote constants. Every such constant
// is used in |FactConstantUniform| and has type with id equal to |type_id|.
std::vector<std::vector<uint32_t>> GetConstantWordsFromUniformsForType(
uint32_t type_id);
};
} // namespace fuzz

View File

@ -18,6 +18,7 @@
#include <vector>
#include "source/fuzz/protobufs/spirvfuzz_protobufs.h"
#include "source/fuzz/transformation_context.h"
#include "source/opt/basic_block.h"
#include "source/opt/instruction.h"
#include "source/opt/ir_context.h"

View File

@ -21,7 +21,6 @@ if (${SPIRV_BUILD_FUZZER})
equivalence_relation_test.cpp
fact_manager_test.cpp
fuzz_test_util.cpp
fuzzer_pass_add_useful_constructs_test.cpp
fuzzer_pass_construct_composites_test.cpp
fuzzer_pass_donate_modules_test.cpp
instruction_descriptor_test.cpp

View File

@ -1,401 +0,0 @@
// 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.
#include "source/fuzz/fuzzer_pass_add_useful_constructs.h"
#include "source/fuzz/pseudo_random_generator.h"
#include "source/fuzz/uniform_buffer_element_descriptor.h"
#include "test/fuzz/fuzz_test_util.h"
namespace spvtools {
namespace fuzz {
namespace {
bool AddFactHelper(
FactManager* fact_manager, opt::IRContext* context, uint32_t word,
const protobufs::UniformBufferElementDescriptor& descriptor) {
protobufs::FactConstantUniform constant_uniform_fact;
constant_uniform_fact.add_constant_word(word);
*constant_uniform_fact.mutable_uniform_buffer_element_descriptor() =
descriptor;
protobufs::Fact fact;
*fact.mutable_constant_uniform_fact() = constant_uniform_fact;
return fact_manager->AddFact(fact, context);
}
TEST(FuzzerPassAddUsefulConstructsTest, CheckBasicStuffIsAdded) {
// The SPIR-V came from the following empty GLSL shader:
//
// #version 450
//
// void main()
// {
// }
std::string shader = R"(
OpCapability Shader
%1 = OpExtInstImport "GLSL.std.450"
OpMemoryModel Logical GLSL450
OpEntryPoint Fragment %4 "main"
OpExecutionMode %4 OriginUpperLeft
OpSource GLSL 450
OpName %4 "main"
%2 = OpTypeVoid
%3 = OpTypeFunction %2
%4 = OpFunction %2 None %3
%5 = OpLabel
OpReturn
OpFunctionEnd
)";
const auto env = SPV_ENV_UNIVERSAL_1_3;
const auto consumer = nullptr;
const auto context = BuildModule(env, consumer, shader, kFuzzAssembleOption);
ASSERT_TRUE(IsValid(env, context.get()));
FactManager fact_manager;
spvtools::ValidatorOptions validator_options;
TransformationContext transformation_context(&fact_manager,
validator_options);
FuzzerContext fuzzer_context(MakeUnique<PseudoRandomGenerator>(0).get(), 100);
protobufs::TransformationSequence transformation_sequence;
FuzzerPassAddUsefulConstructs pass(context.get(), &transformation_context,
&fuzzer_context, &transformation_sequence);
pass.Apply();
ASSERT_TRUE(IsValid(env, context.get()));
std::string after = R"(
OpCapability Shader
%1 = OpExtInstImport "GLSL.std.450"
OpMemoryModel Logical GLSL450
OpEntryPoint Fragment %4 "main"
OpExecutionMode %4 OriginUpperLeft
OpSource GLSL 450
OpName %4 "main"
%2 = OpTypeVoid
%3 = OpTypeFunction %2
%100 = OpTypeBool
%101 = OpTypeInt 32 1
%102 = OpTypeInt 32 0
%103 = OpTypeFloat 32
%104 = OpConstantTrue %100
%105 = OpConstantFalse %100
%106 = OpConstant %101 0
%107 = OpConstant %101 1
%108 = OpConstant %102 0
%109 = OpConstant %102 1
%110 = OpConstant %103 0
%111 = OpConstant %103 1
%4 = OpFunction %2 None %3
%5 = OpLabel
OpReturn
OpFunctionEnd
)";
ASSERT_TRUE(IsEqual(env, after, context.get()));
}
TEST(FuzzerPassAddUsefulConstructsTest,
CheckTypesIndicesAndConstantsAddedForUniformFacts) {
// The SPIR-V came from the following GLSL shader:
//
// #version 450
//
// struct S {
// int x;
// float y;
// int z;
// int w;
// };
//
// uniform buf {
// S s;
// uint w[10];
// };
//
// void main() {
// }
std::string shader = R"(
OpCapability Shader
%1 = OpExtInstImport "GLSL.std.450"
OpMemoryModel Logical GLSL450
OpEntryPoint Fragment %4 "main"
OpExecutionMode %4 OriginUpperLeft
OpSource GLSL 450
OpName %4 "main"
OpName %8 "S"
OpMemberName %8 0 "x"
OpMemberName %8 1 "y"
OpMemberName %8 2 "z"
OpMemberName %8 3 "w"
OpName %12 "buf"
OpMemberName %12 0 "s"
OpMemberName %12 1 "w"
OpName %14 ""
OpMemberDecorate %8 0 Offset 0
OpMemberDecorate %8 1 Offset 4
OpMemberDecorate %8 2 Offset 8
OpMemberDecorate %8 3 Offset 12
OpDecorate %11 ArrayStride 16
OpMemberDecorate %12 0 Offset 0
OpMemberDecorate %12 1 Offset 16
OpDecorate %12 Block
OpDecorate %14 DescriptorSet 0
OpDecorate %14 Binding 0
%2 = OpTypeVoid
%3 = OpTypeFunction %2
%6 = OpTypeInt 32 1
%7 = OpTypeFloat 32
%8 = OpTypeStruct %6 %7 %6 %6
%9 = OpTypeInt 32 0
%10 = OpConstant %9 10
%11 = OpTypeArray %9 %10
%12 = OpTypeStruct %8 %11
%13 = OpTypePointer Uniform %12
%14 = OpVariable %13 Uniform
%4 = OpFunction %2 None %3
%5 = OpLabel
OpReturn
OpFunctionEnd
)";
const auto env = SPV_ENV_UNIVERSAL_1_3;
const auto consumer = nullptr;
const auto context = BuildModule(env, consumer, shader, kFuzzAssembleOption);
ASSERT_TRUE(IsValid(env, context.get()));
FactManager fact_manager;
spvtools::ValidatorOptions validator_options;
TransformationContext transformation_context(&fact_manager,
validator_options);
FuzzerContext fuzzer_context(MakeUnique<PseudoRandomGenerator>(0).get(), 100);
protobufs::TransformationSequence transformation_sequence;
// Add some uniform facts.
// buf.s.x == 200
ASSERT_TRUE(AddFactHelper(&fact_manager, context.get(), 200,
MakeUniformBufferElementDescriptor(0, 0, {0, 0})));
// buf.s.y == 0.5
const float float_value = 0.5;
uint32_t float_value_as_uint;
memcpy(&float_value_as_uint, &float_value, sizeof(float_value));
ASSERT_TRUE(AddFactHelper(&fact_manager, context.get(), float_value_as_uint,
MakeUniformBufferElementDescriptor(0, 0, {0, 1})));
// buf.s.z == 300
ASSERT_TRUE(AddFactHelper(&fact_manager, context.get(), 300,
MakeUniformBufferElementDescriptor(0, 0, {0, 2})));
// buf.s.w == 400
ASSERT_TRUE(AddFactHelper(&fact_manager, context.get(), 400,
MakeUniformBufferElementDescriptor(0, 0, {0, 3})));
// buf.w[6] = 22
ASSERT_TRUE(AddFactHelper(&fact_manager, context.get(), 22,
MakeUniformBufferElementDescriptor(0, 0, {1, 6})));
// buf.w[8] = 23
ASSERT_TRUE(AddFactHelper(&fact_manager, context.get(), 23,
MakeUniformBufferElementDescriptor(0, 0, {1, 8})));
// Assert some things about the module that are not true prior to adding the
// pass
{
// No uniform int pointer
opt::analysis::Integer temp_type_signed_int(32, true);
opt::analysis::Integer* registered_type_signed_int =
context->get_type_mgr()
->GetRegisteredType(&temp_type_signed_int)
->AsInteger();
opt::analysis::Pointer type_pointer_uniform_signed_int(
registered_type_signed_int, SpvStorageClassUniform);
ASSERT_EQ(0,
context->get_type_mgr()->GetId(&type_pointer_uniform_signed_int));
// No uniform uint pointer
opt::analysis::Integer temp_type_unsigned_int(32, false);
opt::analysis::Integer* registered_type_unsigned_int =
context->get_type_mgr()
->GetRegisteredType(&temp_type_unsigned_int)
->AsInteger();
opt::analysis::Pointer type_pointer_uniform_unsigned_int(
registered_type_unsigned_int, SpvStorageClassUniform);
ASSERT_EQ(
0, context->get_type_mgr()->GetId(&type_pointer_uniform_unsigned_int));
// No uniform float pointer
opt::analysis::Float temp_type_float(32);
opt::analysis::Float* registered_type_float =
context->get_type_mgr()->GetRegisteredType(&temp_type_float)->AsFloat();
opt::analysis::Pointer type_pointer_uniform_float(registered_type_float,
SpvStorageClassUniform);
ASSERT_EQ(0, context->get_type_mgr()->GetId(&type_pointer_uniform_float));
// No int constants 200, 300 nor 400
opt::analysis::IntConstant int_constant_200(registered_type_signed_int,
{200});
opt::analysis::IntConstant int_constant_300(registered_type_signed_int,
{300});
opt::analysis::IntConstant int_constant_400(registered_type_signed_int,
{400});
ASSERT_EQ(nullptr,
context->get_constant_mgr()->FindConstant(&int_constant_200));
ASSERT_EQ(nullptr,
context->get_constant_mgr()->FindConstant(&int_constant_300));
ASSERT_EQ(nullptr,
context->get_constant_mgr()->FindConstant(&int_constant_400));
// No float constant 0.5
opt::analysis::FloatConstant float_constant_zero_point_five(
registered_type_float, {float_value_as_uint});
ASSERT_EQ(nullptr, context->get_constant_mgr()->FindConstant(
&float_constant_zero_point_five));
// No uint constant 22
opt::analysis::IntConstant uint_constant_22(registered_type_unsigned_int,
{22});
ASSERT_EQ(nullptr,
context->get_constant_mgr()->FindConstant(&uint_constant_22));
// No uint constant 23
opt::analysis::IntConstant uint_constant_23(registered_type_unsigned_int,
{23});
ASSERT_EQ(nullptr,
context->get_constant_mgr()->FindConstant(&uint_constant_23));
// No int constants 0, 1, 2, 3, 6, 8
opt::analysis::IntConstant int_constant_0(registered_type_signed_int, {0});
opt::analysis::IntConstant int_constant_1(registered_type_signed_int, {1});
opt::analysis::IntConstant int_constant_2(registered_type_signed_int, {2});
opt::analysis::IntConstant int_constant_3(registered_type_signed_int, {3});
opt::analysis::IntConstant int_constant_6(registered_type_signed_int, {6});
opt::analysis::IntConstant int_constant_8(registered_type_signed_int, {8});
ASSERT_EQ(nullptr,
context->get_constant_mgr()->FindConstant(&int_constant_0));
ASSERT_EQ(nullptr,
context->get_constant_mgr()->FindConstant(&int_constant_1));
ASSERT_EQ(nullptr,
context->get_constant_mgr()->FindConstant(&int_constant_2));
ASSERT_EQ(nullptr,
context->get_constant_mgr()->FindConstant(&int_constant_3));
ASSERT_EQ(nullptr,
context->get_constant_mgr()->FindConstant(&int_constant_6));
ASSERT_EQ(nullptr,
context->get_constant_mgr()->FindConstant(&int_constant_8));
}
FuzzerPassAddUsefulConstructs pass(context.get(), &transformation_context,
&fuzzer_context, &transformation_sequence);
pass.Apply();
ASSERT_TRUE(IsValid(env, context.get()));
// Now assert some things about the module that should be true following the
// pass.
// We reconstruct all necessary types and constants to guard against the type
// and constant managers for the module having been invalidated.
{
// Uniform int pointer now present
opt::analysis::Integer temp_type_signed_int(32, true);
opt::analysis::Integer* registered_type_signed_int =
context->get_type_mgr()
->GetRegisteredType(&temp_type_signed_int)
->AsInteger();
opt::analysis::Pointer type_pointer_uniform_signed_int(
registered_type_signed_int, SpvStorageClassUniform);
ASSERT_NE(0,
context->get_type_mgr()->GetId(&type_pointer_uniform_signed_int));
// Uniform uint pointer now present
opt::analysis::Integer temp_type_unsigned_int(32, false);
opt::analysis::Integer* registered_type_unsigned_int =
context->get_type_mgr()
->GetRegisteredType(&temp_type_unsigned_int)
->AsInteger();
opt::analysis::Pointer type_pointer_uniform_unsigned_int(
registered_type_unsigned_int, SpvStorageClassUniform);
ASSERT_NE(
0, context->get_type_mgr()->GetId(&type_pointer_uniform_unsigned_int));
// Uniform float pointer now present
opt::analysis::Float temp_type_float(32);
opt::analysis::Float* registered_type_float =
context->get_type_mgr()->GetRegisteredType(&temp_type_float)->AsFloat();
opt::analysis::Pointer type_pointer_uniform_float(registered_type_float,
SpvStorageClassUniform);
ASSERT_NE(0, context->get_type_mgr()->GetId(&type_pointer_uniform_float));
// int constants 200, 300, 400 now present
opt::analysis::IntConstant int_constant_200(registered_type_signed_int,
{200});
opt::analysis::IntConstant int_constant_300(registered_type_signed_int,
{300});
opt::analysis::IntConstant int_constant_400(registered_type_signed_int,
{400});
ASSERT_NE(nullptr,
context->get_constant_mgr()->FindConstant(&int_constant_200));
ASSERT_NE(nullptr,
context->get_constant_mgr()->FindConstant(&int_constant_300));
ASSERT_NE(nullptr,
context->get_constant_mgr()->FindConstant(&int_constant_400));
// float constant 0.5 now present
opt::analysis::FloatConstant float_constant_zero_point_five(
registered_type_float, {float_value_as_uint});
ASSERT_NE(nullptr, context->get_constant_mgr()->FindConstant(
&float_constant_zero_point_five));
// uint constant 22 now present
opt::analysis::IntConstant uint_constant_22(registered_type_unsigned_int,
{22});
ASSERT_NE(nullptr,
context->get_constant_mgr()->FindConstant(&uint_constant_22));
// uint constant 23 now present
opt::analysis::IntConstant uint_constant_23(registered_type_unsigned_int,
{23});
ASSERT_NE(nullptr,
context->get_constant_mgr()->FindConstant(&uint_constant_23));
// int constants 0, 1, 2, 3, 6, 8 now present
opt::analysis::IntConstant int_constant_0(registered_type_signed_int, {0});
opt::analysis::IntConstant int_constant_1(registered_type_signed_int, {1});
opt::analysis::IntConstant int_constant_2(registered_type_signed_int, {2});
opt::analysis::IntConstant int_constant_3(registered_type_signed_int, {3});
opt::analysis::IntConstant int_constant_6(registered_type_signed_int, {6});
opt::analysis::IntConstant int_constant_8(registered_type_signed_int, {8});
ASSERT_NE(nullptr,
context->get_constant_mgr()->FindConstant(&int_constant_0));
ASSERT_NE(nullptr,
context->get_constant_mgr()->FindConstant(&int_constant_1));
ASSERT_NE(nullptr,
context->get_constant_mgr()->FindConstant(&int_constant_2));
ASSERT_NE(nullptr,
context->get_constant_mgr()->FindConstant(&int_constant_3));
ASSERT_NE(nullptr,
context->get_constant_mgr()->FindConstant(&int_constant_6));
ASSERT_NE(nullptr,
context->get_constant_mgr()->FindConstant(&int_constant_8));
}
}
} // namespace
} // namespace fuzz
} // namespace spvtools