Skip to content

Commit 02470f6

Browse files
authored
Validate duplicate decorations and execution modes (KhronosGroup#5641)
* Disallow duplicate decorations generally * Only FuncParamAttr and UserSemantic can be applied to the same target multiple times * Unchecked: completely duplicate UserSemantic and FuncParamAttr * Disallow duplicate execution modes generally * Exceptions for float controls, float controls2 and some intel execution modes * Fix invalid fuzzer transforms
1 parent 6761288 commit 02470f6

11 files changed

Lines changed: 265 additions & 45 deletions

source/fuzz/transformation_add_no_contraction_decoration.cpp

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,11 @@ bool TransformationAddNoContractionDecoration::IsApplicable(
3636
if (!instr) {
3737
return false;
3838
}
39+
// |instr| must not be decorated with NoContraction.
40+
if (ir_context->get_decoration_mgr()->HasDecoration(
41+
message_.result_id(), spv::Decoration::NoContraction)) {
42+
return false;
43+
}
3944
// The instruction must be arithmetic.
4045
return IsArithmetic(instr->opcode());
4146
}

source/fuzz/transformation_add_relaxed_decoration.cpp

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,11 @@ bool TransformationAddRelaxedDecoration::IsApplicable(
3636
if (!instr) {
3737
return false;
3838
}
39+
// |instr| must not be decorated with RelaxedPrecision.
40+
if (ir_context->get_decoration_mgr()->HasDecoration(
41+
message_.result_id(), spv::Decoration::RelaxedPrecision)) {
42+
return false;
43+
}
3944
opt::BasicBlock* cur_block = ir_context->get_instr_block(instr);
4045
// The instruction must have a block.
4146
if (cur_block == nullptr) {
@@ -46,6 +51,7 @@ bool TransformationAddRelaxedDecoration::IsApplicable(
4651
cur_block->id()))) {
4752
return false;
4853
}
54+
4955
// The instruction must be numeric.
5056
return IsNumeric(instr->opcode());
5157
}

source/val/validate.cpp

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -144,6 +144,9 @@ spv_result_t ValidateEntryPoints(ValidationState_t& _) {
144144
if (auto error = ValidateFloatControls2(_)) {
145145
return error;
146146
}
147+
if (auto error = ValidateDuplicateExecutionModes(_)) {
148+
return error;
149+
}
147150

148151
return SPV_SUCCESS;
149152
}

source/val/validate.h

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -94,6 +94,13 @@ spv_result_t ValidateInterfaces(ValidationState_t& _);
9494
/// @return SPV_SUCCESS if no errors are found.
9595
spv_result_t ValidateFloatControls2(ValidationState_t& _);
9696

97+
/// @brief Validates duplicated execution modes for each entry point.
98+
///
99+
/// @param[in] _ the validation state of the module
100+
///
101+
/// @return SPV_SUCCESS if no errors are found.
102+
spv_result_t ValidateDuplicateExecutionModes(ValidationState_t& _);
103+
97104
/// @brief Validates memory instructions
98105
///
99106
/// @param[in] _ the validation state of the module

source/val/validate_decorations.cpp

