Retain pipeline layouts in bind/push descriptor commands.
Fixes a use-after-free bug when the pipeline layout is destroyed after
recording--e.g. in the
`dEQP-VK.api.pipeline_layout.lifetime.destroy_after_end` test.
diff --git a/MoltenVK/MoltenVK/Commands/MVKCmdPipeline.h b/MoltenVK/MoltenVK/Commands/MVKCmdPipeline.h
index b2985ee..98616cb 100644
--- a/MoltenVK/MoltenVK/Commands/MVKCmdPipeline.h
+++ b/MoltenVK/MoltenVK/Commands/MVKCmdPipeline.h
@@ -139,11 +139,13 @@
void encode(MVKCommandEncoder* cmdEncoder) override;
+ ~MVKCmdBindDescriptorSetsStatic() override;
+
protected:
MVKCommandTypePool<MVKCommand>* getTypePool(MVKCommandPool* cmdPool) override;
MVKSmallVector<MVKDescriptorSet*, N> _descriptorSets;
- MVKPipelineLayout* _pipelineLayout;
+ MVKPipelineLayout* _pipelineLayout = nullptr;
VkPipelineBindPoint _pipelineBindPoint;
uint32_t _firstSet;
};
@@ -211,7 +213,6 @@
MVKCommandTypePool<MVKCommand>* getTypePool(MVKCommandPool* cmdPool) override;
MVKSmallVector<char, N> _pushConstants;
- MVKPipelineLayout* _pipelineLayout;
VkShaderStageFlags _stageFlags;
uint32_t _offset;
};
@@ -245,7 +246,7 @@
void clearDescriptorWrites();
MVKSmallVector<VkWriteDescriptorSet, 1> _descriptorWrites;
- MVKPipelineLayout* _pipelineLayout;
+ MVKPipelineLayout* _pipelineLayout = nullptr;
VkPipelineBindPoint _pipelineBindPoint;
uint32_t _set;
};
@@ -272,7 +273,7 @@
MVKCommandTypePool<MVKCommand>* getTypePool(MVKCommandPool* cmdPool) override;
MVKDescriptorUpdateTemplate* _descUpdateTemplate;
- MVKPipelineLayout* _pipelineLayout;
+ MVKPipelineLayout* _pipelineLayout = nullptr;
void* _pData = nullptr;
uint32_t _set;
};
diff --git a/MoltenVK/MoltenVK/Commands/MVKCmdPipeline.mm b/MoltenVK/MoltenVK/Commands/MVKCmdPipeline.mm
index 4e12de5..00029a6 100644
--- a/MoltenVK/MoltenVK/Commands/MVKCmdPipeline.mm
+++ b/MoltenVK/MoltenVK/Commands/MVKCmdPipeline.mm
@@ -193,10 +193,14 @@
uint32_t firstSet,
uint32_t setCount,
const VkDescriptorSet* pDescriptorSets) {
+ if (_pipelineLayout) { _pipelineLayout->release(); }
+
_pipelineBindPoint = pipelineBindPoint;
_pipelineLayout = (MVKPipelineLayout*)layout;
_firstSet = firstSet;
+ _pipelineLayout->retain();
+
// Add the descriptor sets
_descriptorSets.clear(); // Clear for reuse
_descriptorSets.reserve(setCount);
@@ -212,6 +216,11 @@
_pipelineLayout->bindDescriptorSets(cmdEncoder, _descriptorSets.contents(), _firstSet, MVKArrayRef<uint32_t>());
}
+template <size_t N>
+MVKCmdBindDescriptorSetsStatic<N>::~MVKCmdBindDescriptorSetsStatic() {
+ if (_pipelineLayout) { _pipelineLayout->release(); }
+}
+
template class MVKCmdBindDescriptorSetsStatic<1>;
template class MVKCmdBindDescriptorSetsStatic<4>;
template class MVKCmdBindDescriptorSetsStatic<8>;
@@ -262,7 +271,6 @@
uint32_t offset,
uint32_t size,
const void* pValues) {
- _pipelineLayout = (MVKPipelineLayout*)layout;
_stageFlags = stageFlags;
_offset = offset;
@@ -302,10 +310,14 @@
uint32_t set,
uint32_t descriptorWriteCount,
const VkWriteDescriptorSet* pDescriptorWrites) {
+ if (_pipelineLayout) { _pipelineLayout->release(); }
+
_pipelineBindPoint = pipelineBindPoint;
_pipelineLayout = (MVKPipelineLayout*)layout;
_set = set;
+ _pipelineLayout->retain();
+
// Add the descriptor writes
MVKDevice* mvkDvc = cmdBuff->getDevice();
clearDescriptorWrites(); // Clear for reuse
@@ -360,6 +372,7 @@
MVKCmdPushDescriptorSet::~MVKCmdPushDescriptorSet() {
clearDescriptorWrites();
+ if (_pipelineLayout) { _pipelineLayout->release(); }
}
void MVKCmdPushDescriptorSet::clearDescriptorWrites() {
@@ -393,9 +406,14 @@
VkPipelineLayout layout,
uint32_t set,
const void* pData) {
+ if (_pipelineLayout) { _pipelineLayout->release(); }
+
_descUpdateTemplate = (MVKDescriptorUpdateTemplate*)descUpdateTemplate;
_pipelineLayout = (MVKPipelineLayout*)layout;
_set = set;
+
+ _pipelineLayout->retain();
+
if (_pData) delete[] (char*)_pData;
// Work out how big the memory block in pData is.
const VkDescriptorUpdateTemplateEntryKHR* pEntry =
@@ -443,6 +461,7 @@
}
MVKCmdPushDescriptorSetWithTemplate::~MVKCmdPushDescriptorSetWithTemplate() {
+ if (_pipelineLayout) { _pipelineLayout->release(); }
if (_pData) delete[] (char*)_pData;
}