On Sat May 2 18:36:26 2026 +0000, Joerg Rueppel wrote:
Of course padding with 4 extra bytes just made it crash, so more digging ensued. SE5 is a Delphi program, and it looks like the ptr that is returned here is being coopted as a Delphi dynamic array, which is refcounted. Layout of these things is ptr-8 = 32bit refcount, ptr-4 = 32bit length. ptr = actual data. Theory: The game takes the ptr we give back, adds 4 bytes and then hands it over to delphis dynamic array system. Our returned data starts with a 4 byte vertex counter, so the game is indeed 'smart' in just mapping that to delphis darray layout and reusing the size data that's already in place. That's why we see refcount access to ptr - 4 and not - 8. We also have a use after free here, because after releasing the data object, delphis array system will decrement the refcount once more. If it reaches 0, bad things happen because Delphi then tries to free the memory block with its own memory manager. It stops crashing and works properly when I allocate 12 extra bytes and set the DWORD before the actual data to at least 2 (so the refcount doesn't reach 0). 12 bytes are probably needed because the write after free otherwise destroys wine's internal heap management on the freed memory block? A very clean solution should not free the data in IDirectXFileDataImpl_Release and do it sometime later. A solution that works right now is allocating the 12 extra bytes because then the write after free doesn't damage anything important. Is the latter one acceptable? Do you see another way? Given all this, it sounds safer adding padding areas in the internal buffer, and not doing the individual allocs. Then the game can write to it as much as it wants after releasing the object. I haven't looked into how involved that path would get though.
-- https://gitlab.winehq.org/wine/wine/-/merge_requests/10278#note_138662