fix: kernel concurrency, interrupt context, and user VA safety
This commit is contained in:
@@ -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;
|
||||
}
|
||||
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user