mirror of
https://github.com/KhronosGroup/SPIRV-Tools
synced 2024-10-18 11:10:05 +00:00
Handle overflowing id in merge return (#4606)
If the ids overflow when creating an integer constant in the ir_builder, there will be a nullptr dereference. This is happening from inside merge return. We need to propagate the error up, and make sure it is handled appropriately.
This commit is contained in:
parent
97d4495600
commit
1082de6bb3
@ -359,8 +359,9 @@ class InstructionBuilder {
|
|||||||
return AddInstruction(std::move(select));
|
return AddInstruction(std::move(select));
|
||||||
}
|
}
|
||||||
|
|
||||||
// Adds a signed int32 constant to the binary.
|
// Returns a pointer to the definition of a signed 32-bit integer constant
|
||||||
// The |value| parameter is the constant value to be added.
|
// with the given value. Returns |nullptr| if the constant does not exist and
|
||||||
|
// cannot be created.
|
||||||
Instruction* GetSintConstant(int32_t value) {
|
Instruction* GetSintConstant(int32_t value) {
|
||||||
return GetIntConstant<int32_t>(value, true);
|
return GetIntConstant<int32_t>(value, true);
|
||||||
}
|
}
|
||||||
@ -381,21 +382,24 @@ class InstructionBuilder {
|
|||||||
GetContext()->TakeNextId(), ops));
|
GetContext()->TakeNextId(), ops));
|
||||||
return AddInstruction(std::move(construct));
|
return AddInstruction(std::move(construct));
|
||||||
}
|
}
|
||||||
// Adds an unsigned int32 constant to the binary.
|
|
||||||
// The |value| parameter is the constant value to be added.
|
// Returns a pointer to the definition of an unsigned 32-bit integer constant
|
||||||
|
// with the given value. Returns |nullptr| if the constant does not exist and
|
||||||
|
// cannot be created.
|
||||||
Instruction* GetUintConstant(uint32_t value) {
|
Instruction* GetUintConstant(uint32_t value) {
|
||||||
return GetIntConstant<uint32_t>(value, false);
|
return GetIntConstant<uint32_t>(value, false);
|
||||||
}
|
}
|
||||||
|
|
||||||
uint32_t GetUintConstantId(uint32_t value) {
|
uint32_t GetUintConstantId(uint32_t value) {
|
||||||
Instruction* uint_inst = GetUintConstant(value);
|
Instruction* uint_inst = GetUintConstant(value);
|
||||||
return uint_inst->result_id();
|
return (uint_inst != nullptr ? uint_inst->result_id() : 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Adds either a signed or unsigned 32 bit integer constant to the binary
|
// Adds either a signed or unsigned 32 bit integer constant to the binary
|
||||||
// depedning on the |sign|. If |sign| is true then the value is added as a
|
// depending on the |sign|. If |sign| is true then the value is added as a
|
||||||
// signed constant otherwise as an unsigned constant. If |sign| is false the
|
// signed constant otherwise as an unsigned constant. If |sign| is false the
|
||||||
// value must not be a negative number.
|
// value must not be a negative number. Returns false if the constant does
|
||||||
|
// not exists and could be be created.
|
||||||
template <typename T>
|
template <typename T>
|
||||||
Instruction* GetIntConstant(T value, bool sign) {
|
Instruction* GetIntConstant(T value, bool sign) {
|
||||||
// Assert that we are not trying to store a negative number in an unsigned
|
// Assert that we are not trying to store a negative number in an unsigned
|
||||||
@ -411,6 +415,10 @@ class InstructionBuilder {
|
|||||||
uint32_t type_id =
|
uint32_t type_id =
|
||||||
GetContext()->get_type_mgr()->GetTypeInstruction(&int_type);
|
GetContext()->get_type_mgr()->GetTypeInstruction(&int_type);
|
||||||
|
|
||||||
|
if (type_id == 0) {
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
// Get the memory managed type so that it is safe to be stored by
|
// Get the memory managed type so that it is safe to be stored by
|
||||||
// GetConstant.
|
// GetConstant.
|
||||||
analysis::Type* rebuilt_type =
|
analysis::Type* rebuilt_type =
|
||||||
|
@ -111,7 +111,9 @@ bool MergeReturnPass::ProcessStructured(
|
|||||||
}
|
}
|
||||||
|
|
||||||
RecordImmediateDominators(function);
|
RecordImmediateDominators(function);
|
||||||
AddSingleCaseSwitchAroundFunction();
|
if (!AddSingleCaseSwitchAroundFunction()) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
std::list<BasicBlock*> order;
|
std::list<BasicBlock*> order;
|
||||||
cfg()->ComputeStructuredOrder(function, &*function->begin(), &order);
|
cfg()->ComputeStructuredOrder(function, &*function->begin(), &order);
|
||||||
@ -770,7 +772,7 @@ void MergeReturnPass::InsertAfterElement(BasicBlock* element,
|
|||||||
list->insert(pos, new_element);
|
list->insert(pos, new_element);
|
||||||
}
|
}
|
||||||
|
|
||||||
void MergeReturnPass::AddSingleCaseSwitchAroundFunction() {
|
bool MergeReturnPass::AddSingleCaseSwitchAroundFunction() {
|
||||||
CreateReturnBlock();
|
CreateReturnBlock();
|
||||||
CreateReturn(final_return_block_);
|
CreateReturn(final_return_block_);
|
||||||
|
|
||||||
@ -778,7 +780,10 @@ void MergeReturnPass::AddSingleCaseSwitchAroundFunction() {
|
|||||||
cfg()->RegisterBlock(final_return_block_);
|
cfg()->RegisterBlock(final_return_block_);
|
||||||
}
|
}
|
||||||
|
|
||||||
CreateSingleCaseSwitch(final_return_block_);
|
if (!CreateSingleCaseSwitch(final_return_block_)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
BasicBlock* MergeReturnPass::CreateContinueTarget(uint32_t header_label_id) {
|
BasicBlock* MergeReturnPass::CreateContinueTarget(uint32_t header_label_id) {
|
||||||
@ -813,7 +818,7 @@ BasicBlock* MergeReturnPass::CreateContinueTarget(uint32_t header_label_id) {
|
|||||||
return new_block;
|
return new_block;
|
||||||
}
|
}
|
||||||
|
|
||||||
void MergeReturnPass::CreateSingleCaseSwitch(BasicBlock* merge_target) {
|
bool MergeReturnPass::CreateSingleCaseSwitch(BasicBlock* merge_target) {
|
||||||
// Insert the switch before any code is run. We have to split the entry
|
// Insert the switch before any code is run. We have to split the entry
|
||||||
// block to make sure the OpVariable instructions remain in the entry block.
|
// block to make sure the OpVariable instructions remain in the entry block.
|
||||||
BasicBlock* start_block = &*function_->begin();
|
BasicBlock* start_block = &*function_->begin();
|
||||||
@ -830,13 +835,17 @@ void MergeReturnPass::CreateSingleCaseSwitch(BasicBlock* merge_target) {
|
|||||||
context(), start_block,
|
context(), start_block,
|
||||||
IRContext::kAnalysisDefUse | IRContext::kAnalysisInstrToBlockMapping);
|
IRContext::kAnalysisDefUse | IRContext::kAnalysisInstrToBlockMapping);
|
||||||
|
|
||||||
builder.AddSwitch(builder.GetUintConstantId(0u), old_block->id(), {},
|
uint32_t const_zero_id = builder.GetUintConstantId(0u);
|
||||||
merge_target->id());
|
if (const_zero_id == 0) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
builder.AddSwitch(const_zero_id, old_block->id(), {}, merge_target->id());
|
||||||
|
|
||||||
if (context()->AreAnalysesValid(IRContext::kAnalysisCFG)) {
|
if (context()->AreAnalysesValid(IRContext::kAnalysisCFG)) {
|
||||||
cfg()->RegisterBlock(old_block);
|
cfg()->RegisterBlock(old_block);
|
||||||
cfg()->AddEdges(start_block);
|
cfg()->AddEdges(start_block);
|
||||||
}
|
}
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool MergeReturnPass::HasNontrivialUnreachableBlocks(Function* function) {
|
bool MergeReturnPass::HasNontrivialUnreachableBlocks(Function* function) {
|
||||||
|
@ -277,7 +277,7 @@ class MergeReturnPass : public MemPass {
|
|||||||
// current function where the switch and case value are both zero and the
|
// current function where the switch and case value are both zero and the
|
||||||
// default is the merge block. Returns after the switch is executed. Sets
|
// default is the merge block. Returns after the switch is executed. Sets
|
||||||
// |final_return_block_|.
|
// |final_return_block_|.
|
||||||
void AddSingleCaseSwitchAroundFunction();
|
bool AddSingleCaseSwitchAroundFunction();
|
||||||
|
|
||||||
// Creates a new basic block that branches to |header_label_id|. Returns the
|
// Creates a new basic block that branches to |header_label_id|. Returns the
|
||||||
// new basic block. The block will be the second last basic block in the
|
// new basic block. The block will be the second last basic block in the
|
||||||
@ -286,7 +286,7 @@ class MergeReturnPass : public MemPass {
|
|||||||
|
|
||||||
// Creates a one case switch around the executable code of the function with
|
// Creates a one case switch around the executable code of the function with
|
||||||
// |merge_target| as the merge node.
|
// |merge_target| as the merge node.
|
||||||
void CreateSingleCaseSwitch(BasicBlock* merge_target);
|
bool CreateSingleCaseSwitch(BasicBlock* merge_target);
|
||||||
|
|
||||||
// Returns true if |function| has an unreachable block that is not a continue
|
// Returns true if |function| has an unreachable block that is not a continue
|
||||||
// target that simply branches back to the header, or a merge block containing
|
// target that simply branches back to the header, or a merge block containing
|
||||||
|
@ -2567,6 +2567,39 @@ TEST_F(MergeReturnPassTest, ChainedPointerUsedAfterLoop) {
|
|||||||
SinglePassRunAndMatch<MergeReturnPass>(before, true);
|
SinglePassRunAndMatch<MergeReturnPass>(before, true);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
TEST_F(MergeReturnPassTest, OverflowTest1) {
|
||||||
|
const std::string text =
|
||||||
|
R"(
|
||||||
|
; CHECK: OpReturn
|
||||||
|
; CHECK-NOT: OpReturn
|
||||||
|
; CHECK: OpFunctionEnd
|
||||||
|
OpCapability ClipDistance
|
||||||
|
OpMemoryModel Logical GLSL450
|
||||||
|
OpEntryPoint Fragment %2 "main"
|
||||||
|
OpExecutionMode %2 OriginUpperLeft
|
||||||
|
%void = OpTypeVoid
|
||||||
|
%6 = OpTypeFunction %void
|
||||||
|
%2 = OpFunction %void None %6
|
||||||
|
%4194303 = OpLabel
|
||||||
|
OpBranch %18
|
||||||
|
%18 = OpLabel
|
||||||
|
OpLoopMerge %19 %20 None
|
||||||
|
OpBranch %21
|
||||||
|
%21 = OpLabel
|
||||||
|
OpReturn
|
||||||
|
%20 = OpLabel
|
||||||
|
OpBranch %18
|
||||||
|
%19 = OpLabel
|
||||||
|
OpUnreachable
|
||||||
|
OpFunctionEnd
|
||||||
|
)";
|
||||||
|
|
||||||
|
SetAssembleOptions(SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS);
|
||||||
|
auto result =
|
||||||
|
SinglePassRunToBinary<MergeReturnPass>(text, /* skip_nop = */ true);
|
||||||
|
EXPECT_EQ(Pass::Status::Failure, std::get<1>(result));
|
||||||
|
}
|
||||||
|
|
||||||
} // namespace
|
} // namespace
|
||||||
} // namespace opt
|
} // namespace opt
|
||||||
} // namespace spvtools
|
} // namespace spvtools
|
||||||
|
Loading…
Reference in New Issue
Block a user