Describe the bug
In 1.75.1, once two or more filament::Engine instances have been alive at the same time, the
second Engine::destroy() crashes. 1.67.0 is fine. Reproduced on Linux and Windows with a
program that does nothing but create and destroy engines: no meshes, materials, rendering or swap
chains, and destroy order is irrelevant.
Cause: VulkanDriver::terminate() deletes the process-wide static DebugUtils::mSingleton
unconditionally. In 1.67.0 that delete sat inside #if FVK_ENABLED(FVK_DEBUG_DEBUG_UTILS), so it
never ran in a release build.
1.67.0:
#if FVK_ENABLED(FVK_DEBUG_DEBUG_UTILS)
assert_invariant(DebugUtils::mSingleton);
delete DebugUtils::mSingleton;
#endif
1.75.1:
assert_invariant(DebugUtils::mSingleton);
delete DebugUtils::mSingleton;
delete does not null the pointer and DebugUtils::get() never recreates it, so the second
engine's terminate() deletes it again. ~DebugUtils() then reads mInstance and
mDebugMessenger out of the freed object and calls vkDestroyDebugUtilsMessengerEXT. That gives
either a detected double free or a call through a garbage pointer, which is the two signatures
below. Since the state is global, only the NUMBER of engines matters, not the order.
Introduced by c2341d1 "vk: use debug flag to enable debug utils names (#10220)". That PR moved
debug-utils naming from compile-time guards to a runtime flag and removed
#if FVK_ENABLED(FVK_DEBUG_DEBUG_UTILS) from every site in the file, including this delete, which
was only safe because it was compiled out of release builds.
To Reproduce
#include <filament/Engine.h>
#include <vector>
int main(int argc, char** argv) {
const int n = argc > 1 ? atoi(argv[1]) : 1;
std::vector<filament::Engine*> engines;
for (int i = 0; i < n; ++i)
engines.push_back(filament::Engine::create(filament::Engine::Backend::VULKAN));
for (int i = 0; i < n; ++i)
filament::Engine::destroy(&engines[i]); // crashes on i == 1
return 0;
}
Run with an argument of 2 or more.
Expected behavior
Destroying several engines succeeds, as it does in 1.67.0. Possible fixes: null the pointer after
deleting, reference-count the singleton so only the last terminate() frees it, or gate the
delete on the same runtime flag the rest of the feature now uses.
Screenshots
n/a
Logs
| engines alive |
1.65.0 Windows |
1.75.1 Windows |
1.67.0 Linux |
1.75.1 Linux |
| 1 |
ok |
ok |
ok |
ok |
| 2 |
ok |
0xC0000374 |
ok |
SIGSEGV |
| 3 |
ok |
0xC0000005 |
ok |
SIGSEGV |
| 4 |
ok |
0xC0000374 |
ok |
SIGSEGV |
0xC0000374 is STATUS_HEAP_CORRUPTION, 0xC0000005 an access violation. Sequential
create-then-destroy, never two alive, is always clean.
double free or corruption (out)
Thread "FEngine::loop" received signal SIGABRT
#6 malloc_printerr (str="double free or corruption (out)")
#9 __GI___libc_free (mem=0x7ffabc057df0)
#10 filament::backend::VulkanDriver::terminate ()
#11 filament::FEngine::loop ()
The other signature is the same frame reached through a null pointer:
Thread "FEngine::loop" received signal SIGSEGV
#0 0x0000000000000000 in ?? ()
#1 filament::backend::VulkanDriver::terminate ()
#2 filament::FEngine::loop ()
Desktop (please complete the following information):
- OS: Windows 11 (x64) with MSVC 14.51, Ubuntu 24.04 with clang-18
- GPU: Intel Iris Xe / RTX 3500 Ada / RTX 6000
- Backend: Vulkan
Additional context
n/a
Describe the bug
In 1.75.1, once two or more
filament::Engineinstances have been alive at the same time, thesecond
Engine::destroy()crashes. 1.67.0 is fine. Reproduced on Linux and Windows with aprogram that does nothing but create and destroy engines: no meshes, materials, rendering or swap
chains, and destroy order is irrelevant.
Cause:
VulkanDriver::terminate()deletes the process-wide staticDebugUtils::mSingletonunconditionally. In 1.67.0 that delete sat inside
#if FVK_ENABLED(FVK_DEBUG_DEBUG_UTILS), so itnever ran in a release build.
1.67.0:
1.75.1:
deletedoes not null the pointer andDebugUtils::get()never recreates it, so the secondengine's
terminate()deletes it again.~DebugUtils()then readsmInstanceandmDebugMessengerout of the freed object and callsvkDestroyDebugUtilsMessengerEXT. That giveseither a detected double free or a call through a garbage pointer, which is the two signatures
below. Since the state is global, only the NUMBER of engines matters, not the order.
Introduced by c2341d1 "vk: use debug flag to enable debug utils names (#10220)". That PR moved
debug-utils naming from compile-time guards to a runtime flag and removed
#if FVK_ENABLED(FVK_DEBUG_DEBUG_UTILS)from every site in the file, including this delete, whichwas only safe because it was compiled out of release builds.
To Reproduce
Run with an argument of 2 or more.
Expected behavior
Destroying several engines succeeds, as it does in 1.67.0. Possible fixes: null the pointer after
deleting, reference-count the singleton so only the last
terminate()frees it, or gate thedelete on the same runtime flag the rest of the feature now uses.
Screenshots
n/a
Logs
0xC0000374is STATUS_HEAP_CORRUPTION,0xC0000005an access violation. Sequentialcreate-then-destroy, never two alive, is always clean.
The other signature is the same frame reached through a null pointer:
Desktop (please complete the following information):
Additional context
n/a