From bfd25ace0844b9bdf7134bb637c28d25f00f0278 Mon Sep 17 00:00:00 2001 From: Alastair Donaldson Date: Thu, 2 Apr 2020 17:35:18 +0100 Subject: [PATCH] spirv-fuzz: Limit adding of new variables to 'basic' types (#3257) To avoid problems where global and local variables of opaque or runtime-sized types are added to a module, this change introduces the notion of a 'basic type' -- a type made up from floats, ints, bools, or vectors, matrices, structs and fixed-size arrays of basic types. Added variables have to be of basic type. --- source/fuzz/fuzzer_pass.cpp | 71 +++++++++++++------ source/fuzz/fuzzer_pass.h | 21 +++--- .../fuzz/fuzzer_pass_add_global_variables.cpp | 42 +++++------ .../fuzz/fuzzer_pass_add_local_variables.cpp | 42 +++++------ 4 files changed, 105 insertions(+), 71 deletions(-) diff --git a/source/fuzz/fuzzer_pass.cpp b/source/fuzz/fuzzer_pass.cpp index 4ab29466d..dbe514338 100644 --- a/source/fuzz/fuzzer_pass.cpp +++ b/source/fuzz/fuzzer_pass.cpp @@ -14,6 +14,8 @@ #include "source/fuzz/fuzzer_pass.h" +#include + #include "source/fuzz/fuzzer_util.h" #include "source/fuzz/instruction_descriptor.h" #include "source/fuzz/transformation_add_constant_boolean.h" @@ -329,43 +331,72 @@ uint32_t FuzzerPass::FindOrCreateGlobalUndef(uint32_t type_id) { } std::pair, std::map>> -FuzzerPass::GetAvailableBaseTypesAndPointers( +FuzzerPass::GetAvailableBasicTypesAndPointers( SpvStorageClass storage_class) const { - // Records all of the base types available in the module. - std::vector base_types; + // Records all of the basic types available in the module. + std::set basic_types; - // For each base type, records all the associated pointer types that target - // that base type and that have |storage_class| as their storage class. - std::map> base_type_to_pointers; + // For each basic type, records all the associated pointer types that target + // the basic type and that have |storage_class| as their storage class. + std::map> basic_type_to_pointers; for (auto& inst : GetIRContext()->types_values()) { + // For each basic type that we come across, record type, and the fact that + // we cannot yet have seen any pointers that use the basic type as its + // pointee type. + // + // For pointer types with basic pointee types, associate the pointer type + // with the basic type. switch (inst.opcode()) { - case SpvOpTypeArray: case SpvOpTypeBool: case SpvOpTypeFloat: case SpvOpTypeInt: case SpvOpTypeMatrix: - case SpvOpTypeStruct: case SpvOpTypeVector: - // These types are suitable as pointer base types. Record the type, - // and the fact that we cannot yet have seen any pointers that use this - // as its base type. - base_types.push_back(inst.result_id()); - base_type_to_pointers.insert({inst.result_id(), {}}); + // These are all basic types. + basic_types.insert(inst.result_id()); + basic_type_to_pointers.insert({inst.result_id(), {}}); break; - case SpvOpTypePointer: - if (inst.GetSingleWordInOperand(0) == storage_class) { - // The pointer has the desired storage class, so we are interested in - // it. Associate it with its base type. - base_type_to_pointers.at(inst.GetSingleWordInOperand(1)) - .push_back(inst.result_id()); + case SpvOpTypeArray: + // An array type is basic if its base type is basic. + if (basic_types.count(inst.GetSingleWordInOperand(0))) { + basic_types.insert(inst.result_id()); + basic_type_to_pointers.insert({inst.result_id(), {}}); } break; + case SpvOpTypeStruct: { + // A struct type is basic if all of its members are basic. + bool all_members_are_basic_types = true; + for (uint32_t i = 0; i < inst.NumInOperands(); i++) { + if (!basic_types.count(inst.GetSingleWordInOperand(i))) { + all_members_are_basic_types = false; + break; + } + } + if (all_members_are_basic_types) { + basic_types.insert(inst.result_id()); + basic_type_to_pointers.insert({inst.result_id(), {}}); + } + break; + } + case SpvOpTypePointer: { + // We are interested in the pointer if its pointee type is basic and it + // has the right storage class. + auto pointee_type = inst.GetSingleWordInOperand(1); + if (inst.GetSingleWordInOperand(0) == storage_class && + basic_types.count(pointee_type)) { + // The pointer has the desired storage class, and its pointee type is + // a basic type, so we are interested in it. Associate it with its + // basic type. + basic_type_to_pointers.at(pointee_type).push_back(inst.result_id()); + } + break; + } default: break; } } - return {base_types, base_type_to_pointers}; + return {{basic_types.begin(), basic_types.end()}, basic_type_to_pointers}; } uint32_t FuzzerPass::FindOrCreateZeroConstant( diff --git a/source/fuzz/fuzzer_pass.h b/source/fuzz/fuzzer_pass.h index 436fd7785..94b8dfa44 100644 --- a/source/fuzz/fuzzer_pass.h +++ b/source/fuzz/fuzzer_pass.h @@ -169,18 +169,21 @@ class FuzzerPass { // If no such instruction exists, a transformation is applied to add it. uint32_t FindOrCreateGlobalUndef(uint32_t type_id); - // Yields a pair, (base_type_ids, base_type_ids_to_pointers), such that: - // - base_type_ids captures every scalar or composite type declared in the - // module (i.e., all int, bool, float, vector, matrix, struct and array - // types - // - base_type_ids_to_pointers maps every such base type to the sequence + // Define a *basic type* to be an integer, boolean or floating-point type, + // or a matrix, vector, struct or fixed-size array built from basic types. In + // particular, a basic type cannot contain an opaque type (such as an image), + // or a runtime-sized array. + // + // Yields a pair, (basic_type_ids, basic_type_ids_to_pointers), such that: + // - basic_type_ids captures every basic type declared in the module. + // - basic_type_ids_to_pointers maps every such basic type to the sequence // of all pointer types that have storage class |storage_class| and the - // given base type as their pointee type. The sequence may be empty for - // some base types if no pointers to those types are defined for the given + // given basic type as their pointee type. The sequence may be empty for + // some basic types if no pointers to those types are defined for the given // storage class, and the sequence will have multiple elements if there are - // repeated pointer declarations for the same base type and storage class. + // repeated pointer declarations for the same basic type and storage class. std::pair, std::map>> - GetAvailableBaseTypesAndPointers(SpvStorageClass storage_class) const; + GetAvailableBasicTypesAndPointers(SpvStorageClass storage_class) const; // Given a type id, |scalar_or_composite_type_id|, which must correspond to // some scalar or composite type, returns the result id of an instruction diff --git a/source/fuzz/fuzzer_pass_add_global_variables.cpp b/source/fuzz/fuzzer_pass_add_global_variables.cpp index ce2b8eb1b..80708edfb 100644 --- a/source/fuzz/fuzzer_pass_add_global_variables.cpp +++ b/source/fuzz/fuzzer_pass_add_global_variables.cpp @@ -30,45 +30,45 @@ FuzzerPassAddGlobalVariables::FuzzerPassAddGlobalVariables( FuzzerPassAddGlobalVariables::~FuzzerPassAddGlobalVariables() = default; void FuzzerPassAddGlobalVariables::Apply() { - auto base_type_ids_and_pointers = - GetAvailableBaseTypesAndPointers(SpvStorageClassPrivate); + auto basic_type_ids_and_pointers = + GetAvailableBasicTypesAndPointers(SpvStorageClassPrivate); - // These are the base types that are available to this fuzzer pass. - auto& base_types = base_type_ids_and_pointers.first; + // These are the basic types that are available to this fuzzer pass. + auto& basic_types = basic_type_ids_and_pointers.first; - // These are the pointers to those base types that are *initially* available + // These are the pointers to those basic types that are *initially* available // to the fuzzer pass. The fuzzer pass might add pointer types in cases where - // none are available for a given base type. - auto& base_type_to_pointers = base_type_ids_and_pointers.second; + // none are available for a given basic type. + auto& basic_type_to_pointers = basic_type_ids_and_pointers.second; // Probabilistically keep adding global variables. while (GetFuzzerContext()->ChoosePercentage( GetFuzzerContext()->GetChanceOfAddingGlobalVariable())) { - // Choose a random base type; the new variable's type will be a pointer to - // this base type. - uint32_t base_type = - base_types[GetFuzzerContext()->RandomIndex(base_types)]; + // Choose a random basic type; the new variable's type will be a pointer to + // this basic type. + uint32_t basic_type = + basic_types[GetFuzzerContext()->RandomIndex(basic_types)]; uint32_t pointer_type_id; - std::vector& available_pointers_to_base_type = - base_type_to_pointers.at(base_type); - // Determine whether there is at least one pointer to this base type. - if (available_pointers_to_base_type.empty()) { + std::vector& available_pointers_to_basic_type = + basic_type_to_pointers.at(basic_type); + // Determine whether there is at least one pointer to this basic type. + if (available_pointers_to_basic_type.empty()) { // There is not. Make one, to use here, and add it to the available - // pointers for the base type so that future variables can potentially + // pointers for the basic type so that future variables can potentially // use it. pointer_type_id = GetFuzzerContext()->GetFreshId(); - available_pointers_to_base_type.push_back(pointer_type_id); + available_pointers_to_basic_type.push_back(pointer_type_id); ApplyTransformation(TransformationAddTypePointer( - pointer_type_id, SpvStorageClassPrivate, base_type)); + pointer_type_id, SpvStorageClassPrivate, basic_type)); } else { // There is - grab one. pointer_type_id = - available_pointers_to_base_type[GetFuzzerContext()->RandomIndex( - available_pointers_to_base_type)]; + available_pointers_to_basic_type[GetFuzzerContext()->RandomIndex( + available_pointers_to_basic_type)]; } ApplyTransformation(TransformationAddGlobalVariable( GetFuzzerContext()->GetFreshId(), pointer_type_id, - FindOrCreateZeroConstant(base_type), true)); + FindOrCreateZeroConstant(basic_type), true)); } } diff --git a/source/fuzz/fuzzer_pass_add_local_variables.cpp b/source/fuzz/fuzzer_pass_add_local_variables.cpp index ace9be266..661159e65 100644 --- a/source/fuzz/fuzzer_pass_add_local_variables.cpp +++ b/source/fuzz/fuzzer_pass_add_local_variables.cpp @@ -31,47 +31,47 @@ FuzzerPassAddLocalVariables::FuzzerPassAddLocalVariables( FuzzerPassAddLocalVariables::~FuzzerPassAddLocalVariables() = default; void FuzzerPassAddLocalVariables::Apply() { - auto base_type_ids_and_pointers = - GetAvailableBaseTypesAndPointers(SpvStorageClassFunction); + auto basic_type_ids_and_pointers = + GetAvailableBasicTypesAndPointers(SpvStorageClassFunction); - // These are the base types that are available to this fuzzer pass. - auto& base_types = base_type_ids_and_pointers.first; + // These are the basic types that are available to this fuzzer pass. + auto& basic_types = basic_type_ids_and_pointers.first; - // These are the pointers to those base types that are *initially* available + // These are the pointers to those basic types that are *initially* available // to the fuzzer pass. The fuzzer pass might add pointer types in cases where - // none are available for a given base type. - auto& base_type_to_pointers = base_type_ids_and_pointers.second; + // none are available for a given basic type. + auto& basic_type_to_pointers = basic_type_ids_and_pointers.second; // Consider every function in the module. for (auto& function : *GetIRContext()->module()) { // Probabilistically keep adding random variables to this function. while (GetFuzzerContext()->ChoosePercentage( GetFuzzerContext()->GetChanceOfAddingLocalVariable())) { - // Choose a random base type; the new variable's type will be a pointer to - // this base type. - uint32_t base_type = - base_types[GetFuzzerContext()->RandomIndex(base_types)]; + // Choose a random basic type; the new variable's type will be a pointer + // to this basic type. + uint32_t basic_type = + basic_types[GetFuzzerContext()->RandomIndex(basic_types)]; uint32_t pointer_type; - std::vector& available_pointers_to_base_type = - base_type_to_pointers.at(base_type); - // Determine whether there is at least one pointer to this base type. - if (available_pointers_to_base_type.empty()) { + std::vector& available_pointers_to_basic_type = + basic_type_to_pointers.at(basic_type); + // Determine whether there is at least one pointer to this basic type. + if (available_pointers_to_basic_type.empty()) { // There is not. Make one, to use here, and add it to the available - // pointers for the base type so that future variables can potentially + // pointers for the basic type so that future variables can potentially // use it. pointer_type = GetFuzzerContext()->GetFreshId(); ApplyTransformation(TransformationAddTypePointer( - pointer_type, SpvStorageClassFunction, base_type)); - available_pointers_to_base_type.push_back(pointer_type); + pointer_type, SpvStorageClassFunction, basic_type)); + available_pointers_to_basic_type.push_back(pointer_type); } else { // There is - grab one. pointer_type = - available_pointers_to_base_type[GetFuzzerContext()->RandomIndex( - available_pointers_to_base_type)]; + available_pointers_to_basic_type[GetFuzzerContext()->RandomIndex( + available_pointers_to_basic_type)]; } ApplyTransformation(TransformationAddLocalVariable( GetFuzzerContext()->GetFreshId(), pointer_type, function.result_id(), - FindOrCreateZeroConstant(base_type), true)); + FindOrCreateZeroConstant(basic_type), true)); } } }