Avoid undefined behaviour when getting debug opcode (#4842)

If the `instruction` operand in an extended instruction instruction is
too large, it causes undefined behaviour when that value is cast to the
enum for the corresponding set.  This is done with the
NonSemanticDebug100 instruction set.  We need to avoid the undefined
behaviour.

Fixes #4727
This commit is contained in:
Steven Perron 2022-07-05 14:14:29 -04:00 committed by GitHub
parent 6803cc5126
commit d5a3bfcf2f
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
2 changed files with 45 additions and 2 deletions

View File

@ -693,8 +693,12 @@ NonSemanticShaderDebugInfo100Instructions Instruction::GetShader100DebugOpcode()
return NonSemanticShaderDebugInfo100InstructionsMax;
}
return NonSemanticShaderDebugInfo100Instructions(
GetSingleWordInOperand(kExtInstInstructionInIdx));
uint32_t opcode = GetSingleWordInOperand(kExtInstInstructionInIdx);
if (opcode >= NonSemanticShaderDebugInfo100InstructionsMax) {
return NonSemanticShaderDebugInfo100InstructionsMax;
}
return NonSemanticShaderDebugInfo100Instructions(opcode);
}
CommonDebugInfoInstructions Instruction::GetCommonDebugOpcode() const {

View File

@ -1525,6 +1525,45 @@ OpFunctionEnd
EXPECT_EQ(false, inst->IsVulkanStorageTexelBuffer());
}
TEST_F(DescriptorTypeTest, GetShader100DebugOpcode) {
const std::string text = R"(
OpCapability Shader
%1 = OpExtInstImport "NonSemantic.Shader.DebugInfo.100"
%2 = OpString "ps.hlsl"
%3 = OpString "#line 1 \"ps.hlsl\""
%void = OpTypeVoid
%5 = OpExtInst %void %1 DebugExpression
%6 = OpExtInst %void %1 DebugSource %2 %3
)";
SetAssembleOptions(SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS);
std::unique_ptr<IRContext> context =
BuildModule(SPV_ENV_UNIVERSAL_1_2, nullptr, text);
Instruction* debug_expression = context->get_def_use_mgr()->GetDef(5);
EXPECT_EQ(debug_expression->GetShader100DebugOpcode(),
NonSemanticShaderDebugInfo100DebugExpression);
Instruction* debug_source = context->get_def_use_mgr()->GetDef(6);
EXPECT_EQ(debug_source->GetShader100DebugOpcode(),
NonSemanticShaderDebugInfo100DebugSource);
// Test that an opcode larger than the max will return Max. This instruction
// cannot be in the assembly above because the assembler expects the string
// for the opcode, so we cannot use an arbitrary number. However, a binary
// file could have an arbitrary number.
std::unique_ptr<Instruction> past_max(debug_expression->Clone(context.get()));
const uint32_t kExtInstOpcodeInIndex = 1;
uint32_t large_opcode = NonSemanticShaderDebugInfo100InstructionsMax + 2;
past_max->SetInOperand(kExtInstOpcodeInIndex, {large_opcode});
EXPECT_EQ(past_max->GetShader100DebugOpcode(),
NonSemanticShaderDebugInfo100InstructionsMax);
// Test that an opcode without a value in the enum, but less than Max returns
// the same value.
uint32_t opcode = NonSemanticShaderDebugInfo100InstructionsMax - 2;
past_max->SetInOperand(kExtInstOpcodeInIndex, {opcode});
EXPECT_EQ(past_max->GetShader100DebugOpcode(), opcode);
}
} // namespace
} // namespace opt
} // namespace spvtools