Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions BUILD.gn
Original file line number Diff line number Diff line change
Expand Up @@ -215,6 +215,7 @@ vvl_sources = [
"layers/gpuav/instrumentation/descriptor_checks.h",
"layers/gpuav/instrumentation/mesh_shading.cpp",
"layers/gpuav/instrumentation/post_process_descriptor_indexing.cpp",
"layers/gpuav/instrumentation/poison_value.cpp",
"layers/gpuav/instrumentation/ray_query.cpp",
"layers/gpuav/instrumentation/ray_hit_object.cpp",
"layers/gpuav/instrumentation/register_validation.h",
Expand Down Expand Up @@ -242,6 +243,8 @@ vvl_sources = [
"layers/gpuav/spirv/buffer_device_address_pass.h",
"layers/gpuav/spirv/mesh_shading_pass.cpp",
"layers/gpuav/spirv/mesh_shading_pass.h",
"layers/gpuav/spirv/poison_pass.cpp",
"layers/gpuav/spirv/poison_pass.h",
"layers/gpuav/spirv/post_process_descriptor_indexing_pass.cpp",
"layers/gpuav/spirv/post_process_descriptor_indexing_pass.h",
"layers/gpuav/spirv/vertex_attribute_fetch_oob_pass.cpp",
Expand Down
1 change: 1 addition & 0 deletions layers/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -340,6 +340,7 @@ target_sources(vvl PRIVATE
gpuav/instrumentation/descriptor_checks.cpp
gpuav/instrumentation/mesh_shading.cpp
gpuav/instrumentation/post_process_descriptor_indexing.cpp
gpuav/instrumentation/poison_value.cpp
gpuav/instrumentation/ray_query.cpp
gpuav/instrumentation/ray_hit_object.cpp
gpuav/instrumentation/register_validation.h
Expand Down
15 changes: 15 additions & 0 deletions layers/VkLayer_khronos_validation.json.in
Original file line number Diff line number Diff line change
Expand Up @@ -903,6 +903,21 @@
]
}
},
{
"key": "gpuav_poison_value",
"label": "Poison value (uninitialized variable detection)",
"description": "Instrument shaders to detect uses of uninitialized values.",
"type": "BOOL",
"view": "DEBUG",
"default": true,
"dependence": {
"mode": "ALL",
"settings": [
{ "key": "gpuav_enable", "value": true },
{ "key": "gpuav_shader_instrumentation", "value": true }
]
}
},
{
"key": "gpuav_shared_memory_data_race",
"label": "Shared Memory Data Race Detection",
Expand Down
1 change: 1 addition & 0 deletions layers/gpuav/core/gpuav_record.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -167,6 +167,7 @@ void Validator::PreCallRecordBeginCommandBuffer(VkCommandBuffer commandBuffer, c
RegisterRayQueryValidation(*this, gpuav_cb_state);
RegisterRayHitObjectValidation(*this, gpuav_cb_state);
RegisterSharedMemoryDataRaceValidation(*this, gpuav_cb_state);
RegisterPoisonValueValidation(*this, gpuav_cb_state);
RegisterSanitizer(*this, gpuav_cb_state);
debug_printf::RegisterDebugPrintf(*this, gpuav_cb_state);
debug_descriptor::RegisterDebugDescriptor(*this, gpuav_cb_state);
Expand Down
5 changes: 4 additions & 1 deletion layers/gpuav/core/gpuav_settings.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,8 @@ bool GpuAVSettings::IsShaderInstrumentationEnabled() const {
return shader_instrumentation.descriptor_checks || shader_instrumentation.buffer_device_address ||
shader_instrumentation.ray_query || shader_instrumentation.ray_hit_object || shader_instrumentation.mesh_shading ||
shader_instrumentation.post_process_descriptor_indexing || shader_instrumentation.vertex_attribute_fetch_oob ||
shader_instrumentation.sanitizer || shader_instrumentation.shared_memory_data_race;
shader_instrumentation.poison_value || shader_instrumentation.sanitizer ||
shader_instrumentation.shared_memory_data_race;
}
bool GpuAVSettings::IsSpirvModified() const {
return IsShaderInstrumentationEnabled() || debug_printf_enabled || debug_descriptor_enabled;
Expand All @@ -48,6 +49,7 @@ void GpuAVSettings::DisableShaderInstrumentationAndOptions() {
shader_instrumentation.mesh_shading = false;
shader_instrumentation.post_process_descriptor_indexing = false;
shader_instrumentation.vertex_attribute_fetch_oob = false;
shader_instrumentation.poison_value = false;
shader_instrumentation.sanitizer = false;
shader_instrumentation.shared_memory_data_race = false;
// Because of this setting, cannot really have an "enabled" parameter to pass to this method
Expand Down Expand Up @@ -132,6 +134,7 @@ void GpuAVSettings::TracyLogSettings() const {
VVL_TRACY_PRINT_INSTRUMENTATION_SETTING(mesh_shading);
VVL_TRACY_PRINT_INSTRUMENTATION_SETTING(post_process_descriptor_indexing);
VVL_TRACY_PRINT_INSTRUMENTATION_SETTING(vertex_attribute_fetch_oob);
VVL_TRACY_PRINT_INSTRUMENTATION_SETTING(poison_value);
VVL_TRACY_PRINT_INSTRUMENTATION_SETTING(sanitizer);
VVL_TRACY_PRINT_INSTRUMENTATION_SETTING(shared_memory_data_race);
VVL_TRACY_PRINT_GPUAV_SETTING(debug_printf_only);
Expand Down
1 change: 1 addition & 0 deletions layers/gpuav/core/gpuav_settings.h
Original file line number Diff line number Diff line change
Expand Up @@ -66,6 +66,7 @@ struct GpuAVSettings {
bool mesh_shading = true;
bool post_process_descriptor_indexing = true;
bool vertex_attribute_fetch_oob = true;
bool poison_value = true;
bool sanitizer = true;
bool shared_memory_data_race = true;
} shader_instrumentation;
Expand Down
9 changes: 9 additions & 0 deletions layers/gpuav/instrumentation/gpuav_shader_instrumentor.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@
#include "gpuav/shaders/gpuav_error_codes.h"
#include "gpuav/shaders/gpuav_error_header.h"
#include "gpuav/spirv/log_error_pass.h"
#include "gpuav/spirv/poison_pass.h"
#include "error_message/spirv_logging.h"
#include <spirv/unified1/NonSemanticShaderDebugInfo100.h>
#include <spirv/unified1/spirv.hpp>
Expand Down Expand Up @@ -1637,6 +1638,14 @@ bool GpuShaderInstrumentor::InstrumentShader(const vvl::span<const uint32_t>& in

bool modified = false;

// Poison pass runs first so its checks are outermost: later passes' if/else guards end up
// inside the poison conditional, meaning root-cause "uninitialized value" errors are reported
// before downstream checks (e.g. BDA OOB) that would mask them.
if (gpuav_settings.shader_instrumentation.poison_value) {
spirv::PoisonPass pass(module);
modified |= pass.Run();
}

// If descriptor indexing is enabled, enable length checks and updated descriptor checks
if (gpuav_settings.shader_instrumentation.descriptor_checks) {
if (interface.descriptor_mode == vvl::DescriptorModeClassic) {
Expand Down
95 changes: 95 additions & 0 deletions layers/gpuav/instrumentation/poison_value.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,95 @@
/* Copyright (c) 2026 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
*
* 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 <spirv/unified1/spirv.hpp>
#include "generated/spirv_grammar_helper.h"
#include "gpuav/core/gpuav.h"
#include "gpuav/resources/gpuav_state_trackers.h"
#include "gpuav/shaders/gpuav_error_codes.h"
#include "gpuav/shaders/gpuav_error_header.h"

namespace gpuav {

void RegisterPoisonValueValidation(Validator& gpuav, CommandBufferSubState& cb) {
if (!gpuav.gpuav_settings.shader_instrumentation.poison_value) {
return;
}

cb.on_instrumentation_error_logger_register_functions.emplace_back(
[](Validator& gpuav, CommandBufferSubState& cb, const LastBound& last_bound) {
CommandBufferSubState::InstrumentationErrorLogger inst_error_logger =
[](Validator& gpuav, const Location& loc, const uint32_t* error_record,
const InstrumentedShader* instrumented_shader, std::string& out_error_msg, std::string& out_vuid_msg) {
using namespace glsl;
bool error_found = false;
if (GetErrorGroup(error_record) != kErrorGroup_InstPoisonValue) {
return error_found;
}
error_found = true;

const uint32_t error_sub_code = GetSubError(error_record);
std::ostringstream strm;

switch (error_sub_code) {
case kErrorSubCode_PoisonValue_UninitializedVariable: {
const uint32_t trigger_opcode = error_record[kInst_LogError_ParameterOffset_0];

if (trigger_opcode == spv::OpBranchConditional || trigger_opcode == spv::OpSwitch) {
strm << "Undefined behavior: poison value used in " << string_SpvOpcode(trigger_opcode) << ".";
strm << " A poison value was used as a branch condition.";
out_vuid_msg = "SPIRV-PoisonValue-BranchOnPoison";
} else if (trigger_opcode == spv::OpAccessChain || trigger_opcode == spv::OpInBoundsAccessChain ||
trigger_opcode == spv::OpPtrAccessChain) {
strm << "Undefined behavior: poison value used in " << string_SpvOpcode(trigger_opcode) << ".";
strm << " A poison value was used as an index in an access chain.";
out_vuid_msg = "SPIRV-PoisonValue-AddressFromPoison";
} else if (trigger_opcode == spv::OpStore) {
strm << "Warning: poison value stored to an externally-visible variable"
<< " (not undefined behavior per the SPIR-V spec, but likely an application bug).";
out_vuid_msg = "SPIRV-PoisonValue-ExternalStoreOfPoison";
} else if (trigger_opcode == spv::OpReturnValue) {
strm << "Warning: poison value returned from a function"
<< " (not undefined behavior per the SPIR-V spec, but likely an application bug).";
out_vuid_msg = "SPIRV-PoisonValue-ReturnOfPoison";
} else {
assert(false && "unexpected opcode for poison value error");
out_vuid_msg = "SPIRV-PoisonValue-UninitializedVariable";
}
} break;
case kErrorSubCode_PoisonValue_StoreToFunctionParam: {
strm << "Warning: poison value stored to a function out/inout parameter"
<< " (not undefined behavior per the SPIR-V spec, but likely an application bug).";
out_vuid_msg = "SPIRV-PoisonValue-StoreToFunctionParam";
} break;
case kErrorSubCode_PoisonValue_PoisonPointerDereference: {
const uint32_t trigger_opcode = error_record[kInst_LogError_ParameterOffset_0];
strm << "Undefined behavior: dereference of a poison pointer in " << string_SpvOpcode(trigger_opcode)
<< ".";
out_vuid_msg = "SPIRV-PoisonValue-PoisonPointerDereference";
} break;
default:
error_found = false;
break;
}

out_error_msg += strm.str();
return error_found;
};

return inst_error_logger;
});
}

} // namespace gpuav
1 change: 1 addition & 0 deletions layers/gpuav/instrumentation/register_validation.h
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ void RegisterPostProcessingValidation(Validator& gpuav, CommandBufferSubState& c
void RegisterRayQueryValidation(Validator& gpuav, CommandBufferSubState& cb);
void RegisterRayHitObjectValidation(Validator& gpuav, CommandBufferSubState& cb);
void RegisterMeshShadingValidation(Validator& gpuav, CommandBufferSubState& cb);
void RegisterPoisonValueValidation(Validator& gpuav, CommandBufferSubState& cb);
void RegisterSanitizer(Validator& gpuav, CommandBufferSubState& cb);
void RegisterVertexAttributeFetchOobValidation(Validator& gpuav, CommandBufferSubState& cb);
void RegisterSharedMemoryDataRaceValidation(Validator& gpuav, CommandBufferSubState& cb);
Expand Down
7 changes: 7 additions & 0 deletions layers/gpuav/shaders/gpuav_error_codes.h
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,7 @@ const int kErrorGroup_GpuPreBuildAccelerationStructures = 12;
const int kErrorGroup_InstMeshShading = 13;
const int kErrorGroup_InstRayHitObject = 14;
const int kErrorGroup_SharedMemoryDataRace = 15;
const int kErrorGroup_InstPoisonValue = 16;

// We just take ExecutionModel and normalize it so we only use 5 bits to store it
const int kExecutionModel_Vertex = 0;
Expand Down Expand Up @@ -120,6 +121,12 @@ const int kErrorSubCode_RayHitObject_SkipAABBsWithPipelineSkipTriangles = 14;
const int kErrorSubCode_RayHitObject_TimeOutOfRange = 15;
const int kErrorSubCode_RayHitObject_SBTIndexExceedsLimit = 16;

// Poison value (uninitialized / poisoned SSA uses)
//
const int kErrorSubCode_PoisonValue_UninitializedVariable = 1;
const int kErrorSubCode_PoisonValue_StoreToFunctionParam = 2;
const int kErrorSubCode_PoisonValue_PoisonPointerDereference = 3;

// Shared Memory Data Race
const int kErrorSubCode_SharedMemoryDataRace_RaceOnStore = 1;
const int kErrorSubCode_SharedMemoryDataRace_RaceOnLoad = 2;
Expand Down
31 changes: 31 additions & 0 deletions layers/gpuav/shaders/instrumentation/poison_value.comp
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
// Copyright (c) 2026 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
//
// 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.

// NOTE: This file doesn't contain any entrypoints and should be compiled with the --no-link option for glslang

#version 450
#extension GL_GOOGLE_include_directive : enable
#include "common_descriptor_sets.h"
#include "error_payload.h"

void inst_poison_report(const bool is_poison, const uint inst_offset, const uint trigger_opcode, const uint error_sub_code) {
if (is_poison) {
error_payload = ErrorPayload(
inst_offset,
SpecConstantLinkShaderId | (kErrorGroup_InstPoisonValue << kErrorGroup_Shift) | (error_sub_code << kErrorSubCode_Shift),
trigger_opcode,
0,
0);
}
}
2 changes: 2 additions & 0 deletions layers/gpuav/spirv/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,8 @@ target_sources(gpu_av_spirv PRIVATE
ray_hit_object_pass.cpp
sanitizer_pass.h
sanitizer_pass.cpp
poison_pass.h
poison_pass.cpp
shared_memory_data_race_pass.h
shared_memory_data_race_pass.cpp
debug_descriptor_pass.h
Expand Down
Loading