From 2f69ea849aaa0551af77c599239a68342389f86e Mon Sep 17 00:00:00 2001 From: Vasyl Teliman Date: Tue, 19 May 2020 17:54:55 +0300 Subject: [PATCH] spirv-fuzz: Remove FuzzerPassAddUsefulConstructs (#3341) Fixes #3318. --- source/fuzz/CMakeLists.txt | 2 - source/fuzz/fuzzer.cpp | 11 - source/fuzz/fuzzer_pass.cpp | 29 ++ source/fuzz/fuzzer_pass.h | 8 + source/fuzz/fuzzer_pass_add_dead_blocks.cpp | 9 +- source/fuzz/fuzzer_pass_add_dead_breaks.cpp | 8 +- .../fuzz/fuzzer_pass_add_dead_continues.cpp | 7 +- .../fuzzer_pass_add_useful_constructs.cpp | 222 ---------- .../fuzz/fuzzer_pass_add_useful_constructs.h | 46 -- .../fuzz/fuzzer_pass_obfuscate_constants.cpp | 86 +++- source/fuzz/fuzzer_pass_obfuscate_constants.h | 5 + source/fuzz/fuzzer_util.h | 1 + test/fuzz/CMakeLists.txt | 1 - ...fuzzer_pass_add_useful_constructs_test.cpp | 401 ------------------ 14 files changed, 131 insertions(+), 705 deletions(-) delete mode 100644 source/fuzz/fuzzer_pass_add_useful_constructs.cpp delete mode 100644 source/fuzz/fuzzer_pass_add_useful_constructs.h delete mode 100644 test/fuzz/fuzzer_pass_add_useful_constructs_test.cpp diff --git a/source/fuzz/CMakeLists.txt b/source/fuzz/CMakeLists.txt index 0d4e4ea87..658292749 100644 --- a/source/fuzz/CMakeLists.txt +++ b/source/fuzz/CMakeLists.txt @@ -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 diff --git a/source/fuzz/fuzzer.cpp b/source/fuzz/fuzzer.cpp index d0732541e..3343abc7a 100644 --- a/source/fuzz/fuzzer.cpp +++ b/source/fuzz/fuzzer.cpp @@ -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> passes; while (passes.empty()) { diff --git a/source/fuzz/fuzzer_pass.cpp b/source/fuzz/fuzzer_pass.cpp index dbe514338..cd94e4e92 100644 --- a/source/fuzz/fuzzer_pass.cpp +++ b/source/fuzz/fuzzer_pass.cpp @@ -319,6 +319,35 @@ uint32_t FuzzerPass::FindOrCreateBoolConstant(bool value) { return result; } +uint32_t FuzzerPass::FindOrCreateConstant(const std::vector& 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) { diff --git a/source/fuzz/fuzzer_pass.h b/source/fuzz/fuzzer_pass.h index 67f0f46be..800b88861 100644 --- a/source/fuzz/fuzzer_pass.h +++ b/source/fuzz/fuzzer_pass.h @@ -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& 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. diff --git a/source/fuzz/fuzzer_pass_add_dead_blocks.cpp b/source/fuzz/fuzzer_pass_add_dead_blocks.cpp index 4e9db1f36..30a414544 100644 --- a/source/fuzz/fuzzer_pass_add_dead_blocks.cpp +++ b/source/fuzz/fuzzer_pass_add_dead_blocks.cpp @@ -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. diff --git a/source/fuzz/fuzzer_pass_add_dead_breaks.cpp b/source/fuzz/fuzzer_pass_add_dead_breaks.cpp index 6b171dcd8..f3900aa69 100644 --- a/source/fuzz/fuzzer_pass_add_dead_breaks.cpp +++ b/source/fuzz/fuzzer_pass_add_dead_breaks.cpp @@ -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. diff --git a/source/fuzz/fuzzer_pass_add_dead_continues.cpp b/source/fuzz/fuzzer_pass_add_dead_continues.cpp index b8f07fbe4..56a7fd17c 100644 --- a/source/fuzz/fuzzer_pass_add_dead_continues.cpp +++ b/source/fuzz/fuzzer_pass_add_dead_continues.cpp @@ -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(), diff --git a/source/fuzz/fuzzer_pass_add_useful_constructs.cpp b/source/fuzz/fuzzer_pass_add_useful_constructs.cpp deleted file mode 100644 index a267612bb..000000000 --- a/source/fuzz/fuzzer_pass_add_useful_constructs.cpp +++ /dev/null @@ -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 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 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 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 diff --git a/source/fuzz/fuzzer_pass_add_useful_constructs.h b/source/fuzz/fuzzer_pass_add_useful_constructs.h deleted file mode 100644 index 17e87a82e..000000000 --- a/source/fuzz/fuzzer_pass_add_useful_constructs.h +++ /dev/null @@ -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 data) const; - - void MaybeAddFloatConstant(uint32_t width, std::vector data) const; -}; - -} // namespace fuzz -} // namespace spvtools - -#endif // SOURCE_FUZZ_FUZZER_PASS_ADD_USEFUL_CONSTRUCTS_ diff --git a/source/fuzz/fuzzer_pass_obfuscate_constants.cpp b/source/fuzz/fuzzer_pass_obfuscate_constants.cpp index 4ced507fc..543c0d740 100644 --- a/source/fuzz/fuzzer_pass_obfuscate_constants.cpp +++ b/source/fuzz/fuzzer_pass_obfuscate_constants.cpp @@ -14,11 +14,14 @@ #include "source/fuzz/fuzzer_pass_obfuscate_constants.h" +#include #include +#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> +FuzzerPassObfuscateConstants::GetConstantWordsFromUniformsForType( + uint32_t type_id) { + assert(type_id && "Type id can't be 0"); + std::vector> result; + + for (const auto& facts_and_types : GetTransformationContext() + ->GetFactManager() + ->GetConstantUniformFactsAndTypes()) { + if (facts_and_types.second != type_id) { + continue; + } + + std::vector 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( diff --git a/source/fuzz/fuzzer_pass_obfuscate_constants.h b/source/fuzz/fuzzer_pass_obfuscate_constants.h index 7755d211a..52d8efe59 100644 --- a/source/fuzz/fuzzer_pass_obfuscate_constants.h +++ b/source/fuzz/fuzzer_pass_obfuscate_constants.h @@ -99,6 +99,11 @@ class FuzzerPassObfuscateConstants : public FuzzerPass { uint32_t base_instruction_result_id, const std::map& skipped_opcode_count, std::vector* 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> GetConstantWordsFromUniformsForType( + uint32_t type_id); }; } // namespace fuzz diff --git a/source/fuzz/fuzzer_util.h b/source/fuzz/fuzzer_util.h index 886029ac3..bccd1d015 100644 --- a/source/fuzz/fuzzer_util.h +++ b/source/fuzz/fuzzer_util.h @@ -18,6 +18,7 @@ #include #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" diff --git a/test/fuzz/CMakeLists.txt b/test/fuzz/CMakeLists.txt index afa168d3f..dca142aae 100644 --- a/test/fuzz/CMakeLists.txt +++ b/test/fuzz/CMakeLists.txt @@ -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 diff --git a/test/fuzz/fuzzer_pass_add_useful_constructs_test.cpp b/test/fuzz/fuzzer_pass_add_useful_constructs_test.cpp deleted file mode 100644 index 90d1c8472..000000000 --- a/test/fuzz/fuzzer_pass_add_useful_constructs_test.cpp +++ /dev/null @@ -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(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(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