fix: kernel and desktop bug fixes

This commit is contained in:
2026-05-14 11:31:14 +02:00
parent a7ff1f0a72
commit bb12aeb9b9
11 changed files with 507 additions and 106 deletions
+66 -19
View File
@@ -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();
}
+11
View File
@@ -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) {