mirror of
https://github.com/KhronosGroup/SPIRV-Tools
synced 2024-11-26 21:30:07 +00:00
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.
This commit is contained in:
parent
f28cdeff16
commit
bfd25ace08
@ -14,6 +14,8 @@
|
|||||||
|
|
||||||
#include "source/fuzz/fuzzer_pass.h"
|
#include "source/fuzz/fuzzer_pass.h"
|
||||||
|
|
||||||
|
#include <set>
|
||||||
|
|
||||||
#include "source/fuzz/fuzzer_util.h"
|
#include "source/fuzz/fuzzer_util.h"
|
||||||
#include "source/fuzz/instruction_descriptor.h"
|
#include "source/fuzz/instruction_descriptor.h"
|
||||||
#include "source/fuzz/transformation_add_constant_boolean.h"
|
#include "source/fuzz/transformation_add_constant_boolean.h"
|
||||||
@ -329,43 +331,72 @@ uint32_t FuzzerPass::FindOrCreateGlobalUndef(uint32_t type_id) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
std::pair<std::vector<uint32_t>, std::map<uint32_t, std::vector<uint32_t>>>
|
std::pair<std::vector<uint32_t>, std::map<uint32_t, std::vector<uint32_t>>>
|
||||||
FuzzerPass::GetAvailableBaseTypesAndPointers(
|
FuzzerPass::GetAvailableBasicTypesAndPointers(
|
||||||
SpvStorageClass storage_class) const {
|
SpvStorageClass storage_class) const {
|
||||||
// Records all of the base types available in the module.
|
// Records all of the basic types available in the module.
|
||||||
std::vector<uint32_t> base_types;
|
std::set<uint32_t> basic_types;
|
||||||
|
|
||||||
// For each base type, records all the associated pointer types that target
|
// For each basic type, records all the associated pointer types that target
|
||||||
// that base type and that have |storage_class| as their storage class.
|
// the basic type and that have |storage_class| as their storage class.
|
||||||
std::map<uint32_t, std::vector<uint32_t>> base_type_to_pointers;
|
std::map<uint32_t, std::vector<uint32_t>> basic_type_to_pointers;
|
||||||
|
|
||||||
for (auto& inst : GetIRContext()->types_values()) {
|
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()) {
|
switch (inst.opcode()) {
|
||||||
case SpvOpTypeArray:
|
|
||||||
case SpvOpTypeBool:
|
case SpvOpTypeBool:
|
||||||
case SpvOpTypeFloat:
|
case SpvOpTypeFloat:
|
||||||
case SpvOpTypeInt:
|
case SpvOpTypeInt:
|
||||||
case SpvOpTypeMatrix:
|
case SpvOpTypeMatrix:
|
||||||
case SpvOpTypeStruct:
|
|
||||||
case SpvOpTypeVector:
|
case SpvOpTypeVector:
|
||||||
// These types are suitable as pointer base types. Record the type,
|
// These are all basic types.
|
||||||
// and the fact that we cannot yet have seen any pointers that use this
|
basic_types.insert(inst.result_id());
|
||||||
// as its base type.
|
basic_type_to_pointers.insert({inst.result_id(), {}});
|
||||||
base_types.push_back(inst.result_id());
|
|
||||||
base_type_to_pointers.insert({inst.result_id(), {}});
|
|
||||||
break;
|
break;
|
||||||
case SpvOpTypePointer:
|
case SpvOpTypeArray:
|
||||||
if (inst.GetSingleWordInOperand(0) == storage_class) {
|
// An array type is basic if its base type is basic.
|
||||||
// The pointer has the desired storage class, so we are interested in
|
if (basic_types.count(inst.GetSingleWordInOperand(0))) {
|
||||||
// it. Associate it with its base type.
|
basic_types.insert(inst.result_id());
|
||||||
base_type_to_pointers.at(inst.GetSingleWordInOperand(1))
|
basic_type_to_pointers.insert({inst.result_id(), {}});
|
||||||
.push_back(inst.result_id());
|
|
||||||
}
|
}
|
||||||
break;
|
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:
|
default:
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return {base_types, base_type_to_pointers};
|
return {{basic_types.begin(), basic_types.end()}, basic_type_to_pointers};
|
||||||
}
|
}
|
||||||
|
|
||||||
uint32_t FuzzerPass::FindOrCreateZeroConstant(
|
uint32_t FuzzerPass::FindOrCreateZeroConstant(
|
||||||
|
@ -169,18 +169,21 @@ class FuzzerPass {
|
|||||||
// If no such instruction exists, a transformation is applied to add it.
|
// If no such instruction exists, a transformation is applied to add it.
|
||||||
uint32_t FindOrCreateGlobalUndef(uint32_t type_id);
|
uint32_t FindOrCreateGlobalUndef(uint32_t type_id);
|
||||||
|
|
||||||
// Yields a pair, (base_type_ids, base_type_ids_to_pointers), such that:
|
// Define a *basic type* to be an integer, boolean or floating-point type,
|
||||||
// - base_type_ids captures every scalar or composite type declared in the
|
// or a matrix, vector, struct or fixed-size array built from basic types. In
|
||||||
// module (i.e., all int, bool, float, vector, matrix, struct and array
|
// particular, a basic type cannot contain an opaque type (such as an image),
|
||||||
// types
|
// or a runtime-sized array.
|
||||||
// - base_type_ids_to_pointers maps every such base type to the sequence
|
//
|
||||||
|
// 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
|
// 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
|
// given basic 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
|
// 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
|
// 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>>>
|
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
|
// 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
|
// some scalar or composite type, returns the result id of an instruction
|
||||||
|
@ -30,45 +30,45 @@ FuzzerPassAddGlobalVariables::FuzzerPassAddGlobalVariables(
|
|||||||
FuzzerPassAddGlobalVariables::~FuzzerPassAddGlobalVariables() = default;
|
FuzzerPassAddGlobalVariables::~FuzzerPassAddGlobalVariables() = default;
|
||||||
|
|
||||||
void FuzzerPassAddGlobalVariables::Apply() {
|
void FuzzerPassAddGlobalVariables::Apply() {
|
||||||
auto base_type_ids_and_pointers =
|
auto basic_type_ids_and_pointers =
|
||||||
GetAvailableBaseTypesAndPointers(SpvStorageClassPrivate);
|
GetAvailableBasicTypesAndPointers(SpvStorageClassPrivate);
|
||||||
|
|
||||||
// These are the base types that are available to this fuzzer pass.
|
// These are the basic types that are available to this fuzzer pass.
|
||||||
auto& base_types = base_type_ids_and_pointers.first;
|
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
|
// to the fuzzer pass. The fuzzer pass might add pointer types in cases where
|
||||||
// none are available for a given base type.
|
// none are available for a given basic type.
|
||||||
auto& base_type_to_pointers = base_type_ids_and_pointers.second;
|
auto& basic_type_to_pointers = basic_type_ids_and_pointers.second;
|
||||||
|
|
||||||
// Probabilistically keep adding global variables.
|
// Probabilistically keep adding global variables.
|
||||||
while (GetFuzzerContext()->ChoosePercentage(
|
while (GetFuzzerContext()->ChoosePercentage(
|
||||||
GetFuzzerContext()->GetChanceOfAddingGlobalVariable())) {
|
GetFuzzerContext()->GetChanceOfAddingGlobalVariable())) {
|
||||||
// Choose a random base type; the new variable's type will be a pointer to
|
// Choose a random basic type; the new variable's type will be a pointer to
|
||||||
// this base type.
|
// this basic type.
|
||||||
uint32_t base_type =
|
uint32_t basic_type =
|
||||||
base_types[GetFuzzerContext()->RandomIndex(base_types)];
|
basic_types[GetFuzzerContext()->RandomIndex(basic_types)];
|
||||||
uint32_t pointer_type_id;
|
uint32_t pointer_type_id;
|
||||||
std::vector<uint32_t>& available_pointers_to_base_type =
|
std::vector<uint32_t>& available_pointers_to_basic_type =
|
||||||
base_type_to_pointers.at(base_type);
|
basic_type_to_pointers.at(basic_type);
|
||||||
// Determine whether there is at least one pointer to this base type.
|
// Determine whether there is at least one pointer to this basic type.
|
||||||
if (available_pointers_to_base_type.empty()) {
|
if (available_pointers_to_basic_type.empty()) {
|
||||||
// There is not. Make one, to use here, and add it to the available
|
// 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.
|
// use it.
|
||||||
pointer_type_id = GetFuzzerContext()->GetFreshId();
|
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(
|
ApplyTransformation(TransformationAddTypePointer(
|
||||||
pointer_type_id, SpvStorageClassPrivate, base_type));
|
pointer_type_id, SpvStorageClassPrivate, basic_type));
|
||||||
} else {
|
} else {
|
||||||
// There is - grab one.
|
// There is - grab one.
|
||||||
pointer_type_id =
|
pointer_type_id =
|
||||||
available_pointers_to_base_type[GetFuzzerContext()->RandomIndex(
|
available_pointers_to_basic_type[GetFuzzerContext()->RandomIndex(
|
||||||
available_pointers_to_base_type)];
|
available_pointers_to_basic_type)];
|
||||||
}
|
}
|
||||||
ApplyTransformation(TransformationAddGlobalVariable(
|
ApplyTransformation(TransformationAddGlobalVariable(
|
||||||
GetFuzzerContext()->GetFreshId(), pointer_type_id,
|
GetFuzzerContext()->GetFreshId(), pointer_type_id,
|
||||||
FindOrCreateZeroConstant(base_type), true));
|
FindOrCreateZeroConstant(basic_type), true));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -31,47 +31,47 @@ FuzzerPassAddLocalVariables::FuzzerPassAddLocalVariables(
|
|||||||
FuzzerPassAddLocalVariables::~FuzzerPassAddLocalVariables() = default;
|
FuzzerPassAddLocalVariables::~FuzzerPassAddLocalVariables() = default;
|
||||||
|
|
||||||
void FuzzerPassAddLocalVariables::Apply() {
|
void FuzzerPassAddLocalVariables::Apply() {
|
||||||
auto base_type_ids_and_pointers =
|
auto basic_type_ids_and_pointers =
|
||||||
GetAvailableBaseTypesAndPointers(SpvStorageClassFunction);
|
GetAvailableBasicTypesAndPointers(SpvStorageClassFunction);
|
||||||
|
|
||||||
// These are the base types that are available to this fuzzer pass.
|
// These are the basic types that are available to this fuzzer pass.
|
||||||
auto& base_types = base_type_ids_and_pointers.first;
|
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
|
// to the fuzzer pass. The fuzzer pass might add pointer types in cases where
|
||||||
// none are available for a given base type.
|
// none are available for a given basic type.
|
||||||
auto& base_type_to_pointers = base_type_ids_and_pointers.second;
|
auto& basic_type_to_pointers = basic_type_ids_and_pointers.second;
|
||||||
|
|
||||||
// Consider every function in the module.
|
// Consider every function in the module.
|
||||||
for (auto& function : *GetIRContext()->module()) {
|
for (auto& function : *GetIRContext()->module()) {
|
||||||
// Probabilistically keep adding random variables to this function.
|
// Probabilistically keep adding random variables to this function.
|
||||||
while (GetFuzzerContext()->ChoosePercentage(
|
while (GetFuzzerContext()->ChoosePercentage(
|
||||||
GetFuzzerContext()->GetChanceOfAddingLocalVariable())) {
|
GetFuzzerContext()->GetChanceOfAddingLocalVariable())) {
|
||||||
// Choose a random base type; the new variable's type will be a pointer to
|
// Choose a random basic type; the new variable's type will be a pointer
|
||||||
// this base type.
|
// to this basic type.
|
||||||
uint32_t base_type =
|
uint32_t basic_type =
|
||||||
base_types[GetFuzzerContext()->RandomIndex(base_types)];
|
basic_types[GetFuzzerContext()->RandomIndex(basic_types)];
|
||||||
uint32_t pointer_type;
|
uint32_t pointer_type;
|
||||||
std::vector<uint32_t>& available_pointers_to_base_type =
|
std::vector<uint32_t>& available_pointers_to_basic_type =
|
||||||
base_type_to_pointers.at(base_type);
|
basic_type_to_pointers.at(basic_type);
|
||||||
// Determine whether there is at least one pointer to this base type.
|
// Determine whether there is at least one pointer to this basic type.
|
||||||
if (available_pointers_to_base_type.empty()) {
|
if (available_pointers_to_basic_type.empty()) {
|
||||||
// There is not. Make one, to use here, and add it to the available
|
// 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.
|
// use it.
|
||||||
pointer_type = GetFuzzerContext()->GetFreshId();
|
pointer_type = GetFuzzerContext()->GetFreshId();
|
||||||
ApplyTransformation(TransformationAddTypePointer(
|
ApplyTransformation(TransformationAddTypePointer(
|
||||||
pointer_type, SpvStorageClassFunction, base_type));
|
pointer_type, SpvStorageClassFunction, basic_type));
|
||||||
available_pointers_to_base_type.push_back(pointer_type);
|
available_pointers_to_basic_type.push_back(pointer_type);
|
||||||
} else {
|
} else {
|
||||||
// There is - grab one.
|
// There is - grab one.
|
||||||
pointer_type =
|
pointer_type =
|
||||||
available_pointers_to_base_type[GetFuzzerContext()->RandomIndex(
|
available_pointers_to_basic_type[GetFuzzerContext()->RandomIndex(
|
||||||
available_pointers_to_base_type)];
|
available_pointers_to_basic_type)];
|
||||||
}
|
}
|
||||||
ApplyTransformation(TransformationAddLocalVariable(
|
ApplyTransformation(TransformationAddLocalVariable(
|
||||||
GetFuzzerContext()->GetFreshId(), pointer_type, function.result_id(),
|
GetFuzzerContext()->GetFreshId(), pointer_type, function.result_id(),
|
||||||
FindOrCreateZeroConstant(base_type), true));
|
FindOrCreateZeroConstant(basic_type), true));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user