mirror of
https://github.com/Kelsidavis/WoWee.git
synced 2026-03-22 23:30:14 +00:00
fix: add free-list to WardenEmulator heap allocator to prevent exhaustion
The bump-pointer allocator never reused freed blocks, causing the 16 MB emulated heap to exhaust in long sessions even when blocks were freed. - First-fit reuse from a free-list before advancing the bump pointer - Coalesce adjacent free blocks to limit fragmentation - Roll back the bump pointer when the top free block reaches it - Reset allocator state on initialize() so re-runs start clean
This commit is contained in:
parent
ae40d393c3
commit
5031351736
2 changed files with 65 additions and 3 deletions
|
|
@ -152,6 +152,7 @@ private:
|
|||
|
||||
// Memory allocation tracking
|
||||
std::map<uint32_t, size_t> allocations_;
|
||||
std::map<uint32_t, size_t> freeBlocks_; // free-list keyed by base address
|
||||
uint32_t nextHeapAddr_;
|
||||
|
||||
// Hook handles for cleanup
|
||||
|
|
|
|||
|
|
@ -2,6 +2,7 @@
|
|||
#include "core/logger.hpp"
|
||||
#include <cstring>
|
||||
#include <chrono>
|
||||
#include <iterator>
|
||||
|
||||
#ifdef HAVE_UNICORN
|
||||
// Unicorn Engine headers
|
||||
|
|
@ -46,6 +47,12 @@ bool WardenEmulator::initialize(const void* moduleCode, size_t moduleSize, uint3
|
|||
LOG_ERROR("WardenEmulator: Already initialized");
|
||||
return false;
|
||||
}
|
||||
// Reset allocator state so re-initialization starts with a clean heap.
|
||||
allocations_.clear();
|
||||
freeBlocks_.clear();
|
||||
apiAddresses_.clear();
|
||||
hooks_.clear();
|
||||
nextHeapAddr_ = heapBase_;
|
||||
|
||||
{
|
||||
char addrBuf[32];
|
||||
|
|
@ -282,17 +289,37 @@ std::string WardenEmulator::readString(uint32_t address, size_t maxLen) {
|
|||
}
|
||||
|
||||
uint32_t WardenEmulator::allocateMemory(size_t size, [[maybe_unused]] uint32_t protection) {
|
||||
if (size == 0) return 0;
|
||||
|
||||
// Align to 4KB
|
||||
size = (size + 0xFFF) & ~0xFFF;
|
||||
const uint32_t allocSize = static_cast<uint32_t>(size);
|
||||
|
||||
if (nextHeapAddr_ + size > heapBase_ + heapSize_) {
|
||||
// First-fit from free list so released blocks can be reused.
|
||||
for (auto it = freeBlocks_.begin(); it != freeBlocks_.end(); ++it) {
|
||||
if (it->second < size) continue;
|
||||
const uint32_t addr = it->first;
|
||||
const size_t blockSz = it->second;
|
||||
freeBlocks_.erase(it);
|
||||
if (blockSz > size)
|
||||
freeBlocks_[addr + allocSize] = blockSz - size;
|
||||
allocations_[addr] = size;
|
||||
{
|
||||
char mBuf[32];
|
||||
std::snprintf(mBuf, sizeof(mBuf), "0x%X", addr);
|
||||
LOG_DEBUG("WardenEmulator: Reused ", size, " bytes at ", mBuf);
|
||||
}
|
||||
return addr;
|
||||
}
|
||||
|
||||
const uint64_t heapEnd = static_cast<uint64_t>(heapBase_) + heapSize_;
|
||||
if (static_cast<uint64_t>(nextHeapAddr_) + size > heapEnd) {
|
||||
LOG_ERROR("WardenEmulator: Heap exhausted");
|
||||
return 0;
|
||||
}
|
||||
|
||||
uint32_t addr = nextHeapAddr_;
|
||||
nextHeapAddr_ += size;
|
||||
|
||||
nextHeapAddr_ += allocSize;
|
||||
allocations_[addr] = size;
|
||||
|
||||
{
|
||||
|
|
@ -320,7 +347,41 @@ bool WardenEmulator::freeMemory(uint32_t address) {
|
|||
std::snprintf(fBuf, sizeof(fBuf), "0x%X", address);
|
||||
LOG_DEBUG("WardenEmulator: Freed ", it->second, " bytes at ", fBuf);
|
||||
}
|
||||
|
||||
const size_t freedSize = it->second;
|
||||
allocations_.erase(it);
|
||||
|
||||
// Insert in free list and coalesce adjacent blocks to limit fragmentation.
|
||||
auto [curr, inserted] = freeBlocks_.emplace(address, freedSize);
|
||||
if (!inserted) curr->second += freedSize;
|
||||
|
||||
if (curr != freeBlocks_.begin()) {
|
||||
auto prev = std::prev(curr);
|
||||
if (static_cast<uint64_t>(prev->first) + prev->second == curr->first) {
|
||||
prev->second += curr->second;
|
||||
freeBlocks_.erase(curr);
|
||||
curr = prev;
|
||||
}
|
||||
}
|
||||
|
||||
auto next = std::next(curr);
|
||||
if (next != freeBlocks_.end() &&
|
||||
static_cast<uint64_t>(curr->first) + curr->second == next->first) {
|
||||
curr->second += next->second;
|
||||
freeBlocks_.erase(next);
|
||||
}
|
||||
|
||||
// Roll back the bump pointer if the highest free block reaches it.
|
||||
while (!freeBlocks_.empty()) {
|
||||
auto last = std::prev(freeBlocks_.end());
|
||||
if (static_cast<uint64_t>(last->first) + last->second == nextHeapAddr_) {
|
||||
nextHeapAddr_ = last->first;
|
||||
freeBlocks_.erase(last);
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue