Add validation for OpArrayLength. (#2117)
The validation rules for OpArrayLength are not checked by the validator.
This with add them.
Fixes https://crbug.com/907451.
diff --git a/source/val/validate_memory.cpp b/source/val/validate_memory.cpp
index fb9d253..c08d7e6 100644
--- a/source/val/validate_memory.cpp
+++ b/source/val/validate_memory.cpp
@@ -842,6 +842,61 @@
return ValidateAccessChain(_, inst);
}
+spv_result_t ValidateArrayLength(ValidationState_t& state,
+ const Instruction* inst) {
+ std::string instr_name =
+ "Op" + std::string(spvOpcodeString(static_cast<SpvOp>(inst->opcode())));
+
+ // Result type must be a 32-bit unsigned int.
+ auto result_type = state.FindDef(inst->type_id());
+ if (result_type->opcode() != SpvOpTypeInt ||
+ result_type->GetOperandAs<uint32_t>(1) != 32 ||
+ result_type->GetOperandAs<uint32_t>(2) != 0) {
+ return state.diag(SPV_ERROR_INVALID_ID, inst)
+ << "The Result Type of " << instr_name << " <id> '"
+ << state.getIdName(inst->id())
+ << "' must be OpTypeInt with width 32 and signedness 0.";
+ }
+
+ // The structure that is passed in must be an pointer to a structure, whose
+ // last element is a runtime array.
+ auto pointer = state.FindDef(inst->GetOperandAs<uint32_t>(2));
+ auto pointer_type = state.FindDef(pointer->type_id());
+ if (pointer_type->opcode() != SpvOpTypePointer) {
+ return state.diag(SPV_ERROR_INVALID_ID, inst)
+ << "The Struture's type in " << instr_name << " <id> '"
+ << state.getIdName(inst->id())
+ << "' must be a pointer to an OpTypeStruct.";
+ }
+
+ auto structure_type = state.FindDef(pointer_type->GetOperandAs<uint32_t>(2));
+ if (structure_type->opcode() != SpvOpTypeStruct) {
+ return state.diag(SPV_ERROR_INVALID_ID, inst)
+ << "The Struture's type in " << instr_name << " <id> '"
+ << state.getIdName(inst->id())
+ << "' must be a pointer to an OpTypeStruct.";
+ }
+
+ auto num_of_members = structure_type->operands().size() - 1;
+ auto last_member =
+ state.FindDef(structure_type->GetOperandAs<uint32_t>(num_of_members));
+ if (last_member->opcode() != SpvOpTypeRuntimeArray) {
+ return state.diag(SPV_ERROR_INVALID_ID, inst)
+ << "The Struture's last member in " << instr_name << " <id> '"
+ << state.getIdName(inst->id()) << "' must be an OpTypeRuntimeArray.";
+ }
+
+ // The array member must the the index of the last element (the run time
+ // array).
+ if (inst->GetOperandAs<uint32_t>(3) != num_of_members - 1) {
+ return state.diag(SPV_ERROR_INVALID_ID, inst)
+ << "The array member in " << instr_name << " <id> '"
+ << state.getIdName(inst->id())
+ << "' must be an the last member of the struct.";
+ }
+ return SPV_SUCCESS;
+}
+
} // namespace
spv_result_t MemoryPass(ValidationState_t& _, const Instruction* inst) {
@@ -867,8 +922,10 @@
case SpvOpInBoundsPtrAccessChain:
if (auto error = ValidateAccessChain(_, inst)) return error;
break;
- case SpvOpImageTexelPointer:
case SpvOpArrayLength:
+ if (auto error = ValidateArrayLength(_, inst)) return error;
+ break;
+ case SpvOpImageTexelPointer:
case SpvOpGenericPtrMemSemantics:
default:
break;
@@ -876,6 +933,5 @@
return SPV_SUCCESS;
}
-
} // namespace val
} // namespace spvtools
diff --git a/test/val/val_memory_test.cpp b/test/val/val_memory_test.cpp
index 8dafa0d..a2e8bf3 100644
--- a/test/val/val_memory_test.cpp
+++ b/test/val/val_memory_test.cpp
@@ -543,6 +543,301 @@
"%5 = OpVariable %_ptr_Input_float Input %float_1\n"));
}
+TEST_F(ValidateMemory, ArrayLenCorrectResultType) {
+ std::string spirv = R"(
+ OpCapability Shader
+ OpMemoryModel Logical GLSL450
+ OpEntryPoint Fragment %1 "main"
+ OpExecutionMode %1 OriginUpperLeft
+ %void = OpTypeVoid
+ %3 = OpTypeFunction %void
+ %float = OpTypeFloat 32
+ %uint = OpTypeInt 32 0
+%_runtimearr_float = OpTypeRuntimeArray %float
+ %_struct_7 = OpTypeStruct %_runtimearr_float
+%_ptr_Function__struct_7 = OpTypePointer Function %_struct_7
+ %1 = OpFunction %void None %3
+ %9 = OpLabel
+ %10 = OpVariable %_ptr_Function__struct_7 Function
+ %11 = OpArrayLength %uint %10 0
+ OpReturn
+ OpFunctionEnd
+
+)";
+
+ CompileSuccessfully(spirv.c_str());
+ EXPECT_EQ(SPV_SUCCESS, ValidateInstructions());
+}
+
+TEST_F(ValidateMemory, ArrayLenIndexCorrectWith2Members) {
+ std::string spirv = R"(
+ OpCapability Shader
+ OpMemoryModel Logical GLSL450
+ OpEntryPoint Fragment %1 "main"
+ OpExecutionMode %1 OriginUpperLeft
+ %void = OpTypeVoid
+ %3 = OpTypeFunction %void
+ %float = OpTypeFloat 32
+ %uint = OpTypeInt 32 0
+%_runtimearr_float = OpTypeRuntimeArray %float
+ %_struct_7 = OpTypeStruct %float %_runtimearr_float
+%_ptr_Function__struct_7 = OpTypePointer Function %_struct_7
+ %1 = OpFunction %void None %3
+ %9 = OpLabel
+ %10 = OpVariable %_ptr_Function__struct_7 Function
+ %11 = OpArrayLength %uint %10 1
+ OpReturn
+ OpFunctionEnd
+
+)";
+
+ CompileSuccessfully(spirv.c_str());
+ EXPECT_EQ(SPV_SUCCESS, ValidateInstructions());
+}
+
+TEST_F(ValidateMemory, ArrayLenResultNotIntType) {
+ std::string spirv = R"(
+ OpCapability Shader
+ OpMemoryModel Logical GLSL450
+ OpEntryPoint Fragment %1 "main"
+ OpExecutionMode %1 OriginUpperLeft
+ %void = OpTypeVoid
+ %3 = OpTypeFunction %void
+ %float = OpTypeFloat 32
+%_runtimearr_float = OpTypeRuntimeArray %float
+ %_struct_6 = OpTypeStruct %_runtimearr_float
+%_ptr_Function__struct_6 = OpTypePointer Function %_struct_6
+ %1 = OpFunction %void None %3
+ %8 = OpLabel
+ %9 = OpVariable %_ptr_Function__struct_6 Function
+ %10 = OpArrayLength %float %9 0
+ OpReturn
+ OpFunctionEnd
+)";
+
+ CompileSuccessfully(spirv.c_str());
+ EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions());
+ EXPECT_THAT(
+ getDiagnosticString(),
+ HasSubstr(
+ "The Result Type of OpArrayLength <id> '10' must be OpTypeInt with "
+ "width 32 and signedness 0.\n %10 = OpArrayLength %float %9 0\n"));
+}
+
+TEST_F(ValidateMemory, ArrayLenResultNot32bits) {
+ std::string spirv = R"(
+ OpCapability Shader
+ OpCapability Int16
+ OpMemoryModel Logical GLSL450
+ OpEntryPoint Fragment %1 "main"
+ OpExecutionMode %1 OriginUpperLeft
+ %void = OpTypeVoid
+ %3 = OpTypeFunction %void
+ %float = OpTypeFloat 32
+ %ushort = OpTypeInt 16 0
+%_runtimearr_float = OpTypeRuntimeArray %float
+ %_struct_7 = OpTypeStruct %_runtimearr_float
+%_ptr_Function__struct_7 = OpTypePointer Function %_struct_7
+ %1 = OpFunction %void None %3
+ %9 = OpLabel
+ %10 = OpVariable %_ptr_Function__struct_7 Function
+ %11 = OpArrayLength %ushort %10 0
+ OpReturn
+ OpFunctionEnd
+
+)";
+
+ CompileSuccessfully(spirv.c_str());
+ EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions());
+ EXPECT_THAT(
+ getDiagnosticString(),
+ HasSubstr(
+ "The Result Type of OpArrayLength <id> '11' must be OpTypeInt with "
+ "width 32 and signedness 0.\n %11 = OpArrayLength %ushort %10 0\n"));
+}
+
+TEST_F(ValidateMemory, ArrayLenResultSigned) {
+ std::string spirv = R"(
+ OpCapability Shader
+ OpMemoryModel Logical GLSL450
+ OpEntryPoint Fragment %1 "main"
+ OpExecutionMode %1 OriginUpperLeft
+ %void = OpTypeVoid
+ %3 = OpTypeFunction %void
+ %float = OpTypeFloat 32
+ %int = OpTypeInt 32 1
+%_runtimearr_float = OpTypeRuntimeArray %float
+ %_struct_7 = OpTypeStruct %_runtimearr_float
+%_ptr_Function__struct_7 = OpTypePointer Function %_struct_7
+ %1 = OpFunction %void None %3
+ %9 = OpLabel
+ %10 = OpVariable %_ptr_Function__struct_7 Function
+ %11 = OpArrayLength %int %10 0
+ OpReturn
+ OpFunctionEnd
+
+)";
+
+ CompileSuccessfully(spirv.c_str());
+ EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions());
+ EXPECT_THAT(
+ getDiagnosticString(),
+ HasSubstr(
+ "The Result Type of OpArrayLength <id> '11' must be OpTypeInt with "
+ "width 32 and signedness 0.\n %11 = OpArrayLength %int %10 0\n"));
+}
+
+TEST_F(ValidateMemory, ArrayLenInputNotStruct) {
+ std::string spirv = R"(
+ OpCapability Shader
+ OpMemoryModel Logical GLSL450
+ OpEntryPoint Fragment %1 "main"
+ OpExecutionMode %1 OriginUpperLeft
+ %void = OpTypeVoid
+ %3 = OpTypeFunction %void
+ %float = OpTypeFloat 32
+ %uint = OpTypeInt 32 0
+%_runtimearr_float = OpTypeRuntimeArray %float
+ %_struct_7 = OpTypeStruct %_runtimearr_float
+%_ptr_Function_float = OpTypePointer Function %float
+ %1 = OpFunction %void None %3
+ %9 = OpLabel
+ %10 = OpVariable %_ptr_Function_float Function
+ %11 = OpArrayLength %uint %10 0
+ OpReturn
+ OpFunctionEnd
+
+)";
+
+ CompileSuccessfully(spirv.c_str());
+ EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions());
+ EXPECT_THAT(getDiagnosticString(),
+ HasSubstr("The Struture's type in OpArrayLength <id> '11' must "
+ "be a pointer to an OpTypeStruct."));
+}
+
+TEST_F(ValidateMemory, ArrayLenInputLastMemberNoRTA) {
+ std::string spirv = R"(
+ OpCapability Shader
+ OpMemoryModel Logical GLSL450
+ OpEntryPoint Fragment %1 "main"
+ OpExecutionMode %1 OriginUpperLeft
+ %void = OpTypeVoid
+ %3 = OpTypeFunction %void
+ %float = OpTypeFloat 32
+ %uint = OpTypeInt 32 0
+%_runtimearr_float = OpTypeRuntimeArray %float
+ %_struct_7 = OpTypeStruct %float
+%_ptr_Function__struct_7 = OpTypePointer Function %_struct_7
+ %1 = OpFunction %void None %3
+ %9 = OpLabel
+ %10 = OpVariable %_ptr_Function__struct_7 Function
+ %11 = OpArrayLength %uint %10 0
+ OpReturn
+ OpFunctionEnd
+
+)";
+
+ CompileSuccessfully(spirv.c_str());
+ EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions());
+ EXPECT_THAT(
+ getDiagnosticString(),
+ HasSubstr("The Struture's last member in OpArrayLength <id> '11' must be "
+ "an OpTypeRuntimeArray.\n %11 = OpArrayLength %uint %10 0\n"));
+}
+
+TEST_F(ValidateMemory, ArrayLenInputLastMemberNoRTA2) {
+ std::string spirv = R"(
+ OpCapability Shader
+ OpMemoryModel Logical GLSL450
+ OpEntryPoint Fragment %1 "main"
+ OpExecutionMode %1 OriginUpperLeft
+ %void = OpTypeVoid
+ %3 = OpTypeFunction %void
+ %float = OpTypeFloat 32
+ %uint = OpTypeInt 32 0
+%_runtimearr_float = OpTypeRuntimeArray %float
+ %_struct_7 = OpTypeStruct %_runtimearr_float %float
+%_ptr_Function__struct_7 = OpTypePointer Function %_struct_7
+ %1 = OpFunction %void None %3
+ %9 = OpLabel
+ %10 = OpVariable %_ptr_Function__struct_7 Function
+ %11 = OpArrayLength %uint %10 1
+ OpReturn
+ OpFunctionEnd
+
+)";
+
+ CompileSuccessfully(spirv.c_str());
+ EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions());
+ EXPECT_THAT(
+ getDiagnosticString(),
+ HasSubstr("The Struture's last member in OpArrayLength <id> '11' must be "
+ "an OpTypeRuntimeArray.\n %11 = OpArrayLength %uint %10 1\n"));
+}
+
+TEST_F(ValidateMemory, ArrayLenIndexNotLastMember) {
+ std::string spirv = R"(
+ OpCapability Shader
+ OpMemoryModel Logical GLSL450
+ OpEntryPoint Fragment %1 "main"
+ OpExecutionMode %1 OriginUpperLeft
+ %void = OpTypeVoid
+ %3 = OpTypeFunction %void
+ %float = OpTypeFloat 32
+ %uint = OpTypeInt 32 0
+%_runtimearr_float = OpTypeRuntimeArray %float
+ %_struct_7 = OpTypeStruct %float %_runtimearr_float
+%_ptr_Function__struct_7 = OpTypePointer Function %_struct_7
+ %1 = OpFunction %void None %3
+ %9 = OpLabel
+ %10 = OpVariable %_ptr_Function__struct_7 Function
+ %11 = OpArrayLength %uint %10 0
+ OpReturn
+ OpFunctionEnd
+
+)";
+
+ CompileSuccessfully(spirv.c_str());
+ EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions());
+ EXPECT_THAT(
+ getDiagnosticString(),
+ HasSubstr(
+ "The array member in OpArrayLength <id> '11' must be an the last "
+ "member of the struct.\n %11 = OpArrayLength %uint %10 0\n"));
+}
+
+TEST_F(ValidateMemory, ArrayLenIndexNotPointerToStruct) {
+ std::string spirv = R"(
+ OpCapability Shader
+ OpMemoryModel Logical GLSL450
+ OpEntryPoint Fragment %1 "main"
+ OpExecutionMode %1 OriginUpperLeft
+ %void = OpTypeVoid
+ %3 = OpTypeFunction %void
+ %float = OpTypeFloat 32
+ %uint = OpTypeInt 32 0
+%_runtimearr_float = OpTypeRuntimeArray %float
+ %_struct_7 = OpTypeStruct %float %_runtimearr_float
+%_ptr_Function__struct_7 = OpTypePointer Function %_struct_7
+ %1 = OpFunction %void None %3
+ %9 = OpLabel
+ %10 = OpVariable %_ptr_Function__struct_7 Function
+ %11 = OpLoad %_struct_7 %10
+ %12 = OpArrayLength %uint %11 0
+ OpReturn
+ OpFunctionEnd
+
+)";
+
+ CompileSuccessfully(spirv.c_str());
+ EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions());
+ EXPECT_THAT(
+ getDiagnosticString(),
+ HasSubstr(
+ "The Struture's type in OpArrayLength <id> '12' must be a pointer to "
+ "an OpTypeStruct.\n %12 = OpArrayLength %uint %11 0\n"));
+}
} // namespace
} // namespace val
} // namespace spvtools