fix: kernel and desktop bug fixes
This commit is contained in:
+66
-19
@@ -29,12 +29,47 @@ namespace Memory
|
||||
return header->size;
|
||||
}
|
||||
|
||||
// Insert a free block, keeping the list sorted by address and coalescing with
|
||||
// any adjacent neighbors. Coalescing keeps the free list compact and prevents
|
||||
// fragmentation from accumulating across alloc/free cycles, which is otherwise
|
||||
// pathological because Realloc always allocates a fresh block.
|
||||
void HeapAllocator::InsertToFreelist(void* ptr, std::size_t size) {
|
||||
auto prev_next = head.next;
|
||||
|
||||
head.next = (Node*)ptr;
|
||||
head.next->next = prev_next;
|
||||
head.next->size = size;
|
||||
if (ptr == nullptr || size == 0) return;
|
||||
|
||||
uintptr_t addr = (uintptr_t)ptr;
|
||||
|
||||
Node* prev = &head;
|
||||
Node* current = head.next;
|
||||
while (current != nullptr && (uintptr_t)current < addr) {
|
||||
prev = current;
|
||||
current = current->next;
|
||||
}
|
||||
|
||||
bool mergedPrev = false;
|
||||
if (prev != &head) {
|
||||
uintptr_t prevEnd = (uintptr_t)prev + prev->size;
|
||||
if (prevEnd == addr) {
|
||||
prev->size += size;
|
||||
mergedPrev = true;
|
||||
}
|
||||
}
|
||||
|
||||
if (current != nullptr && addr + size == (uintptr_t)current) {
|
||||
if (mergedPrev) {
|
||||
prev->size += current->size;
|
||||
prev->next = current->next;
|
||||
} else {
|
||||
Node* newNode = (Node*)addr;
|
||||
newNode->size = size + current->size;
|
||||
newNode->next = current->next;
|
||||
prev->next = newNode;
|
||||
}
|
||||
} else if (!mergedPrev) {
|
||||
Node* newNode = (Node*)addr;
|
||||
newNode->size = size;
|
||||
newNode->next = current;
|
||||
prev->next = newNode;
|
||||
}
|
||||
}
|
||||
|
||||
void HeapAllocator::InsertPageToFreelist() {
|
||||
@@ -56,7 +91,10 @@ namespace Memory
|
||||
void* HeapAllocator::Request(size_t size) {
|
||||
Lock.Acquire();
|
||||
|
||||
size_t sizeNeeded = size + sizeof(Header);
|
||||
// Round the user request up so allocated blocks are 8-byte aligned. The
|
||||
// Header itself is 16 bytes, so the returned pointer is always aligned.
|
||||
size_t alignedSize = (size + 7u) & ~(size_t)7u;
|
||||
size_t sizeNeeded = alignedSize + sizeof(Header);
|
||||
|
||||
retry:
|
||||
Node* current = head.next;
|
||||
@@ -64,21 +102,24 @@ namespace Memory
|
||||
|
||||
while (current != nullptr) {
|
||||
if (current->size >= sizeNeeded) {
|
||||
// Unlink the node
|
||||
auto locatedBlockSize = current->size;
|
||||
|
||||
// Only split if the leftover region can hold a free-list Node.
|
||||
// Otherwise hand out the whole block; storing a sub-Node-sized
|
||||
// remainder would write the Node header past the block.
|
||||
bool canSplit = (locatedBlockSize >= sizeNeeded + sizeof(Node));
|
||||
|
||||
prev->next = current->next;
|
||||
Header* header = (Header*)current;
|
||||
|
||||
header->magic = headerMagic;
|
||||
header->size = size;
|
||||
header->size = canSplit ? alignedSize : (locatedBlockSize - sizeof(Header));
|
||||
|
||||
void* block = (void*)((uintptr_t)header + sizeof(Header));
|
||||
|
||||
if (locatedBlockSize > sizeNeeded) {
|
||||
if (canSplit) {
|
||||
void* rest = (void*)((uintptr_t)header + sizeNeeded);
|
||||
auto newBlockSize = locatedBlockSize - sizeNeeded;
|
||||
|
||||
InsertToFreelist(rest, newBlockSize);
|
||||
}
|
||||
|
||||
@@ -92,7 +133,9 @@ namespace Memory
|
||||
|
||||
// No suitable block found -- grow the heap under the lock so
|
||||
// InsertToFreelist is protected from concurrent modification.
|
||||
size_t pagesNeeded = (sizeNeeded + 0xFFF) / 0x1000;
|
||||
// Ensure the new block leaves room for the Header *and* a splittable
|
||||
// free-list Node, so even worst-case requests succeed after growth.
|
||||
size_t pagesNeeded = (sizeNeeded + sizeof(Node) + 0xFFF) / 0x1000;
|
||||
InsertPagesToFreelist(pagesNeeded);
|
||||
goto retry;
|
||||
}
|
||||
@@ -111,20 +154,24 @@ namespace Memory
|
||||
}
|
||||
|
||||
void HeapAllocator::Free(void* ptr) {
|
||||
Lock.Acquire();
|
||||
|
||||
if (ptr == nullptr) return;
|
||||
|
||||
Header* header = GetHeader(ptr);
|
||||
auto size = header->size;
|
||||
|
||||
|
||||
Lock.Acquire();
|
||||
|
||||
// Validate the magic under the lock so that two concurrent frees of
|
||||
// the same pointer can't both pass the check and both insert. The
|
||||
// magic is cleared inside the lock for the same reason.
|
||||
if (header->magic != headerMagic) {
|
||||
Lock.Release();
|
||||
Panic("Bad magic in HeapAllocator header", nullptr);
|
||||
return;
|
||||
}
|
||||
|
||||
auto actualSize = size + sizeof(Header);
|
||||
void* actualBlock = (void*)header;
|
||||
|
||||
InsertToFreelist(actualBlock, actualSize);
|
||||
size_t size = header->size;
|
||||
header->magic = 0;
|
||||
InsertToFreelist((void*)header, size + sizeof(Header));
|
||||
|
||||
Lock.Release();
|
||||
}
|
||||
|
||||
@@ -143,6 +143,17 @@ namespace Memory {
|
||||
return;
|
||||
}
|
||||
|
||||
// Overlap check: the new region must end at or before `current` starts.
|
||||
// The previous double-free guards only matched single-page overlaps;
|
||||
// freeing a multi-page span that overlaps the start of an existing
|
||||
// block would silently corrupt the free list.
|
||||
if (current != nullptr && addr + size > (uint64_t)current) {
|
||||
Kt::KernelLogStream(Kt::WARNING, "PFA")
|
||||
<< "Overlapping free at " << addr << " size " << size << ", ignoring";
|
||||
Lock.Release();
|
||||
return;
|
||||
}
|
||||
|
||||
// Try to coalesce with previous block (if prev ends where new block starts)
|
||||
bool merged_prev = false;
|
||||
if (prev != &head) {
|
||||
|
||||
Reference in New Issue
Block a user