From 19fc7346c5e6393eee7ee1144994fe548762443b Mon Sep 17 00:00:00 2001 From: Swung0x48 Date: Tue, 11 Aug 2026 23:05:13 -0400 Subject: [PATCH] [Fix] (MG_Backend/DirectVulkan): a re-entrant cache lookup dangled the reference glShaderStorageBlockBinding then wrote through --- MobileGL/MG_Backend/DirectVulkan/DirectVulkan.cpp | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/MobileGL/MG_Backend/DirectVulkan/DirectVulkan.cpp b/MobileGL/MG_Backend/DirectVulkan/DirectVulkan.cpp index eb108838..046c8a9e 100644 --- a/MobileGL/MG_Backend/DirectVulkan/DirectVulkan.cpp +++ b/MobileGL/MG_Backend/DirectVulkan/DirectVulkan.cpp @@ -801,9 +801,18 @@ namespace MobileGL::MG_Backend::DirectVulkan { // already recorded the new binding on the program - which is what reseeds this cache // whenever it is rebuilt. Writing the entry here as well keeps an ALREADY-BUILT cache // (the common case: the very next draw reads it) from having to be thrown away. - auto& cache = GetProgramResourceCache(*programObject); + // + // Resolve the index BEFORE taking the reference, and bounds-check the way the + // sibling getter does. GetShaderStorageBlockIndex re-enters GetProgramResourceCache, + // which indexes g_programResourceCaches and can therefore insert - and that map is + // FastSTL's open-addressed unordered_map, whose rehash MOVES its buckets, so a + // reference taken before the call is left dangling. Binding a program's storage block + // while another program's entry was still absent from the cache was a reproducible + // segfault (ProgramPipelineScenario's two storage-block cases, in one process). const GLuint blockIndex = GetShaderStorageBlockIndex(*programObject, storageBlockName); if (blockIndex == GL_INVALID_INDEX) return; + auto& cache = GetProgramResourceCache(*programObject); + if (blockIndex >= cache.storageBlocks.size()) return; cache.storageBlocks[blockIndex].binding = storageBlockBinding; } void ReadPixels(GLint x, GLint y, GLsizei width, GLsizei height, GLenum format, GLenum type, void* pixels) {