Skip to content

Commit

Permalink
spirv-fuzz: Transformation to add OpConstantNull (KhronosGroup#3273)
Browse files Browse the repository at this point in the history
Adds a transformation for adding OpConstantNull to a module, for
appropriate data types.
  • Loading branch information
afd authored Apr 2, 2020
1 parent 5d491a7 commit e95fbfb
Show file tree
Hide file tree
Showing 11 changed files with 372 additions and 0 deletions.
2 changes: 2 additions & 0 deletions source/fuzz/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -79,6 +79,7 @@ if(SPIRV_BUILD_FUZZER)
transformation_access_chain.h
transformation_add_constant_boolean.h
transformation_add_constant_composite.h
transformation_add_constant_null.h
transformation_add_constant_scalar.h
transformation_add_dead_block.h
transformation_add_dead_break.h
Expand Down Expand Up @@ -171,6 +172,7 @@ if(SPIRV_BUILD_FUZZER)
transformation_access_chain.cpp
transformation_add_constant_boolean.cpp
transformation_add_constant_composite.cpp
transformation_add_constant_null.cpp
transformation_add_constant_scalar.cpp
transformation_add_dead_block.cpp
transformation_add_dead_break.cpp
Expand Down
15 changes: 15 additions & 0 deletions source/fuzz/fuzzer_pass_donate_modules.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@
#include "source/fuzz/instruction_message.h"
#include "source/fuzz/transformation_add_constant_boolean.h"
#include "source/fuzz/transformation_add_constant_composite.h"
#include "source/fuzz/transformation_add_constant_null.h"
#include "source/fuzz/transformation_add_constant_scalar.h"
#include "source/fuzz/transformation_add_function.h"
#include "source/fuzz/transformation_add_global_undef.h"
Expand Down Expand Up @@ -394,6 +395,20 @@ void FuzzerPassDonateModules::HandleTypesAndValues(
original_id_to_donated_id->at(type_or_value.type_id()),
constituent_ids));
} break;
case SpvOpConstantNull: {
if (!original_id_to_donated_id->count(type_or_value.type_id())) {
// We did not donate the type associated with this null constant, so
// we cannot donate the null constant.
continue;
}

// It is fine to have multiple OpConstantNull instructions of the same
// type, so we just add this to the recipient module.
new_result_id = GetFuzzerContext()->GetFreshId();
ApplyTransformation(TransformationAddConstantNull(
new_result_id,
original_id_to_donated_id->at(type_or_value.type_id())));
} break;
case SpvOpVariable: {
// This is a global variable that could have one of various storage
// classes. However, we change all global variable pointer storage
Expand Down
7 changes: 7 additions & 0 deletions source/fuzz/fuzzer_util.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -537,6 +537,13 @@ uint32_t MaybeGetPointerType(opt::IRContext* context, uint32_t pointee_type_id,
return 0;
}

bool IsNullConstantSupported(const opt::analysis::Type& type) {
return type.AsBool() || type.AsInteger() || type.AsFloat() ||
type.AsMatrix() || type.AsVector() || type.AsArray() ||
type.AsStruct() || type.AsPointer() || type.AsEvent() ||
type.AsDeviceEvent() || type.AsReserveId() || type.AsQueue();
}

} // namespace fuzzerutil

} // namespace fuzz
Expand Down
4 changes: 4 additions & 0 deletions source/fuzz/fuzzer_util.h
Original file line number Diff line number Diff line change
Expand Up @@ -210,6 +210,10 @@ SpvStorageClass GetStorageClassFromPointerType(opt::IRContext* context,
uint32_t MaybeGetPointerType(opt::IRContext* context, uint32_t pointee_type_id,
SpvStorageClass storage_class);

// Returns true if and only if |type| is one of the types for which it is legal
// to have an OpConstantNull value.
bool IsNullConstantSupported(const opt::analysis::Type& type);

} // namespace fuzzerutil

} // namespace fuzz
Expand Down
13 changes: 13 additions & 0 deletions source/fuzz/protobufs/spvtoolsfuzz.proto
Original file line number Diff line number Diff line change
Expand Up @@ -372,6 +372,7 @@ message Transformation {
TransformationSwapCommutableOperands swap_commutable_operands = 41;
TransformationPermuteFunctionParameters permute_function_parameters = 42;
TransformationToggleAccessChainInstruction toggle_access_chain_instruction = 43;
TransformationAddConstantNull add_constant_null = 44;
// Add additional option using the next available number.
}
}
Expand Down Expand Up @@ -422,6 +423,18 @@ message TransformationAddConstantComposite {

}

