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.
diff --git a/source/fuzz/fuzzer_pass.cpp b/source/fuzz/fuzzer_pass.cpp
index 4ab2946..dbe5143 100644
--- a/source/fuzz/fuzzer_pass.cpp
+++ b/source/fuzz/fuzzer_pass.cpp
@@ -14,6 +14,8 @@
#include "source/fuzz/fuzzer_pass.h"
+#include <set>
+
#include "source/fuzz/fuzzer_util.h"
#include "source/fuzz/instruction_descriptor.h"
#include "source/fuzz/transformation_add_constant_boolean.h"
@@ -329,43 +331,72 @@
}
std::pair<std::vector<uint32_t>, std::map<uint32_t, std::vector<uint32_t>>>
-FuzzerPass::GetAvailableBaseTypesAndPointers(
+FuzzerPass::GetAvailableBasicTypesAndPointers(
SpvStorageClass storage_class) const {
- // Records all of the base types available in the module.
- std::vector<uint32_t> base_types;
+ // Records all of the basic types available in the module.
+ std::set<uint32_t> 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<uint32_t, std::vector<uint32_t>> 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<uint32_t, std::vector<uint32_t>> 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 436fd77..94b8dfa 100644
--- a/source/fuzz/fuzzer_pass.h
+++ b/source/fuzz/fuzzer_pass.h
@@ -169,18 +169,21 @@
// 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::vector<uint32_t>, std::map<uint32_t, std::vector<uint32_t>>>
- 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 ce2b8eb..80708ed 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() = 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<uint32_t>& 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<uint32_t>& 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 ace9be2..661159e 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() = 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<uint32_t>& 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<uint32_t>& 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));
}
}
}