diff --git a/filament/backend/src/vulkan/memory/Resource.h b/filament/backend/src/vulkan/memory/Resource.h index 7db90e4af0f3..c856e62879ef 100644 --- a/filament/backend/src/vulkan/memory/Resource.h +++ b/filament/backend/src/vulkan/memory/Resource.h @@ -66,7 +66,8 @@ struct Resource { : resManager(nullptr), id(HandleBase::nullid), mCount(0), - restype(ResourceType::UNDEFINED_TYPE) {} + restype(ResourceType::UNDEFINED_TYPE), + mHandleConsideredDestroyed(false) {} private: inline void inc() noexcept { @@ -80,6 +81,16 @@ struct Resource { } } + // To be able to detect use-after-free, we need a bit to signify if the handle should be + // consider destroyed (from Filament's perspective). + inline void setHandleConsiderDestroyed() noexcept { + mHandleConsideredDestroyed = true; + } + + inline bool isHandleConsideredDestroyed() const { + return mHandleConsideredDestroyed; + } + template <typename T> inline void init(HandleId id, ResourceManager* resManager) { this->id = id; @@ -92,7 +103,9 @@ struct Resource { ResourceManager* resManager; // 8 HandleId id; // 4 uint32_t mCount : 24; - ResourceType restype : 6; // restype + mCount is 4 bytes. + ResourceType restype : 7; + bool mHandleConsideredDestroyed : 1; // restype + mCount + mHandleConsideredDestroyed + // is 4 bytes. friend class ResourceManager; @@ -105,7 +118,8 @@ struct ThreadSafeResource { : resManager(nullptr), id(HandleBase::nullid), mCount(0), - restype(ResourceType::UNDEFINED_TYPE) {} + restype(ResourceType::UNDEFINED_TYPE), + mHandleConsideredDestroyed(false) {} private: inline void inc() noexcept { @@ -118,6 +132,16 @@ struct ThreadSafeResource { } } + // To be able to detect use-after-free, we need a bit to signify if the handle should be + // consider destroyed (from Filament's perspective). + inline void setHandleConsiderDestroyed() noexcept { + mHandleConsideredDestroyed = true; + } + + inline bool isHandleConsideredDestroyed() const { + return mHandleConsideredDestroyed; + } + template <typename T> inline void init(HandleId id, ResourceManager* resManager) { this->id = id; @@ -130,7 +154,8 @@ struct ThreadSafeResource { ResourceManager* resManager; // 8 HandleId id; // 4 std::atomic<uint32_t> mCount; // 4 - ResourceType restype; // 1 + ResourceType restype : 7; + bool mHandleConsideredDestroyed : 1; // restype + mHandleConsideredDestroyed is 1 byte friend class ResourceManager; diff --git a/filament/backend/src/vulkan/memory/ResourcePointer.h b/filament/backend/src/vulkan/memory/ResourcePointer.h index 6f34b9252ab1..307d9d19934d 100644 --- a/filament/backend/src/vulkan/memory/ResourcePointer.h +++ b/filament/backend/src/vulkan/memory/ResourcePointer.h @@ -21,6 +21,7 @@ #include "vulkan/memory/ResourceManager.h" #include <backend/Handle.h> +#include <utils/compiler.h> #include <utility> @@ -59,6 +60,11 @@ struct resource_ptr { static enabled_resource_ptr<B> cast(ResourceManager* resManager, Handle<B> const& handle) noexcept { D* ptr = resManager->handle_cast<D*, B>(handle); + if (UTILS_UNLIKELY(ptr->isHandleConsideredDestroyed())) { + FILAMENT_CHECK_PRECONDITION(!ptr->isHandleConsideredDestroyed()) + << "Handle id=" << ptr->id << " (" << getTypeStr(ptr->restype) + << ") is being used after it has been freed"; + } return {ptr}; } @@ -156,6 +162,7 @@ struct resource_ptr { // only be used from VulkanDriver. inline void dec() { assert_invariant(mRef); + mRef->setHandleConsiderDestroyed(); mRef->dec(); }