mirror of
https://github.com/KhronosGroup/SPIRV-Tools
synced 2024-11-26 21:30:07 +00:00
001e823b65
Adds a new transformation that can replace a constant with a uniform known to have the same value, and adds a fuzzer pass that (a) replaces a boolean with a comparison of literals (e.g. replacing "true" with "42 > 24"), and then (b) obfuscates the literals appearing in this comparison by replacing them with identically-valued uniforms, if available. The fuzzer_replayer test file has also been updated to allow initial facts to be provided, and to do error checking of the status results returned by the fuzzer and replayer components.
233 lines
10 KiB
C++
233 lines
10 KiB
C++
// 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 {
|
|
|
|
using opt::IRContext;
|
|
|
|
FuzzerPassAddUsefulConstructs::FuzzerPassAddUsefulConstructs(
|
|
opt::IRContext* ir_context, FactManager* fact_manager,
|
|
FuzzerContext* fuzzer_context,
|
|
protobufs::TransformationSequence* transformations)
|
|
: FuzzerPass(ir_context, fact_manager, 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)) {
|
|
protobufs::TransformationAddConstantScalar add_constant_int =
|
|
transformation::MakeTransformationAddConstantScalar(
|
|
GetFuzzerContext()->GetFreshId(), int_type_id, data);
|
|
assert(transformation::IsApplicable(add_constant_int, GetIRContext(),
|
|
*GetFactManager()) &&
|
|
"Should be applicable by construction.");
|
|
transformation::Apply(add_constant_int, GetIRContext(), GetFactManager());
|
|
*GetTransformations()->add_transformation()->mutable_add_constant_scalar() =
|
|
add_constant_int;
|
|
}
|
|
}
|
|
|
|
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)) {
|
|
protobufs::TransformationAddConstantScalar add_constant_float =
|
|
transformation::MakeTransformationAddConstantScalar(
|
|
GetFuzzerContext()->GetFreshId(), float_type_id, data);
|
|
assert(transformation::IsApplicable(add_constant_float, GetIRContext(),
|
|
*GetFactManager()) &&
|
|
"Should be applicable by construction.");
|
|
transformation::Apply(add_constant_float, GetIRContext(), GetFactManager());
|
|
*GetTransformations()->add_transformation()->mutable_add_constant_scalar() =
|
|
add_constant_float;
|
|
}
|
|
}
|
|
|
|
void FuzzerPassAddUsefulConstructs::Apply() {
|
|
{
|
|
// Add boolean type if not present.
|
|
opt::analysis::Bool temp_bool_type;
|
|
if (!GetIRContext()->get_type_mgr()->GetId(&temp_bool_type)) {
|
|
protobufs::TransformationAddTypeBoolean add_type_boolean =
|
|
transformation::MakeTransformationAddTypeBoolean(
|
|
GetFuzzerContext()->GetFreshId());
|
|
assert(transformation::IsApplicable(add_type_boolean, GetIRContext(),
|
|
*GetFactManager()) &&
|
|
"Should be applicable by construction.");
|
|
transformation::Apply(add_type_boolean, GetIRContext(), GetFactManager());
|
|
*GetTransformations()->add_transformation()->mutable_add_type_boolean() =
|
|
add_type_boolean;
|
|
}
|
|
}
|
|
|
|
{
|
|
// 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)) {
|
|
protobufs::TransformationAddTypeInt add_type_int =
|
|
transformation::MakeTransformationAddTypeInt(
|
|
GetFuzzerContext()->GetFreshId(), 32, is_signed);
|
|
assert(transformation::IsApplicable(add_type_int, GetIRContext(),
|
|
*GetFactManager()) &&
|
|
"Should be applicable by construction.");
|
|
transformation::Apply(add_type_int, GetIRContext(), GetFactManager());
|
|
*GetTransformations()->add_transformation()->mutable_add_type_int() =
|
|
add_type_int;
|
|
}
|
|
}
|
|
}
|
|
|
|
{
|
|
// Add 32-bit float type if not present.
|
|
opt::analysis::Float temp_float_type(32);
|
|
if (!GetIRContext()->get_type_mgr()->GetId(&temp_float_type)) {
|
|
protobufs::TransformationAddTypeFloat add_type_float =
|
|
transformation::MakeTransformationAddTypeFloat(
|
|
GetFuzzerContext()->GetFreshId(), 32);
|
|
assert(transformation::IsApplicable(add_type_float, GetIRContext(),
|
|
*GetFactManager()) &&
|
|
"Should be applicable by construction.");
|
|
transformation::Apply(add_type_float, GetIRContext(), GetFactManager());
|
|
*GetTransformations()->add_transformation()->mutable_add_type_float() =
|
|
add_type_float;
|
|
}
|
|
}
|
|
|
|
// 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)) {
|
|
protobufs::TransformationAddConstantBoolean add_constant_boolean =
|
|
transformation::MakeTransformationAddConstantBoolean(
|
|
GetFuzzerContext()->GetFreshId(), boolean_value);
|
|
assert(transformation::IsApplicable(add_constant_boolean, GetIRContext(),
|
|
*GetFactManager()) &&
|
|
"Should be applicable by construction.");
|
|
transformation::Apply(add_constant_boolean, GetIRContext(),
|
|
GetFactManager());
|
|
*GetTransformations()
|
|
->add_transformation()
|
|
->mutable_add_constant_boolean() = add_constant_boolean;
|
|
}
|
|
}
|
|
|
|
// 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 :
|
|
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 = transformation::MakeTransformationAddTypePointer(
|
|
GetFuzzerContext()->GetFreshId(), SpvStorageClassUniform,
|
|
element_type_id);
|
|
assert(transformation::IsApplicable(add_pointer, GetIRContext(),
|
|
*GetFactManager()) &&
|
|
"Should be applicable by construction.");
|
|
transformation::Apply(add_pointer, GetIRContext(), GetFactManager());
|
|
*GetTransformations()->add_transformation()->mutable_add_type_pointer() =
|
|
add_pointer;
|
|
}
|
|
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
|