On Sat Apr 18 19:57:40 2026 +0000, Matteo Bruni wrote:
Cool, nice sleuthing! I don't think that there is a general Wine rule, we have to figure out something that makes sense in this particular instance. At a very high level, we don't need (or want) to replicate all the implementation details that don't matter in practice. In this case I think either option is okay, so the extra 4 bytes allocation + offset should be simpler. At any rate, the new test currently in this MR can stay as a `todo_wine` even if we don't end up fixing it, so that we don't forget in case that particular quirk comes back to bite us at some later point. BTW, this reminded me that sometimes the memory location "before" a returned address can be used to store extra data (see e.g. https://gitlab.winehq.org/wine/wine/-/blob/47ad1f1d5724fabd206d8ea2c198fad2b... for one such use I introduced many years ago). I quickly hacked tracing the value at `data[-1]` in the tests, it looks like the lower 16 bits change every run, while the high 16 bits seem to be consistent and appear to be flags of some sort. I think we can leave it at that :sweat_smile: 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 DW ORD 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?
-- https://gitlab.winehq.org/wine/wine/-/merge_requests/10278#note_138661