In our case, FSlateFontMeasure::MeasureStringInternal triggered the use-after-free. Any code using this pattern will trigger it
- Cache a const FCharacterEntry& from FCharacterList::GetCharacter()
- Call another FCharacterList::GetCharacter() later in that function on a new (uncached) character
- Deref the first const FCharacterEntry& can result in a use-after-free if the internal FCharacterList::MappedEntries is resized to accommodate the new cache size
I spotted a few risky cases in Engine code
- FSlateFontMeasure::MeasureStringInternal described here
- FCharacterList::GetKerning
As an experiment, I changed FCharacterList::MappedEntries to transact in unique ptrs to bypass the use-after-free and our crash disappeared when using -stompmalloc
TMap<TCHAR, TUniquePtr<FCharacterEntry>> MappedEntries;
I’m reluctant to use this code, since I don’t know the potential performance characteristics of introducing an allocation/level of indirection. Another, hacky, approach I considered was to reserve MappedEntries to the total number of possible characters in the font so it will never need to resize. That feels pretty magical, though.