Lines changed: 3 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1325,21 +1325,14 @@ spv_result_t CheckDecorationsOfBuffers(ValidationState_t& vstate) {
13251325

13261326
// Returns true if |decoration| cannot be applied to the same id more than once.
13271327
bool AtMostOncePerId(spv::Decoration decoration) {
1328-
return decoration == spv::Decoration::ArrayStride;
1328+
return decoration != spv::Decoration::UserSemantic &&
1329+
decoration != spv::Decoration::FuncParamAttr;
13291330
}
13301331

13311332
// Returns true if |decoration| cannot be applied to the same member more than
13321333
// once.
13331334
bool AtMostOncePerMember(spv::Decoration decoration) {
1334-
switch (decoration) {
1335-
case spv::Decoration::Offset:
1336-
case spv::Decoration::MatrixStride:
1337-
case spv::Decoration::RowMajor:
1338-
case spv::Decoration::ColMajor:
1339-
return true;
1340-
default:
1341-
return false;
1342-
}
1335+
return decoration != spv::Decoration::UserSemantic;
13431336
}
13441337

13451338
spv_result_t CheckDecorationsCompatibility(ValidationState_t& vstate) {

source/val/validate_interfaces.cpp

Lines changed: 1 addition & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -254,37 +254,24 @@ spv_result_t GetLocationsForVariable(
254254
// equal. Also track Patch and PerTaskNV decorations.
255255
bool has_location = false;
256256
uint32_t location = 0;
257-
bool has_component = false;
258257
uint32_t component = 0;
259258
bool has_index = false;
260259
uint32_t index = 0;
261260
bool has_patch = false;
262261
bool has_per_task_nv = false;
263262
bool has_per_vertex_khr = false;
263+
// Duplicate Location, Component, Index are checked elsewhere.
264264
for (auto& dec : _.id_decorations(variable->id())) {
265265
if (dec.dec_type() == spv::Decoration::Location) {
266-
if (has_location && dec.params()[0] != location) {
267-
return _.diag(SPV_ERROR_INVALID_DATA, variable)
268-
<< "Variable has conflicting location decorations";
269-
}
270266
has_location = true;
271267
location = dec.params()[0];
272268
} else if (dec.dec_type() == spv::Decoration::Component) {
273-
if (has_component && dec.params()[0] != component) {
274-
return _.diag(SPV_ERROR_INVALID_DATA, variable)
275-
<< "Variable has conflicting component decorations";
276-
}
277-
has_component = true;
278269
component = dec.params()[0];
279270
} else if (dec.dec_type() == spv::Decoration::Index) {
280271
if (!is_output || !is_fragment) {
281272
return _.diag(SPV_ERROR_INVALID_DATA, variable)
282273
<< "Index can only be applied to Fragment output variables";
283274
}
284-
if (has_index && dec.params()[0] != index) {
285-
return _.diag(SPV_ERROR_INVALID_DATA, variable)
286-
<< "Variable has conflicting index decorations";
287-
}
288275
has_index = true;
289276
index = dec.params()[0];
290277
} else if (dec.dec_type() == spv::Decoration::BuiltIn) {

source/val/validate_mode_setting.cpp

Lines changed: 66 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -724,6 +724,25 @@ spv_result_t ValidateMemoryModel(ValidationState_t& _,
724724
return SPV_SUCCESS;
725725
}
726726

727+
bool PerEntryExecutionMode(spv::ExecutionMode mode) {
728+
switch (mode) {
729+
// These execution modes can be specified multiple times per entry point.
730+
case spv::ExecutionMode::DenormPreserve:
731+
case spv::ExecutionMode::DenormFlushToZero:
732+
case spv::ExecutionMode::SignedZeroInfNanPreserve:
733+
case spv::ExecutionMode::RoundingModeRTE:
734+
case spv::ExecutionMode::RoundingModeRTZ:
735+
case spv::ExecutionMode::FPFastMathDefault:
736+
case spv::ExecutionMode::RoundingModeRTPINTEL:
737+
case spv::ExecutionMode::RoundingModeRTNINTEL:
738+
case spv::ExecutionMode::FloatingPointModeALTINTEL:
739+
case spv::ExecutionMode::FloatingPointModeIEEEINTEL:
740+
return false;
741+
default:
742+
return true;
743+
}
744+
}
745+
727746
} // namespace
728747

729748
spv_result_t ValidateFloatControls2(ValidationState_t& _) {
@@ -808,5 +827,52 @@ spv_result_t ModeSettingPass(ValidationState_t& _, const Instruction* inst) {
808827
return SPV_SUCCESS;
809828
}
810829

830+
spv_result_t ValidateDuplicateExecutionModes(ValidationState_t& _) {
831+
using PerEntryKey = std::tuple<spv::ExecutionMode, uint32_t>;
832+
using PerOperandKey = std::tuple<spv::ExecutionMode, uint32_t, uint32_t>;
833+
std::set<PerEntryKey> seen_per_entry;
834+
std::set<PerOperandKey> seen_per_operand;
835+
836+
const auto lookupMode = [&_](spv::ExecutionMode mode) -> std::string {
837+
spv_operand_desc desc = nullptr;
838+
if (_.grammar().lookupOperand(SPV_OPERAND_TYPE_EXECUTION_MODE,
839+
static_cast<uint32_t>(mode),
840+
&desc) == SPV_SUCCESS) {
841+
return std::string(desc->name);
842+
}
843+
return "Unknown";
844+
};
845+
846+
for (const auto& inst : _.ordered_instructions()) {
847+
if (inst.opcode() != spv::Op::OpExecutionMode &&
848+
inst.opcode() != spv::Op::OpExecutionModeId) {
849+
continue;
850+
}
851+
852+
const auto entry = inst.GetOperandAs<uint32_t>(0);
853+
const auto mode = inst.GetOperandAs<spv::ExecutionMode>(1);
854+
if (PerEntryExecutionMode(mode)) {
855+
if (!seen_per_entry.insert(std::make_tuple(mode, entry)).second) {
856+
return _.diag(SPV_ERROR_INVALID_ID, &inst)
857+
<< lookupMode(mode)
858+
<< " execution mode must not be specified multiple times per "
859+
"entry point";
860+
}
861+
} else {
862+
// Execution modes allowed multiple times all take a single operand.
863+
const auto operand = inst.GetOperandAs<uint32_t>(2);
864+
if (!seen_per_operand.insert(std::make_tuple(mode, entry, operand))
865+
.second) {
866+
return _.diag(SPV_ERROR_INVALID_ID, &inst)
867+
<< lookupMode(mode)
868+
<< " execution mode must not be specified multiple times for "
869+
"the same entry point and operands";
870+
}
871+
}
872+
}
873+
874+
return SPV_SUCCESS;
875+
}
876+
811877
} // namespace val
812878
} // namespace spvtools

test/fuzz/transformation_add_no_contraction_decoration_test.cpp

Lines changed: 2 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -36,7 +36,6 @@ TEST(TransformationAddNoContractionDecorationTest, BasicScenarios) {
3636
OpName %8 "x"
3737
OpName %10 "y"
3838
OpName %14 "i"
39-
OpDecorate %32 NoContraction
4039
%2 = OpTypeVoid
4140
%3 = OpTypeFunction %2
4241
%6 = OpTypeFloat 32
@@ -110,9 +109,8 @@ TEST(TransformationAddNoContractionDecorationTest, BasicScenarios) {
110109
ASSERT_FALSE(TransformationAddNoContractionDecoration(24).IsApplicable(
111110
context.get(), transformation_context));
112111

113-
// It is valid to add NoContraction to each of these ids (and it's fine to
114-
// have duplicates of the decoration, in the case of 32).
115-
for (uint32_t result_id : {32u, 32u, 27u, 29u, 39u}) {
112+
// It is valid to add NoContraction to each of these ids.
113+
for (uint32_t result_id : {32u, 27u, 29u, 39u}) {
116114
TransformationAddNoContractionDecoration transformation(result_id);
117115
ASSERT_TRUE(
118116
transformation.IsApplicable(context.get(), transformation_context));
@@ -134,8 +132,6 @@ TEST(TransformationAddNoContractionDecorationTest, BasicScenarios) {
134132
OpName %10 "y"
135133
OpName %14 "i"
136134
OpDecorate %32 NoContraction
137-
OpDecorate %32 NoContraction
138-
OpDecorate %32 NoContraction
139135
OpDecorate %27 NoContraction
140136
OpDecorate %29 NoContraction
141137
OpDecorate %39 NoContraction

test/fuzz/transformation_add_relaxed_decoration_test.cpp

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -86,9 +86,8 @@ TEST(TransformationAddRelaxedDecorationTest, BasicScenarios) {
8686
// Invalid: 28 is in a dead block, but returns bool (not numeric).
8787
ASSERT_FALSE(TransformationAddRelaxedDecoration(28).IsApplicable(
8888
context.get(), transformation_context));
89-
// It is valid to add RelaxedPrecision to 25 (and it's fine to
90-
// have a duplicate).
91-
for (uint32_t result_id : {25u, 25u}) {
89+
// It is valid to add RelaxedPrecision to 25
90+
for (uint32_t result_id : {25u}) {
9291
TransformationAddRelaxedDecoration transformation(result_id);
9392
ASSERT_TRUE(
9493
transformation.IsApplicable(context.get(), transformation_context));
@@ -110,7 +109,6 @@ TEST(TransformationAddRelaxedDecorationTest, BasicScenarios) {
110109
OpName %10 "b"
111110
OpName %14 "c"
112111
OpDecorate %25 RelaxedPrecision
113-
OpDecorate %25 RelaxedPrecision
114112
%2 = OpTypeVoid
115113
%3 = OpTypeFunction %2
116114
%6 = OpTypeInt 32 1

test/val/val_interfaces_test.cpp

Lines changed: 14 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -419,9 +419,10 @@ OpFunctionEnd
419419
)";
420420

421421
CompileSuccessfully(text, SPV_ENV_VULKAN_1_0);
422-
EXPECT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions(SPV_ENV_VULKAN_1_0));
423-
EXPECT_THAT(getDiagnosticString(),
424-
HasSubstr("Variable has conflicting location decorations"));
422+
EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions(SPV_ENV_VULKAN_1_0));
423+
EXPECT_THAT(
424+
getDiagnosticString(),
425+
HasSubstr("decorated with Location multiple times is not allowed"));
425426
}
426427

427428
TEST_F(ValidateInterfacesTest, VulkanLocationsVariableAndMemberAssigned) {
@@ -505,9 +506,10 @@ OpFunctionEnd
505506
)";
506507

507508
CompileSuccessfully(text, SPV_ENV_VULKAN_1_0);
508-
EXPECT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions(SPV_ENV_VULKAN_1_0));
509-
EXPECT_THAT(getDiagnosticString(),
510-
HasSubstr("Member index 1 has conflicting location assignments"));
509+
EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions(SPV_ENV_VULKAN_1_0));
510+
EXPECT_THAT(
511+
getDiagnosticString(),
512+
HasSubstr("decorated with Location multiple times is not allowed"));
511513
}
512514

513515
TEST_F(ValidateInterfacesTest, VulkanLocationsMissingAssignmentStructMember) {
@@ -1157,9 +1159,10 @@ OpFunctionEnd
11571159
)";
11581160

11591161
CompileSuccessfully(text, SPV_ENV_VULKAN_1_0);
1160-
EXPECT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions(SPV_ENV_VULKAN_1_0));
1161-
EXPECT_THAT(getDiagnosticString(),
1162-
HasSubstr("Variable has conflicting component decorations"));
1162+
EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions(SPV_ENV_VULKAN_1_0));
1163+
EXPECT_THAT(
1164+
getDiagnosticString(),
1165+
HasSubstr("decorated with Component multiple times is not allowed"));
11631166
}
11641167

11651168
TEST_F(ValidateInterfacesTest,
@@ -1186,10 +1189,10 @@ OpFunctionEnd
11861189
)";
11871190

11881191
CompileSuccessfully(text, SPV_ENV_VULKAN_1_0);
1189-
EXPECT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions(SPV_ENV_VULKAN_1_0));
1192+
EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions(SPV_ENV_VULKAN_1_0));
11901193
EXPECT_THAT(
11911194
getDiagnosticString(),
1192-
HasSubstr("Member index 0 has conflicting component assignments"));
1195+
HasSubstr("decorated with Component multiple times is not allowed"));
11931196
}
11941197

11951198
TEST_F(ValidateInterfacesTest, VulkanLocationsVariableConflictOutputIndex1) {

0 commit comments

Comments
 (0)