fix: kernel concurrency, interrupt context, and user VA safety

This commit is contained in:
2026-08-01 15:43:38 +02:00
parent f222bf1f04
commit b1c55073c7
71 changed files with 2654 additions and 1092 deletions
+25 -3
View File
@@ -70,6 +70,15 @@ namespace Memory {
}
void* PageFrameAllocator::ReallocConsecutive(void* ptr, int n) {
// This primitive grows a single-page allocation into a contiguous
// span; it is not a sized-free API. Guard zero/negative requests so a
// caller can never carve a zero-byte block at the end of the free
// pool and memcpy beyond it. Multi-page owners must use Free(ptr, n).
if (n <= 0) {
if (ptr != nullptr) Free(ptr);
return nullptr;
}
Lock.Acquire();
// Search the free list for a single contiguous region >= n pages.
@@ -118,6 +127,19 @@ namespace Memory {
Lock.Acquire();
uint64_t addr = (uint64_t)ptr;
uint64_t poolStart = g_section.address;
uint64_t poolEnd = poolStart + g_section.size;
// Reject malformed frees before they can splice arbitrary memory into
// the allocator's linked list. Avoid overflowing addr + size while
// validating the upper bound.
if ((addr & 0xFFFULL) || (size & 0xFFFULL) || addr < poolStart
|| poolEnd < poolStart || addr > poolEnd || size > poolEnd - addr) {
Lock.Release();
Kt::KernelLogStream(Kt::WARNING, "PFA")
<< "Invalid free range at " << addr << " size " << size << ", ignoring";
return;
}
// Walk to find the sorted insertion point: prev < addr < current
Page* prev = &head;
@@ -126,9 +148,9 @@ namespace Memory {
while (current != nullptr && (uint64_t)current < addr) {
// Double-free check: addr falls within an existing free block
if (addr < (uint64_t)current + current->size) {
Lock.Release();
Kt::KernelLogStream(Kt::WARNING, "PFA")
<< "Double-free detected at " << addr << ", ignoring";
Lock.Release();
return;
}
prev = current;
@@ -137,9 +159,9 @@ namespace Memory {
// Double-free check: exact match with next block
if (current != nullptr && (uint64_t)current == addr) {
Lock.Release();
Kt::KernelLogStream(Kt::WARNING, "PFA")
<< "Double-free detected at " << addr << ", ignoring";
Lock.Release();
return;
}
@@ -148,9 +170,9 @@ namespace Memory {
// 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) {
Lock.Release();
Kt::KernelLogStream(Kt::WARNING, "PFA")
<< "Overlapping free at " << addr << " size " << size << ", ignoring";
Lock.Release();
return;
}
+31 -29
View File
@@ -7,6 +7,26 @@
namespace Memory::VMM {
Paging* g_paging = nullptr;
static constexpr uint64_t LeafAddressMask = 0x000FFFFFFFFFF000ULL;
static constexpr uint64_t LeafPresent = 1ULL << 0;
static constexpr uint64_t LeafWritable = 1ULL << 1;
static constexpr uint64_t LeafUser = 1ULL << 2;
static constexpr uint64_t LeafWriteThrough = 1ULL << 3;
static constexpr uint64_t LeafCacheDisabled = 1ULL << 4;
static inline void SetLeafPte(PageTableEntry* entry, uint64_t physicalAddress,
bool user, bool writeThrough, bool cacheDisabled) {
uint64_t flags = LeafPresent | LeafWritable;
if (user) flags |= LeafUser;
if (writeThrough) flags |= LeafWriteThrough;
if (cacheDisabled) flags |= LeafCacheDisabled;
// Replace the complete PTE in one aligned store. Updating individual
// bitfields left old PWT/PCD/PAT/accessed state behind when a virtual
// address was reused for a mapping with a different cache policy.
*(volatile uint64_t*)entry = (physicalAddress & LeafAddressMask) | flags;
}
// Protects user page table modifications from concurrent SMP access
static kcp::Mutex pagingLock;
@@ -143,10 +163,7 @@ namespace Memory::VMM {
PageTableEntry* pageEntry = (PageTableEntry*)Memory::HHDM(&PML1->entries[virtualAddressObj.GetPageIndex()]);
pageEntry->Present = true;
pageEntry->Writable = true;
pageEntry->Address = physicalAddress >> 12;
SetLeafPte(pageEntry, physicalAddress, false, false, false);
}
void Paging::MapWC(std::uint64_t physicalAddress, std::uint64_t virtualAddress) {
@@ -162,11 +179,7 @@ namespace Memory::VMM {
PageTableEntry* pageEntry = (PageTableEntry*)Memory::HHDM(&PML1->entries[virtualAddressObj.GetPageIndex()]);
pageEntry->Present = true;
pageEntry->Writable = true;
pageEntry->WriteThrough = true; // PWT=1, PCD=0 → PAT entry 1 = WC
pageEntry->Address = physicalAddress >> 12;
SetLeafPte(pageEntry, physicalAddress, false, true, false);
}
void Paging::MapMMIO(std::uint64_t physicalAddress, std::uint64_t virtualAddress) {
@@ -182,12 +195,7 @@ namespace Memory::VMM {
PageTableEntry* pageEntry = (PageTableEntry*)Memory::HHDM(&PML1->entries[virtualAddressObj.GetPageIndex()]);
pageEntry->Present = true;
pageEntry->Writable = true;
pageEntry->CacheDisabled = true;
pageEntry->WriteThrough = true;
pageEntry->Address = physicalAddress >> 12;
SetLeafPte(pageEntry, physicalAddress, false, true, true);
}
void Paging::MapUser(std::uint64_t physicalAddress, std::uint64_t virtualAddress) {
@@ -203,11 +211,7 @@ namespace Memory::VMM {
PageTableEntry* pageEntry = (PageTableEntry*)Memory::HHDM(&PML1->entries[virtualAddressObj.GetPageIndex()]);
pageEntry->Present = true;
pageEntry->Writable = true;
pageEntry->Supervisor = 1; // User-accessible
pageEntry->Address = physicalAddress >> 12;
SetLeafPte(pageEntry, physicalAddress, true, false, false);
}
std::uint64_t Paging::CreateUserPML4() {
@@ -262,10 +266,7 @@ namespace Memory::VMM {
if (!pml1) { pagingLock.Release(); return false; }
PageTableEntry* pageEntry = (PageTableEntry*)Memory::HHDM(&pml1->entries[va.GetPageIndex()]);
pageEntry->Present = true;
pageEntry->Writable = true;
pageEntry->Supervisor = 1;
pageEntry->Address = physicalAddress >> 12;
SetLeafPte(pageEntry, physicalAddress, true, false, false);
pagingLock.Release();
return true;
}
@@ -305,11 +306,7 @@ namespace Memory::VMM {
if (!pml1) { pagingLock.Release(); return false; }
PageTableEntry* pageEntry = (PageTableEntry*)Memory::HHDM(&pml1->entries[va.GetPageIndex()]);
pageEntry->Present = true;
pageEntry->Writable = true;
pageEntry->Supervisor = 1;
pageEntry->WriteThrough = true; // PWT=1, PCD=0 -> PAT entry 1 = WC
pageEntry->Address = physicalAddress >> 12;
SetLeafPte(pageEntry, physicalAddress, true, true, false);
pagingLock.Release();
return true;
}
@@ -456,9 +453,11 @@ namespace Memory::VMM {
PageTable* pml4Virt = (PageTable*)HHDM(pml4);
PageTableEntry* pml4_entry = &pml4Virt->entries[virtualAddressObj.GetL4Index()];
if (!pml4_entry->Present) return 0;
PageTable* pml3 = (PageTable*)HHDM((pml4_entry->Address & kPhysAddrMask) << 12);
PageTableEntry* pml3_entry = &pml3->entries[virtualAddressObj.GetL3Index()];
if (!pml3_entry->Present) return 0;
// 1GB large page at PML3 level
if (pml3_entry->LargerPages) {
@@ -468,6 +467,7 @@ namespace Memory::VMM {
PageTable* pml2 = (PageTable*)HHDM((pml3_entry->Address & kPhysAddrMask) << 12);
PageTableEntry* pml2_entry = &pml2->entries[virtualAddressObj.GetL2Index()];
if (!pml2_entry->Present) return 0;
// 2MB large page at PML2 level
if (pml2_entry->LargerPages) {
@@ -479,10 +479,12 @@ namespace Memory::VMM {
if (use40BitL1 == true) {
PageTableEntry40Bit* pml1_entry = (PageTableEntry40Bit*)&pml1->entries[virtualAddressObj.GetPageIndex()];
if (!pml1_entry->Present) return 0;
return (uint64_t)pml1_entry->Address << 12;
}
PageTableEntry* pml1_entry = &pml1->entries[virtualAddressObj.GetPageIndex()];
if (!pml1_entry->Present) return 0;
return (uint64_t)(pml1_entry->Address & kPhysAddrMask) << 12;
}