From d54ec57a5d77494ce1e61f7e642638fcba504b7e Mon Sep 17 00:00:00 2001 From: Swung0x48 Date: Tue, 8 Sep 2026 07:31:48 -0400 Subject: [PATCH] [Fix] (MG_Impl, MG_Pipe): outlive the exit handlers - a frontend object destroyed by __run_exit_handlers reached the slot allocator, the resource tracker and the applier after their own destructors had run --- MobileGL/MG_Impl/Pipe/ResourceTracker.h | 7 +++++-- MobileGL/MG_Impl/Pipe/SlotAllocator.cpp | 12 ++++++++++-- MobileGL/MG_Pipe/PipeApply.cpp | 6 +++++- 3 files changed, 20 insertions(+), 5 deletions(-) diff --git a/MobileGL/MG_Impl/Pipe/ResourceTracker.h b/MobileGL/MG_Impl/Pipe/ResourceTracker.h index b6c251db..7f5159d0 100644 --- a/MobileGL/MG_Impl/Pipe/ResourceTracker.h +++ b/MobileGL/MG_Impl/Pipe/ResourceTracker.h @@ -552,8 +552,11 @@ namespace MobileGL::MG_Pipe { // The monolith's one resource tracker, beside the state tracker, the CSO cache and the // set-hash suppressor. inline MGPipeResourceTracker& MGPipeResourceTrackerInstance() { - static MGPipeResourceTracker tracker; - return tracker; + // NEVER DESTROYED, for MGPipeSlots()' reason (SlotAllocator.cpp): ~BufferObject reads + // and writes this tracker, and the objects that own the last reference to a + // BufferObject outlive every function-local static. + static MGPipeResourceTracker* tracker = new MGPipeResourceTracker(); + return *tracker; } // --------------------------------------------------------------------------------- diff --git a/MobileGL/MG_Impl/Pipe/SlotAllocator.cpp b/MobileGL/MG_Impl/Pipe/SlotAllocator.cpp index ef0d1f85..fd8c1e24 100755 --- a/MobileGL/MG_Impl/Pipe/SlotAllocator.cpp +++ b/MobileGL/MG_Impl/Pipe/SlotAllocator.cpp @@ -168,7 +168,15 @@ namespace MobileGL::MG_Pipe { } MGPipeSlotAllocator& MGPipeSlots() { - static MGPipeSlotAllocator allocator; - return allocator; + // NEVER DESTROYED, deliberately (one allocation for the life of the process). A + // frontend object's destructor reaches this allocator - ~BufferObject through + // MGPipeEmitResourceDestroyAndFree, ~VertexArrayObject through the death notice - and + // MG_Backend/MGPipe/PipeInputs.h's gPipeInputs holds SharedPtrs to those objects at + // namespace scope, so they are destroyed by __run_exit_handlers AFTER this + // function-local static would have been. A destroyed allocator then answers + // FindByLifetimeId out of a freed hash table and Free() writes into freed vectors - + // an exit-time heap corruption whose fatality depends only on the allocator's layout. + static MGPipeSlotAllocator* allocator = new MGPipeSlotAllocator(); + return *allocator; } } // namespace MobileGL::MG_Pipe diff --git a/MobileGL/MG_Pipe/PipeApply.cpp b/MobileGL/MG_Pipe/PipeApply.cpp index 9cadad57..a9793cd9 100644 --- a/MobileGL/MG_Pipe/PipeApply.cpp +++ b/MobileGL/MG_Pipe/PipeApply.cpp @@ -377,7 +377,11 @@ namespace MobileGL::MG_Pipe { static_assert(kCapabilityCount <= 64, "ResidualValueBlock::CapabilityBits is a Uint64; 35 bits fit, 65 would not"); - MGPipeApplierState g_applier{}; + // A REFERENCE TO A NEVER-DESTROYED BLOCK, for MGPipeSlots()' reason + // (MG_Impl/Pipe/SlotAllocator.cpp): resource_destroy and delete_vertex_elements are + // raised from ~BufferObject / ~VertexArrayObject, and those objects are released by + // exit handlers that run after this translation unit's own globals are gone. + MGPipeApplierState& g_applier = *new MGPipeApplierState{}; // The installed handle-shaped resource table. Null until a backend registers one, // which is what makes the client half landable on its own: with nothing here every