spirv-reader: reject undef and null pointers
This is defensive. Without variable pointers capabilities, this is definitely invalid, but not yet checked by the SPIRV-Tools validator. Bug: tint:807 Change-Id: If9b0b19573b1ca14a1c55aa20c9d42784ec12568 Reviewed-on: https://dawn-review.googlesource.com/c/tint/+/56700 Auto-Submit: David Neto <dneto@google.com> Kokoro: Kokoro <noreply+kokoro@google.com> Commit-Queue: David Neto <dneto@google.com> Reviewed-by: Ben Clayton <bclayton@google.com>
This commit is contained in:
parent
20c2ff60d2
commit
a01bb4c984
|
@ -4560,6 +4560,8 @@ bool FunctionEmitter::RegisterLocallyDefinedValues() {
|
||||||
}
|
}
|
||||||
switch (inst.opcode()) {
|
switch (inst.opcode()) {
|
||||||
case SpvOpUndef:
|
case SpvOpUndef:
|
||||||
|
return Fail()
|
||||||
|
<< "undef pointer is not valid: " << inst.PrettyPrint();
|
||||||
case SpvOpVariable:
|
case SpvOpVariable:
|
||||||
// Keep the default decision based on the result type.
|
// Keep the default decision based on the result type.
|
||||||
break;
|
break;
|
||||||
|
|
|
@ -1280,12 +1280,11 @@ Return{}
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST_F(SpvParserMemoryTest, DISABLED_RemapStorageBuffer_ThroughFunctionCall) {
|
TEST_F(SpvParserMemoryTest, DISABLED_RemapStorageBuffer_ThroughFunctionCall) {
|
||||||
// TODO(dneto): Blocked on OpFunctionCall support.
|
// WGSL does not support pointer-to-storage-buffer as function parameter
|
||||||
// We might need this for passing pointers into atomic builtins.
|
|
||||||
}
|
}
|
||||||
TEST_F(SpvParserMemoryTest,
|
TEST_F(SpvParserMemoryTest,
|
||||||
DISABLED_RemapStorageBuffer_ThroughFunctionParameter) {
|
DISABLED_RemapStorageBuffer_ThroughFunctionParameter) {
|
||||||
// TODO(dneto): Blocked on OpFunctionCall support.
|
// WGSL does not support pointer-to-storage-buffer as function parameter
|
||||||
}
|
}
|
||||||
|
|
||||||
std::string RuntimeArrayPreamble() {
|
std::string RuntimeArrayPreamble() {
|
||||||
|
@ -1359,6 +1358,79 @@ TEST_F(SpvParserMemoryTest, ArrayLength) {
|
||||||
)")) << body_str;
|
)")) << body_str;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
std::string InvalidPointerPreamble() {
|
||||||
|
return R"(
|
||||||
|
OpCapability Shader
|
||||||
|
OpMemoryModel Logical Simple
|
||||||
|
OpEntryPoint Fragment %main "main"
|
||||||
|
OpExecutionMode %main OriginUpperLeft
|
||||||
|
|
||||||
|
%uint = OpTypeInt 32 0
|
||||||
|
%ptr_ty = OpTypePointer Function %uint
|
||||||
|
|
||||||
|
%void = OpTypeVoid
|
||||||
|
%voidfn = OpTypeFunction %void
|
||||||
|
)";
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_F(SpvParserMemoryTest, InvalidPointer_Undef_ModuleScope_IsError) {
|
||||||
|
const std::string assembly = InvalidPointerPreamble() + R"(
|
||||||
|
%ptr = OpUndef %ptr_ty
|
||||||
|
|
||||||
|
%main = OpFunction %void None %voidfn
|
||||||
|
%entry = OpLabel
|
||||||
|
%1 = OpCopyObject %ptr_ty %ptr
|
||||||
|
%2 = OpAccessChain %ptr_ty %ptr
|
||||||
|
%3 = OpInBoundsAccessChain %ptr_ty %ptr
|
||||||
|
; now show the invalid pointer propagates
|
||||||
|
%10 = OpCopyObject %ptr_ty %1
|
||||||
|
%20 = OpAccessChain %ptr_ty %2
|
||||||
|
%30 = OpInBoundsAccessChain %ptr_ty %3
|
||||||
|
OpReturn
|
||||||
|
OpFunctionEnd
|
||||||
|
)";
|
||||||
|
auto p = parser(test::Assemble(assembly));
|
||||||
|
EXPECT_FALSE(p->BuildAndParseInternalModule()) << assembly;
|
||||||
|
EXPECT_EQ(p->error(), "undef pointer is not valid: %9 = OpUndef %6");
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_F(SpvParserMemoryTest, InvalidPointer_Undef_FunctionScope_IsError) {
|
||||||
|
const std::string assembly = InvalidPointerPreamble() + R"(
|
||||||
|
|
||||||
|
%main = OpFunction %void None %voidfn
|
||||||
|
%entry = OpLabel
|
||||||
|
%ptr = OpUndef %ptr_ty
|
||||||
|
OpReturn
|
||||||
|
OpFunctionEnd
|
||||||
|
)";
|
||||||
|
auto p = parser(test::Assemble(assembly));
|
||||||
|
EXPECT_FALSE(p->BuildAndParseInternalModule()) << assembly;
|
||||||
|
EXPECT_EQ(p->error(), "undef pointer is not valid: %7 = OpUndef %3");
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_F(SpvParserMemoryTest, InvalidPointer_ConstantNull_IsError) {
|
||||||
|
// OpConstantNull on logical pointer requires variable-pointers, which
|
||||||
|
// is not (yet) supported by WGSL features.
|
||||||
|
const std::string assembly = InvalidPointerPreamble() + R"(
|
||||||
|
%ptr = OpConstantNull %ptr_ty
|
||||||
|
|
||||||
|
%main = OpFunction %void None %voidfn
|
||||||
|
%entry = OpLabel
|
||||||
|
%1 = OpCopyObject %ptr_ty %ptr
|
||||||
|
%2 = OpAccessChain %ptr_ty %ptr
|
||||||
|
%3 = OpInBoundsAccessChain %ptr_ty %ptr
|
||||||
|
; now show the invalid pointer propagates
|
||||||
|
%10 = OpCopyObject %ptr_ty %1
|
||||||
|
%20 = OpAccessChain %ptr_ty %2
|
||||||
|
%30 = OpInBoundsAccessChain %ptr_ty %3
|
||||||
|
OpReturn
|
||||||
|
OpFunctionEnd
|
||||||
|
)";
|
||||||
|
auto p = parser(test::Assemble(assembly));
|
||||||
|
EXPECT_FALSE(p->BuildAndParseInternalModule());
|
||||||
|
EXPECT_EQ(p->error(), "null pointer is not valid: %9 = OpConstantNull %6");
|
||||||
|
}
|
||||||
|
|
||||||
} // namespace
|
} // namespace
|
||||||
} // namespace spirv
|
} // namespace spirv
|
||||||
} // namespace reader
|
} // namespace reader
|
||||||
|
|
|
@ -610,6 +610,9 @@ bool ParserImpl::ParseInternalModuleExceptFunctions() {
|
||||||
if (!RegisterTypes()) {
|
if (!RegisterTypes()) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
if (!RejectInvalidPointerRoots()) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
if (!EmitScalarSpecConstants()) {
|
if (!EmitScalarSpecConstants()) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
@ -1288,6 +1291,33 @@ bool ParserImpl::RegisterTypes() {
|
||||||
return success_;
|
return success_;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool ParserImpl::RejectInvalidPointerRoots() {
|
||||||
|
if (!success_) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
for (auto& inst : module_->types_values()) {
|
||||||
|
if (const auto* result_type = type_mgr_->GetType(inst.type_id())) {
|
||||||
|
if (result_type->AsPointer()) {
|
||||||
|
switch (inst.opcode()) {
|
||||||
|
case SpvOpVariable:
|
||||||
|
// This is the only valid case.
|
||||||
|
break;
|
||||||
|
case SpvOpUndef:
|
||||||
|
return Fail() << "undef pointer is not valid: "
|
||||||
|
<< inst.PrettyPrint();
|
||||||
|
case SpvOpConstantNull:
|
||||||
|
return Fail() << "null pointer is not valid: "
|
||||||
|
<< inst.PrettyPrint();
|
||||||
|
default:
|
||||||
|
return Fail() << "module-scope pointer is not valid: "
|
||||||
|
<< inst.PrettyPrint();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return success();
|
||||||
|
}
|
||||||
|
|
||||||
bool ParserImpl::EmitScalarSpecConstants() {
|
bool ParserImpl::EmitScalarSpecConstants() {
|
||||||
if (!success_) {
|
if (!success_) {
|
||||||
return false;
|
return false;
|
||||||
|
|
|
@ -349,6 +349,11 @@ class ParserImpl : Reader {
|
||||||
/// @returns true if parser is still successful.
|
/// @returns true if parser is still successful.
|
||||||
bool RegisterTypes();
|
bool RegisterTypes();
|
||||||
|
|
||||||
|
/// Fail if there are any module-scope pointer values other than those
|
||||||
|
/// declared by OpVariable.
|
||||||
|
/// @returns true if parser is still successful.
|
||||||
|
bool RejectInvalidPointerRoots();
|
||||||
|
|
||||||
/// Register sampler and texture usage for memory object declarations.
|
/// Register sampler and texture usage for memory object declarations.
|
||||||
/// This must be called after we've registered line numbers for all
|
/// This must be called after we've registered line numbers for all
|
||||||
/// instructions. This is a no-op if the parser has already failed.
|
/// instructions. This is a no-op if the parser has already failed.
|
||||||
|
|
|
@ -344,7 +344,7 @@ TEST_F(SpvModuleScopeVarParserTest,
|
||||||
const std::string assembly = PerVertexPreamble() + R"(
|
const std::string assembly = PerVertexPreamble() + R"(
|
||||||
%main = OpFunction %void None %voidfn
|
%main = OpFunction %void None %voidfn
|
||||||
%entry = OpLabel
|
%entry = OpLabel
|
||||||
%1000 = OpUndef %11
|
%1000 = OpCopyObject %11 %1
|
||||||
OpReturn
|
OpReturn
|
||||||
OpFunctionEnd
|
OpFunctionEnd
|
||||||
)";
|
)";
|
||||||
|
@ -352,7 +352,7 @@ TEST_F(SpvModuleScopeVarParserTest,
|
||||||
EXPECT_FALSE(p->BuildAndParseInternalModule());
|
EXPECT_FALSE(p->BuildAndParseInternalModule());
|
||||||
EXPECT_THAT(p->error(),
|
EXPECT_THAT(p->error(),
|
||||||
Eq("operations producing a pointer to a per-vertex structure are "
|
Eq("operations producing a pointer to a per-vertex structure are "
|
||||||
"not supported: %1000 = OpUndef %11"))
|
"not supported: %1000 = OpCopyObject %11 %1"))
|
||||||
<< p->error();
|
<< p->error();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue