MemBlockInfo: Clamp marked ranges to the address space

A size that makes addr + size wrap around 32 bits had Split() build a slab
that ends before it starts. Lookups then skipped it, and a later
Merge() ran FillHeads() over it with a huge count, writing far outside
heads_. Sizes come from games through HLE calls, and the large ones are
recorded even without detailed mem info. Seen in a Play crash report, on
the FlushMemInfo thread.

FillHeads() also took the address one past the end of heads_ for a slab
in the last slice, which Debug builds assert on.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
This commit is contained in:
Henrik RydgårdandClaude Opus 5.5 committed 2026-10-25 19:37:23 +01:00
1 parent 8a50821839
commit 42f9af1744
2 files changed
+38 -5

No files matched your search

+10 -5
View File
@@ -127,6 +127,14 @@ MemSlabMap::~MemSlabMap() {
}
bool MemSlabMap::Mark(uint32_t addr, uint32_t size, uint64_t ticks, uint32_t pc, bool allocated, const char *tag, size_t tagLen) {
if (addr >= MAX_SIZE) {
return false;
}
// The size comes from the game. If addr + size wraps, Split() makes a slab that ends before it
// starts, and a later FillHeads() on it writes far outside heads_.
if (size > MAX_SIZE - addr) {
size = MAX_SIZE - addr;
}
uint32_t end = addr + size;
Slab *slab = FindSlab(addr);
Slab *firstMatch = nullptr;
@@ -430,11 +438,8 @@ void MemSlabMap::FillHeads(Slab *slab) {
}
// Now replace all the rest - we definitely cover the start of them.
Slab **next = &heads_[slice + 1];
// We want to set slice + 1 through endSlice, inclusive.
size_t c = endSlice - slice;
for (size_t i = 0; i < c; ++i) {
next[i] = slab;
for (uint32_t i = slice + 1; i <= endSlice; ++i) {
heads_[i] = slab;
}
}