message TransformationAddConstantNull {

// Adds a null constant.

// Id for the constant
uint32 fresh_id = 1;

// Type of the constant
uint32 type_id = 2;

}

message TransformationAddConstantScalar {

// Adds a constant of the given scalar type.
Expand Down
4 changes: 4 additions & 0 deletions source/fuzz/transformation.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@
#include "source/fuzz/transformation_access_chain.h"
#include "source/fuzz/transformation_add_constant_boolean.h"
#include "source/fuzz/transformation_add_constant_composite.h"
#include "source/fuzz/transformation_add_constant_null.h"
#include "source/fuzz/transformation_add_constant_scalar.h"
#include "source/fuzz/transformation_add_dead_block.h"
#include "source/fuzz/transformation_add_dead_break.h"
Expand Down Expand Up @@ -78,6 +79,9 @@ std::unique_ptr<Transformation> Transformation::FromMessage(
case protobufs::Transformation::TransformationCase::kAddConstantComposite:
return MakeUnique<TransformationAddConstantComposite>(
message.add_constant_composite());
case protobufs::Transformation::TransformationCase::kAddConstantNull:
return MakeUnique<TransformationAddConstantNull>(
message.add_constant_null());
case protobufs::Transformation::TransformationCase::kAddConstantScalar:
return MakeUnique<TransformationAddConstantScalar>(
message.add_constant_scalar());
Expand Down
66 changes: 66 additions & 0 deletions source/fuzz/transformation_add_constant_null.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
// Copyright (c) 2020 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/transformation_add_constant_null.h"

#include "source/fuzz/fuzzer_util.h"

namespace spvtools {
namespace fuzz {

TransformationAddConstantNull::TransformationAddConstantNull(
const spvtools::fuzz::protobufs::TransformationAddConstantNull& message)
: message_(message) {}

TransformationAddConstantNull::TransformationAddConstantNull(uint32_t fresh_id,
uint32_t type_id) {
message_.set_fresh_id(fresh_id);
message_.set_type_id(type_id);
}

bool TransformationAddConstantNull::IsApplicable(
opt::IRContext* context, const TransformationContext& /*unused*/) const {
// A fresh id is required.
if (!fuzzerutil::IsFreshId(context, message_.fresh_id())) {
return false;
}
auto type = context->get_type_mgr()->GetType(message_.type_id());
// The type must exist.
if (!type) {
return false;
}
// The type must be one of the types for which null constants are allowed,
// according to the SPIR-V spec.
return fuzzerutil::IsNullConstantSupported(*type);
}

void TransformationAddConstantNull::Apply(
opt::IRContext* context, TransformationContext* /*unused*/) const {
context->module()->AddGlobalValue(MakeUnique<opt::Instruction>(
context, SpvOpConstantNull, message_.type_id(), message_.fresh_id(),
opt::Instruction::OperandList()));
fuzzerutil::UpdateModuleIdBound(context, message_.fresh_id());
// We have added an instruction to the module, so need to be careful about the
// validity of existing analyses.
context->InvalidateAnalysesExceptFor(opt::IRContext::Analysis::kAnalysisNone);
}

protobufs::Transformation TransformationAddConstantNull::ToMessage() const {
protobufs::Transformation result;
*result.mutable_add_constant_null() = message_;
return result;
}

} // namespace fuzz
} // namespace spvtools
54 changes: 54 additions & 0 deletions source/fuzz/transformation_add_constant_null.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
// Copyright (c) 2020 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_TRANSFORMATION_ADD_CONSTANT_NULL_H_
#define SOURCE_FUZZ_TRANSFORMATION_ADD_CONSTANT_NULL_H_

#include "source/fuzz/protobufs/spirvfuzz_protobufs.h"
#include "source/fuzz/transformation.h"
#include "source/fuzz/transformation_context.h"
#include "source/opt/ir_context.h"

namespace spvtools {
namespace fuzz {

class TransformationAddConstantNull : public Transformation {
public:
explicit TransformationAddConstantNull(
const protobufs::TransformationAddConstantNull& message);

TransformationAddConstantNull(uint32_t fresh_id, uint32_t type_id);

// - |message_.fresh_id| must be fresh
// - |message_.type_id| must be the id of a type for which it is acceptable
// to create a null constant
bool IsApplicable(
opt::IRContext* context,
const TransformationContext& transformation_context) const override;

// Adds an OpConstantNull instruction to the module, with |message_.type_id|
// as its type. The instruction has result id |message_.fresh_id|.
void Apply(opt::IRContext* context,
TransformationContext* transformation_context) const override;

protobufs::Transformation ToMessage() const override;

private:
protobufs::TransformationAddConstantNull message_;
};

} // namespace fuzz
} // namespace spvtools

#endif // SOURCE_FUZZ_TRANSFORMATION_ADD_CONSTANT_NULL_H_
1 change: 1 addition & 0 deletions test/fuzz/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ if (${SPIRV_BUILD_FUZZER})
transformation_access_chain_test.cpp
transformation_add_constant_boolean_test.cpp
transformation_add_constant_composite_test.cpp
transformation_add_constant_null_test.cpp
transformation_add_constant_scalar_test.cpp
transformation_add_dead_block_test.cpp
transformation_add_dead_break_test.cpp
Expand Down
66 changes: 66 additions & 0 deletions test/fuzz/fuzzer_pass_donate_modules_test.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -508,6 +508,72 @@ TEST(FuzzerPassDonateModulesTest, DonateFunctionTypeWithDifferentPointers) {
ASSERT_TRUE(IsValid(env, recipient_context.get()));
}

TEST(FuzzerPassDonateModulesTest, DonateOpConstantNull) {
std::string recipient_shader = R"(
OpCapability Shader
OpCapability ImageQuery
%1 = OpExtInstImport "GLSL.std.450"
OpMemoryModel Logical GLSL450
OpEntryPoint Fragment %4 "main"
OpExecutionMode %4 OriginUpperLeft
OpSource ESSL 320
OpSourceExtension "GL_EXT_samplerless_texture_functions"
%2 = OpTypeVoid
%3 = OpTypeFunction %2
%4 = OpFunction %2 None %3
%5 = OpLabel
OpReturn
OpFunctionEnd
)";

std::string donor_shader = R"(
OpCapability Shader
OpCapability ImageQuery
%1 = OpExtInstImport "GLSL.std.450"
OpMemoryModel Logical GLSL450
OpEntryPoint Fragment %4 "main"
OpExecutionMode %4 OriginUpperLeft
OpSource ESSL 320
%2 = OpTypeVoid
%3 = OpTypeFunction %2
%6 = OpTypeFloat 32
%7 = OpTypePointer Private %6
%8 = OpConstantNull %7
%4 = OpFunction %2 None %3
%5 = OpLabel
OpReturn
OpFunctionEnd
)";

const auto env = SPV_ENV_UNIVERSAL_1_3;
const auto consumer = nullptr;
const auto recipient_context =
BuildModule(env, consumer, recipient_shader, kFuzzAssembleOption);
ASSERT_TRUE(IsValid(env, recipient_context.get()));

const auto donor_context =
BuildModule(env, consumer, donor_shader, kFuzzAssembleOption);
ASSERT_TRUE(IsValid(env, donor_context.get()));

FactManager fact_manager;
spvtools::ValidatorOptions validator_options;
TransformationContext transformation_context(&fact_manager,
validator_options);

FuzzerContext fuzzer_context(MakeUnique<PseudoRandomGenerator>(0).get(), 100);
protobufs::TransformationSequence transformation_sequence;

FuzzerPassDonateModules fuzzer_pass(recipient_context.get(),
&transformation_context, &fuzzer_context,
&transformation_sequence, {});

fuzzer_pass.DonateSingleModule(donor_context.get(), false);

// We just check that the result is valid. Checking to what it should be
// exactly equal to would be very fragile.
ASSERT_TRUE(IsValid(env, recipient_context.get()));
}

TEST(FuzzerPassDonateModulesTest, Miscellaneous1) {
std::string recipient_shader = R"(
OpCapability Shader
Expand Down
Loading

0 comments on commit e95fbfb

Please sign in to comment.