diff --git a/layers/core_checks/cc_spirv.cpp b/layers/core_checks/cc_spirv.cpp index dc791e88987..d98a23cfdbd 100644 --- a/layers/core_checks/cc_spirv.cpp +++ b/layers/core_checks/cc_spirv.cpp @@ -374,6 +374,115 @@ bool CoreChecks::ValidateSubgroupRotateClustered(const spirv::Module &module_sta return skip; } +// Checks that any indexing/accesses that are known statically (using constants) are valid. +// This could try checking a LOT of edge cases, but if things are staic, any proper HLL (glsl, hlsl, slang, etc) will catch this, so +// this is just trying to do basic coverage. +bool CoreChecks::ValidateStaticAccess(const spirv::Module &module_state, const Location &loc) const { + bool skip = false; + + // Profiled and found we wasted lots of time getting to end of search to realize the base of the access chain contains no constant arrays, so track them and quickly exit if seen many times. + vvl::unordered_set skip_bases; + for (const spirv::Instruction &insn : module_state.GetInstructions()) { + if (insn.Opcode() != spv::OpLoad && insn.Opcode() != spv::OpStore) { + continue; + } + + // TODO - Should have loop to walk Load/Store to the Pointer, + // this case will not cover things such as OpCopyObject or double OpAccessChains or OpInBoundsAccessChain + const uint32_t pointer_insn_index = insn.Opcode() == spv::OpLoad ? 3 : 1; + const spirv::Instruction *access_chain_insn = module_state.FindDef(insn.Word(pointer_insn_index)); + if (!access_chain_insn || access_chain_insn->Opcode() != spv::OpAccessChain) { + continue; + } + + const uint32_t base_id = access_chain_insn->Word(3); + if (skip_bases.find(base_id) != skip_bases.end()) { + continue; + } + + const uint32_t index_0_offset = 4; // in OpAccessChain + const uint32_t index_count = access_chain_insn->Length() - index_0_offset; + if (index_count == 0) { + continue; // can occur if accessing into something like gl_Position + } + std::vector indexes(index_count); + for (uint32_t i = 0; i < index_count; i++) { + const spirv::Instruction *index_insn = module_state.FindDef(access_chain_insn->Word(index_0_offset + i)); + // TODO - Handle Spec Constants + if (!index_insn || index_insn->Opcode() != spv::OpConstant) { + continue; // not constants, so can't detect and need GPU-AV to catch + } + indexes[i] = index_insn->GetConstantValue(); + } + + // Currently just searching inside Resource Interface variables + const spirv::Instruction *base_insn = module_state.FindDef(base_id); + if (!base_insn || base_insn->Opcode() != spv::OpVariable) { + skip_bases.insert(base_id); + continue; + } + + const spirv::Instruction *base_ptr_insn = module_state.FindDef(base_insn->TypeId()); + if (!base_ptr_insn || base_ptr_insn->Opcode() != spv::OpTypePointer) { + skip_bases.insert(base_id); + continue; + } + + // From here, walk down the types and check if there is an array with constants + uint32_t current_index = 0; + bool found_constant_array = false; + const spirv::Instruction *type_insn = module_state.FindDef(base_ptr_insn->Word(3)); + while (type_insn && current_index < index_count) { + switch (type_insn->Opcode()) { + case spv::OpCopyObject: + type_insn = module_state.FindDef(type_insn->Word(3)); + break; + case spv::OpTypeStruct: + // Structs start members at word 2 + type_insn = module_state.FindDef(type_insn->Word(2 + indexes[current_index])); + current_index++; + break; + case spv::OpTypeArray: { + // can't use GetConstantValueById() because of + // https://github.com/KhronosGroup/Vulkan-ValidationLayers/issues/6293 + const spirv::Instruction *array_length_insn = module_state.FindDef(type_insn->Word(3)); + if (array_length_insn->Opcode() == spv::OpConstant) { + found_constant_array = true; + const uint32_t array_length = array_length_insn->GetConstantValue(); + if (indexes[current_index] >= array_length) { + // This is considered undefined behavior in SPIR-V, but is not properly detectable until runtime + const char *vuid = loc.function == Func::vkCreateShadersEXT + ? "VUID-VkShaderCreateInfoEXT-pCode-08737" + : "VUID-VkShaderModuleCreateInfo-pCode-08737"; + skip |= + LogError(vuid, module_state.handle(), loc, + "SPIR-V instruction (%s) indexes [%" PRIu32 "] into an array of size [%" PRIu32 + "]. (Even if this is in a conditional path that won't be taken, we can't assume how the " + "compiler may optimize this logic).", + insn.Describe().c_str(), indexes[current_index], array_length); + return skip; + } + } + + type_insn = module_state.FindDef(type_insn->Word(2)); // for 2D or 3D arrays + current_index++; + break; + } + case spv::OpTypeRuntimeArray: // can't detect and need GPU-AV to catch + default: + type_insn = nullptr; + break; + } + } + + if (!found_constant_array) { + skip_bases.insert(base_id); + } + } + + return skip; +} + bool CoreChecks::ValidateShaderStorageImageFormatsVariables(const spirv::Module &module_state, const spirv::Instruction &insn, const Location &loc) const { bool skip = false; @@ -2899,6 +3008,7 @@ bool CoreChecks::ValidateSpirvStateless(const spirv::Module &module_state, const skip |= ValidateShaderClock(module_state, stateless_data, loc); skip |= ValidateAtomicsTypes(module_state, stateless_data, loc); + skip |= ValidateStaticAccess(module_state, loc); skip |= ValidateVariables(module_state, loc); if (enabled_features.transformFeedback) { diff --git a/layers/core_checks/core_validation.h b/layers/core_checks/core_validation.h index c569daaf564..c731a3401fe 100644 --- a/layers/core_checks/core_validation.h +++ b/layers/core_checks/core_validation.h @@ -710,6 +710,7 @@ class CoreChecks : public ValidationStateTracker { bool ValidateMemoryScope(const spirv::Module& module_state, const spirv::Instruction& insn, const Location& loc) const; bool ValidateSubgroupRotateClustered(const spirv::Module& module_state, const spirv::Instruction& insn, const Location& loc) const; + bool ValidateStaticAccess(const spirv::Module& module_state, const Location& loc) const; bool ValidateCooperativeMatrix(const spirv::Module& module_state, const spirv::EntryPoint& entrypoint, const ShaderStageState& stage_state, const uint32_t local_size_x, const Location& loc) const; bool ValidateShaderResolveQCOM(const spirv::Module& module_state, VkShaderStageFlagBits stage, const vvl::Pipeline& pipeline, diff --git a/layers/gpu/spirv/bindless_descriptor_pass.cpp b/layers/gpu/spirv/bindless_descriptor_pass.cpp index ec9ca2cb5f5..f00077fa9e6 100644 --- a/layers/gpu/spirv/bindless_descriptor_pass.cpp +++ b/layers/gpu/spirv/bindless_descriptor_pass.cpp @@ -313,7 +313,7 @@ bool BindlessDescriptorPass::AnalyzeInstruction(const Function& function, const if (opcode == spv::OpLoad || opcode == spv::OpStore) { // TODO - Should have loop to walk Load/Store to the Pointer, - // this case will not cover things such as OpCopyObject or double OpAccessChains + // this case will not cover things such as OpCopyObject or double OpAccessChains or OpInBoundsAccessChain access_chain_inst_ = function.FindInstruction(inst.Operand(0)); if (!access_chain_inst_ || access_chain_inst_->Opcode() != spv::OpAccessChain) { return false; diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt index e48a766fd62..eece6adb9ce 100644 --- a/tests/CMakeLists.txt +++ b/tests/CMakeLists.txt @@ -181,6 +181,7 @@ target_sources(vk_layer_validation_tests PRIVATE unit/shader_mesh.cpp unit/shader_object.cpp unit/shader_object_positive.cpp + unit/shader_oob.cpp unit/shader_push_constants.cpp unit/shader_push_constants_positive.cpp unit/shader_spirv.cpp diff --git a/tests/unit/shader_oob.cpp b/tests/unit/shader_oob.cpp new file mode 100644 index 00000000000..7b3ddde4d53 --- /dev/null +++ b/tests/unit/shader_oob.cpp @@ -0,0 +1,421 @@ +/* + * Copyright (c) 2024 The Khronos Group Inc. + * Copyright (c) 2024 Valve Corporation + * Copyright (c) 2024 LunarG, Inc. + * + * 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 + */ + +#include "../framework/layer_validation_tests.h" +#include "../framework/pipeline_helper.h" + +class NegativeShaderOOB : public VkLayerTest {}; + +TEST_F(NegativeShaderOOB, ConstantBufferArray) { + SetTargetApiVersion(VK_API_VERSION_1_2); + RETURN_IF_SKIP(Init()); + + // layout(set = 0, binding = 0, std140) buffer Block1 { + // int i_data[4]; + // int o_data; + // }; + // void main() { + // o_data = i_data[4]; + // } + char const *cs_src = R"( + OpCapability Shader + OpMemoryModel Logical GLSL450 + OpEntryPoint GLCompute %main "main" %_ + OpExecutionMode %main LocalSize 1 1 1 + OpDecorate %_arr_int_uint_4 ArrayStride 16 + OpMemberDecorate %Block1 0 Offset 0 + OpMemberDecorate %Block1 1 Offset 64 + OpDecorate %Block1 Block + OpDecorate %_ DescriptorSet 0 + OpDecorate %_ Binding 0 + %void = OpTypeVoid + %3 = OpTypeFunction %void + %int = OpTypeInt 32 1 + %uint = OpTypeInt 32 0 + %uint_4 = OpConstant %uint 4 +%_arr_int_uint_4 = OpTypeArray %int %uint_4 + %Block1 = OpTypeStruct %_arr_int_uint_4 %int +%_ptr_StorageBuffer_Block1 = OpTypePointer StorageBuffer %Block1 + %_ = OpVariable %_ptr_StorageBuffer_Block1 StorageBuffer + %int_1 = OpConstant %int 1 + %int_0 = OpConstant %int 0 + %int_4 = OpConstant %int 4 +%_ptr_StorageBuffer_int = OpTypePointer StorageBuffer %int + %main = OpFunction %void None %3 + %5 = OpLabel + %17 = OpAccessChain %_ptr_StorageBuffer_int %_ %int_0 %int_4 + %18 = OpLoad %int %17 + %19 = OpAccessChain %_ptr_StorageBuffer_int %_ %int_1 + OpStore %19 %18 + OpReturn + OpFunctionEnd + )"; + + m_errorMonitor->SetDesiredError("VUID-VkShaderModuleCreateInfo-pCode-08737"); + auto cs = VkShaderObj::CreateFromASM(this, cs_src, VK_SHADER_STAGE_COMPUTE_BIT, SPV_ENV_VULKAN_1_2); + m_errorMonitor->VerifyFound(); +} + +TEST_F(NegativeShaderOOB, ConstantBufferArrayConditional) { + SetTargetApiVersion(VK_API_VERSION_1_2); + RETURN_IF_SKIP(Init()); + + // layout(set = 0, binding = 0, std140) buffer Block1 { + // int i_data[4]; + // int o_data; + // }; + // void main() { + // if (i_data[0] > 99) { + // o_data = i_data[3]; + // } + // } + char const *cs_src = R"( + OpCapability Shader + OpMemoryModel Logical GLSL450 + OpEntryPoint GLCompute %main "main" %_ + OpExecutionMode %main LocalSize 1 1 1 + OpDecorate %_arr_int_uint_4 ArrayStride 16 + OpMemberDecorate %Block1 0 Offset 0 + OpMemberDecorate %Block1 1 Offset 64 + OpDecorate %Block1 Block + OpDecorate %_ DescriptorSet 0 + OpDecorate %_ Binding 0 + %void = OpTypeVoid + %3 = OpTypeFunction %void + %int = OpTypeInt 32 1 + %uint = OpTypeInt 32 0 + %uint_4 = OpConstant %uint 4 +%_arr_int_uint_4 = OpTypeArray %int %uint_4 + %Block1 = OpTypeStruct %_arr_int_uint_4 %int +%_ptr_StorageBuffer_Block1 = OpTypePointer StorageBuffer %Block1 + %_ = OpVariable %_ptr_StorageBuffer_Block1 StorageBuffer + %int_0 = OpConstant %int 0 +%_ptr_StorageBuffer_int = OpTypePointer StorageBuffer %int + %int_99 = OpConstant %int 99 + %bool = OpTypeBool + %int_1 = OpConstant %int 1 + %int_4 = OpConstant %int 4 + %main = OpFunction %void None %3 + %5 = OpLabel + %15 = OpAccessChain %_ptr_StorageBuffer_int %_ %int_0 %int_0 + %16 = OpLoad %int %15 + %19 = OpSGreaterThan %bool %16 %int_99 + OpSelectionMerge %21 None + OpBranchConditional %19 %20 %21 + %20 = OpLabel + %24 = OpAccessChain %_ptr_StorageBuffer_int %_ %int_0 %int_4 + %25 = OpLoad %int %24 + %26 = OpAccessChain %_ptr_StorageBuffer_int %_ %int_1 + OpStore %26 %25 + OpBranch %21 + %21 = OpLabel + OpReturn + OpFunctionEnd + )"; + + m_errorMonitor->SetDesiredError("VUID-VkShaderModuleCreateInfo-pCode-08737"); + auto cs = VkShaderObj::CreateFromASM(this, cs_src, VK_SHADER_STAGE_COMPUTE_BIT, SPV_ENV_VULKAN_1_2); + m_errorMonitor->VerifyFound(); +} + +TEST_F(NegativeShaderOOB, ConstantBufferArrayInStruct) { + SetTargetApiVersion(VK_API_VERSION_1_2); + RETURN_IF_SKIP(Init()); + + // struct Foo { + // int a[4]; + // int b; + // }; + // layout(set = 0, binding = 0, std140) buffer Block1 { + // int o_data; + // Foo i_data[3]; + // }; + // void main() { + // o_data = i_data[1].a[4]; + // } + char const *cs_src = R"( + OpCapability Shader + OpMemoryModel Logical GLSL450 + OpEntryPoint GLCompute %main "main" %_ + OpExecutionMode %main LocalSize 1 1 1 + OpDecorate %_arr_int_uint_4 ArrayStride 16 + OpMemberDecorate %Foo 0 Offset 0 + OpMemberDecorate %Foo 1 Offset 64 + OpDecorate %_arr_Foo_uint_3 ArrayStride 80 + OpMemberDecorate %Block1 0 Offset 0 + OpMemberDecorate %Block1 1 Offset 16 + OpDecorate %Block1 Block + OpDecorate %_ DescriptorSet 0 + OpDecorate %_ Binding 0 + %void = OpTypeVoid + %3 = OpTypeFunction %void + %int = OpTypeInt 32 1 + %uint = OpTypeInt 32 0 + %uint_4 = OpConstant %uint 4 +%_arr_int_uint_4 = OpTypeArray %int %uint_4 + %Foo = OpTypeStruct %_arr_int_uint_4 %int + %uint_3 = OpConstant %uint 3 +%_arr_Foo_uint_3 = OpTypeArray %Foo %uint_3 + %Block1 = OpTypeStruct %int %_arr_Foo_uint_3 +%_ptr_StorageBuffer_Block1 = OpTypePointer StorageBuffer %Block1 + %_ = OpVariable %_ptr_StorageBuffer_Block1 StorageBuffer + %int_0 = OpConstant %int 0 + %int_1 = OpConstant %int 1 + %int_4 = OpConstant %int 4 +%_ptr_StorageBuffer_int = OpTypePointer StorageBuffer %int + %main = OpFunction %void None %3 + %5 = OpLabel + %20 = OpAccessChain %_ptr_StorageBuffer_int %_ %int_1 %int_1 %int_0 %int_4 + %21 = OpLoad %int %20 + %22 = OpAccessChain %_ptr_StorageBuffer_int %_ %int_0 + OpStore %22 %21 + OpReturn + OpFunctionEnd + )"; + + m_errorMonitor->SetDesiredError("VUID-VkShaderModuleCreateInfo-pCode-08737"); + auto cs = VkShaderObj::CreateFromASM(this, cs_src, VK_SHADER_STAGE_COMPUTE_BIT, SPV_ENV_VULKAN_1_2); + m_errorMonitor->VerifyFound(); +} + +TEST_F(NegativeShaderOOB, ConstantBufferArrayInStruct2) { + SetTargetApiVersion(VK_API_VERSION_1_2); + RETURN_IF_SKIP(Init()); + + // struct Foo { + // int a[4]; + // int b; + // }; + // layout(set = 0, binding = 0, std140) buffer Block1 { + // int o_data; + // Foo i_data[3]; + // }; + // void main() { + // o_data = i_data[1].a[4]; + // } + char const *cs_src = R"( + OpCapability Shader + OpMemoryModel Logical GLSL450 + OpEntryPoint GLCompute %main "main" %_ + OpExecutionMode %main LocalSize 1 1 1 + OpDecorate %_arr_int_uint_4 ArrayStride 16 + OpMemberDecorate %Foo 0 Offset 0 + OpMemberDecorate %Foo 1 Offset 64 + OpDecorate %_arr_Foo_uint_3 ArrayStride 80 + OpMemberDecorate %Block1 0 Offset 0 + OpMemberDecorate %Block1 1 Offset 16 + OpDecorate %Block1 Block + OpDecorate %_ DescriptorSet 0 + OpDecorate %_ Binding 0 + %void = OpTypeVoid + %3 = OpTypeFunction %void + %int = OpTypeInt 32 1 + %uint = OpTypeInt 32 0 + %uint_4 = OpConstant %uint 4 +%_arr_int_uint_4 = OpTypeArray %int %uint_4 + %Foo = OpTypeStruct %_arr_int_uint_4 %int + %uint_3 = OpConstant %uint 3 +%_arr_Foo_uint_3 = OpTypeArray %Foo %uint_3 + %Block1 = OpTypeStruct %int %_arr_Foo_uint_3 +%_ptr_StorageBuffer_Block1 = OpTypePointer StorageBuffer %Block1 + %_ = OpVariable %_ptr_StorageBuffer_Block1 StorageBuffer + %int_0 = OpConstant %int 0 + %int_1 = OpConstant %int 1 + %int_4 = OpConstant %int 4 +%ptr_array = OpTypePointer StorageBuffer %_arr_int_uint_4 +%ptr_int = OpTypePointer StorageBuffer %int + %main = OpFunction %void None %3 + %5 = OpLabel + %array_base = OpAccessChain %ptr_array %_ %int_1 %int_1 %int_0 + %20 = OpAccessChain %ptr_int %struct_base %int_4 + %21 = OpLoad %int %20 + %22 = OpAccessChain %ptr_int %_ %int_0 + OpStore %22 %21 + OpReturn + OpFunctionEnd + )"; + + m_errorMonitor->SetDesiredError("VUID-VkShaderModuleCreateInfo-pCode-08737"); + auto cs = VkShaderObj::CreateFromASM(this, cs_src, VK_SHADER_STAGE_COMPUTE_BIT, SPV_ENV_VULKAN_1_2); + m_errorMonitor->VerifyFound(); +} + +TEST_F(NegativeShaderOOB, ConstantBuffer2DArray) { + SetTargetApiVersion(VK_API_VERSION_1_2); + RETURN_IF_SKIP(Init()); + + // layout(set = 0, binding = 0, std140) buffer Block1 { + // int o_data; + // int i_data[4][4]; + // }; + // void main() { + // o_data = i_data[1][4] + i_data[4][1]; + // } + char const *cs_src = R"( + OpCapability Shader + OpMemoryModel Logical GLSL450 + OpEntryPoint GLCompute %main "main" %_ + OpExecutionMode %main LocalSize 1 1 1 + OpDecorate %_arr_int_uint_4 ArrayStride 16 + OpDecorate %_arr__arr_int_uint_4_uint_4 ArrayStride 64 + OpMemberDecorate %Block1 0 Offset 0 + OpMemberDecorate %Block1 1 Offset 16 + OpDecorate %Block1 Block + OpDecorate %_ DescriptorSet 0 + OpDecorate %_ Binding 0 + %void = OpTypeVoid + %3 = OpTypeFunction %void + %int = OpTypeInt 32 1 + %uint = OpTypeInt 32 0 + %uint_4 = OpConstant %uint 4 +%_arr_int_uint_4 = OpTypeArray %int %uint_4 +%_arr__arr_int_uint_4_uint_4 = OpTypeArray %_arr_int_uint_4 %uint_4 + %Block1 = OpTypeStruct %int %_arr__arr_int_uint_4_uint_4 +%_ptr_StorageBuffer_Block1 = OpTypePointer StorageBuffer %Block1 + %_ = OpVariable %_ptr_StorageBuffer_Block1 StorageBuffer + %int_0 = OpConstant %int 0 + %int_1 = OpConstant %int 1 + %int_4 = OpConstant %int 4 +%_ptr_StorageBuffer_int = OpTypePointer StorageBuffer %int + %main = OpFunction %void None %3 + %5 = OpLabel + %18 = OpAccessChain %_ptr_StorageBuffer_int %_ %int_1 %int_1 %int_4 + %19 = OpLoad %int %18 + %20 = OpAccessChain %_ptr_StorageBuffer_int %_ %int_1 %int_4 %int_1 + %21 = OpLoad %int %20 + %22 = OpIAdd %int %19 %21 + %23 = OpAccessChain %_ptr_StorageBuffer_int %_ %int_0 + OpStore %23 %22 + OpReturn + OpFunctionEnd + )"; + + m_errorMonitor->SetDesiredError("VUID-VkShaderModuleCreateInfo-pCode-08737"); // i_data + m_errorMonitor->SetDesiredError("VUID-VkShaderModuleCreateInfo-pCode-08737"); // o_data + auto cs = VkShaderObj::CreateFromASM(this, cs_src, VK_SHADER_STAGE_COMPUTE_BIT, SPV_ENV_VULKAN_1_2); + m_errorMonitor->VerifyFound(); +} + +TEST_F(NegativeShaderOOB, ConstantBuffer3DArray) { + SetTargetApiVersion(VK_API_VERSION_1_2); + RETURN_IF_SKIP(Init()); + + // layout(set = 0, binding = 0, std140) buffer Block1 { + // int i_data; + // int o_data[4][4][4]; + // }; + // void main() { + // o_data[1][4][1] = i_data; + // } + char const *cs_src = R"( + OpCapability Shader + OpMemoryModel Logical GLSL450 + OpEntryPoint GLCompute %main "main" %_ + OpExecutionMode %main LocalSize 1 1 1 + OpDecorate %_arr_int_uint_4 ArrayStride 16 + OpDecorate %_arr__arr_int_uint_4_uint_4 ArrayStride 64 + OpDecorate %_arr__arr__arr_int_uint_4_uint_4_uint_4 ArrayStride 256 + OpMemberDecorate %Block1 0 Offset 0 + OpMemberDecorate %Block1 1 Offset 16 + OpDecorate %Block1 Block + OpDecorate %_ DescriptorSet 0 + OpDecorate %_ Binding 0 + %void = OpTypeVoid + %3 = OpTypeFunction %void + %int = OpTypeInt 32 1 + %uint = OpTypeInt 32 0 + %uint_4 = OpConstant %uint 4 +%_arr_int_uint_4 = OpTypeArray %int %uint_4 +%_arr__arr_int_uint_4_uint_4 = OpTypeArray %_arr_int_uint_4 %uint_4 +%_arr__arr__arr_int_uint_4_uint_4_uint_4 = OpTypeArray %_arr__arr_int_uint_4_uint_4 %uint_4 + %Block1 = OpTypeStruct %int %_arr__arr__arr_int_uint_4_uint_4_uint_4 +%_ptr_StorageBuffer_Block1 = OpTypePointer StorageBuffer %Block1 + %_ = OpVariable %_ptr_StorageBuffer_Block1 StorageBuffer + %int_1 = OpConstant %int 1 + %int_4 = OpConstant %int 4 + %int_0 = OpConstant %int 0 +%_ptr_StorageBuffer_int = OpTypePointer StorageBuffer %int + %main = OpFunction %void None %3 + %5 = OpLabel + %18 = OpAccessChain %_ptr_StorageBuffer_int %_ %int_0 + %19 = OpLoad %int %18 + %20 = OpAccessChain %_ptr_StorageBuffer_int %_ %int_1 %int_1 %int_4 %int_1 + OpStore %20 %19 + OpReturn + OpFunctionEnd + )"; + + m_errorMonitor->SetDesiredError("VUID-VkShaderModuleCreateInfo-pCode-08737"); + auto cs = VkShaderObj::CreateFromASM(this, cs_src, VK_SHADER_STAGE_COMPUTE_BIT, SPV_ENV_VULKAN_1_2); + m_errorMonitor->VerifyFound(); +} + +TEST_F(NegativeShaderOOB, ConstantBufferFunction) { + SetTargetApiVersion(VK_API_VERSION_1_2); + RETURN_IF_SKIP(Init()); + + // layout(set = 0, binding = 0, std140) buffer Block1 { + // int i_data[4]; + // int o_data[4]; + // }; + // void foo() { + // o_data[4] = i_data[4]; + // } + // void main() { + // foo(); + // } + char const *cs_src = R"( + OpCapability Shader + OpMemoryModel Logical GLSL450 + OpEntryPoint GLCompute %main "main" %_ + OpExecutionMode %main LocalSize 1 1 1 + OpDecorate %_arr_int_uint_4 ArrayStride 16 + OpDecorate %_arr_int_uint_4_0 ArrayStride 16 + OpMemberDecorate %Block1 0 Offset 0 + OpMemberDecorate %Block1 1 Offset 64 + OpDecorate %Block1 Block + OpDecorate %_ DescriptorSet 0 + OpDecorate %_ Binding 0 + %void = OpTypeVoid + %3 = OpTypeFunction %void + %int = OpTypeInt 32 1 + %uint = OpTypeInt 32 0 + %uint_4 = OpConstant %uint 4 +%_arr_int_uint_4 = OpTypeArray %int %uint_4 +%_arr_int_uint_4_0 = OpTypeArray %int %uint_4 + %Block1 = OpTypeStruct %_arr_int_uint_4 %_arr_int_uint_4_0 +%_ptr_StorageBuffer_Block1 = OpTypePointer StorageBuffer %Block1 + %_ = OpVariable %_ptr_StorageBuffer_Block1 StorageBuffer + %int_1 = OpConstant %int 1 + %int_4 = OpConstant %int 4 + %int_0 = OpConstant %int 0 +%_ptr_StorageBuffer_int = OpTypePointer StorageBuffer %int + %main = OpFunction %void None %3 + %5 = OpLabel + %22 = OpFunctionCall %void %foo_ + OpReturn + OpFunctionEnd + %foo_ = OpFunction %void None %3 + %7 = OpLabel + %19 = OpAccessChain %_ptr_StorageBuffer_int %_ %int_0 %int_4 + %20 = OpLoad %int %19 + %21 = OpAccessChain %_ptr_StorageBuffer_int %_ %int_1 %int_4 + OpStore %21 %20 + OpReturn + OpFunctionEnd + )"; + + m_errorMonitor->SetDesiredError("VUID-VkShaderModuleCreateInfo-pCode-08737"); // i_data + m_errorMonitor->SetDesiredError("VUID-VkShaderModuleCreateInfo-pCode-08737"); // o_data + auto cs = VkShaderObj::CreateFromASM(this, cs_src, VK_SHADER_STAGE_COMPUTE_BIT, SPV_ENV_VULKAN_1_2); + m_errorMonitor->VerifyFound(); +}