From fd7fcbbbc87c1750d9fa74b30211e71ba1d4e145 Mon Sep 17 00:00:00 2001 From: Daniel Hammer Date: Fri, 10 Apr 2026 23:16:02 +0200 Subject: [PATCH] fix: various kernel bug fixes --- kernel/src/Api/Heap.hpp | 49 ++++++-- kernel/src/Api/LibSyscall.hpp | 18 +-- kernel/src/Api/Storage.hpp | 4 + kernel/src/Api/Syscall.cpp | 224 ++++++++++++++++----------------- kernel/src/CppLib/Alloc.cpp | 9 +- kernel/src/CppLib/Vector.hpp | 40 +++--- kernel/src/Hal/IDT.cpp | 6 +- kernel/src/Ipc/Ipc.cpp | 20 +++ kernel/src/Memory/Paging.cpp | 47 +++++++ kernel/src/Memory/Paging.hpp | 6 + kernel/src/Sched/Scheduler.cpp | 21 +++- kernel/src/Sched/Scheduler.hpp | 1 + 12 files changed, 282 insertions(+), 163 deletions(-) diff --git a/kernel/src/Api/Heap.hpp b/kernel/src/Api/Heap.hpp index 8570105..15ae038 100644 --- a/kernel/src/Api/Heap.hpp +++ b/kernel/src/Api/Heap.hpp @@ -20,11 +20,11 @@ namespace Montauk { static constexpr int MaxHeapAllocs = 512; - static HeapAlloc g_heapAllocs[Sched::MaxProcesses][MaxHeapAllocs]; - static int g_heapAllocCount[Sched::MaxProcesses]; + inline HeapAlloc g_heapAllocs[Sched::MaxProcesses][MaxHeapAllocs] = {}; + inline int g_heapAllocCount[Sched::MaxProcesses] = {}; // Get the process table slot index for the current process - static int GetCurrentSlot() { + inline int GetCurrentSlot() { auto* proc = Sched::GetCurrentProcessPtr(); if (proc == nullptr) return -1; // Process slot index = pointer offset from slot 0 @@ -32,9 +32,11 @@ namespace Montauk { return (int)(proc - slot0); } - static uint64_t Sys_Alloc(uint64_t size) { + inline uint64_t Sys_Alloc(uint64_t size) { auto* proc = Sched::GetCurrentProcessPtr(); if (proc == nullptr) return 0; + int slot = GetCurrentSlot(); + if (slot < 0) return 0; // Guard against overflow before rounding static constexpr uint64_t USER_SPACE_END = 0x0000800000000000ULL; @@ -50,36 +52,57 @@ namespace Montauk { if (userVa + size < userVa || userVa + size > USER_SPACE_END) return 0; uint64_t numPages = size / 0x1000; + if (g_heapAllocCount[slot] >= MaxHeapAllocs) return 0; // Allocate physical pages and map them into the process + uint64_t mappedPages = 0; for (uint64_t i = 0; i < numPages; i++) { void* page = Memory::g_pfa->AllocateZeroed(); - if (page == nullptr) return 0; + if (page == nullptr) { + for (uint64_t j = 0; j < mappedPages; j++) { + uint64_t pageVa = userVa + j * 0x1000; + uint64_t physAddr = Memory::VMM::Paging::GetPhysAddr(proc->pml4Phys, pageVa); + if (physAddr != 0) { + Memory::g_pfa->Free((void*)Memory::HHDM(physAddr)); + } + Memory::VMM::Paging::UnmapUserIn(proc->pml4Phys, pageVa); + } + return 0; + } uint64_t physAddr = Memory::SubHHDM((uint64_t)page); - if (!Memory::VMM::Paging::MapUserIn(proc->pml4Phys, physAddr, userVa + i * 0x1000)) return 0; + if (!Memory::VMM::Paging::MapUserIn(proc->pml4Phys, physAddr, userVa + i * 0x1000)) { + Memory::g_pfa->Free(page); + for (uint64_t j = 0; j < mappedPages; j++) { + uint64_t pageVa = userVa + j * 0x1000; + uint64_t mappedPhys = Memory::VMM::Paging::GetPhysAddr(proc->pml4Phys, pageVa); + if (mappedPhys != 0) { + Memory::g_pfa->Free((void*)Memory::HHDM(mappedPhys)); + } + Memory::VMM::Paging::UnmapUserIn(proc->pml4Phys, pageVa); + } + return 0; + } + mappedPages++; } proc->heapNext += size; // Track the allocation so Sys_Free can release it - int slot = GetCurrentSlot(); - if (slot >= 0) Sched::g_allocatedPages[slot] += numPages; - if (slot >= 0 && g_heapAllocCount[slot] < MaxHeapAllocs) { - g_heapAllocs[slot][g_heapAllocCount[slot]++] = { userVa, numPages }; - } + Sched::g_allocatedPages[slot] += numPages; + g_heapAllocs[slot][g_heapAllocCount[slot]++] = { userVa, numPages }; return userVa; } // Reset heap allocation tracking for a process slot. // The actual physical pages are freed by Paging::FreeUserHalf() during process cleanup. - static void CleanupHeapForSlot(int slot, uint64_t /*pml4Phys*/) { + inline void CleanupHeapForSlot(int slot, uint64_t /*pml4Phys*/) { if (slot < 0 || slot >= Sched::MaxProcesses) return; g_heapAllocCount[slot] = 0; Sched::g_allocatedPages[slot] = 0; } - static void Sys_Free(uint64_t addr) { + inline void Sys_Free(uint64_t addr) { auto* proc = Sched::GetCurrentProcessPtr(); if (proc == nullptr) return; diff --git a/kernel/src/Api/LibSyscall.hpp b/kernel/src/Api/LibSyscall.hpp index 6a380bb..469ff3d 100644 --- a/kernel/src/Api/LibSyscall.hpp +++ b/kernel/src/Api/LibSyscall.hpp @@ -18,7 +18,7 @@ namespace Montauk { // Maximum libraries per process static constexpr int MaxLibsPerProcess = 8; - static constexpr int MaxProcesses = 64; + static constexpr int MaxProcesses = Sched::MaxProcesses; // Library entry tracking struct LibEntry { @@ -28,15 +28,15 @@ namespace Montauk { bool inUse; }; - static uint64_t GetLibSlotBase(int libSlot) { + inline uint64_t GetLibSlotBase(int libSlot) { return Sched::LIB_BASE + (uint64_t(libSlot) * Sched::LIB_MAX_SIZE); } // Per-process library table - static LibEntry g_libTable[MaxProcesses][MaxLibsPerProcess]; + inline LibEntry g_libTable[MaxProcesses][MaxLibsPerProcess] = {}; // Initialize library table for a process slot - static void InitLibTable(int slot) { + inline void InitLibTable(int slot) { if (slot < 0 || slot >= MaxProcesses) return; for (int i = 0; i < MaxLibsPerProcess; i++) { g_libTable[slot][i].inUse = false; @@ -49,7 +49,7 @@ namespace Montauk { // Load a shared library into the current process's address space. // Returns a library handle (slot index + 1) on success, or 0 on failure. // The library is loaded at a fixed address based on the slot. - static uint64_t Sys_LoadLib(const char* path) { + inline uint64_t Sys_LoadLib(const char* path) { int slot = GetCurrentSlot(); if (slot < 0) return 0; @@ -100,7 +100,7 @@ namespace Montauk { // Unload a shared library. // Decrements refcount; actually unmaps when refcount reaches 0. - static int Sys_UnloadLib(uint64_t handle) { + inline int Sys_UnloadLib(uint64_t handle) { int slot = GetCurrentSlot(); if (slot < 0) return -1; @@ -139,7 +139,7 @@ namespace Montauk { // handle = library handle from LoadLib // symbolOffset = ELF symbol value from the library's symbol table // Returns the virtual address of the symbol, or 0 if not found. - static uint64_t Sys_DLSym(uint64_t handle, uint64_t symbolOffset) { + inline uint64_t Sys_DLSym(uint64_t handle, uint64_t symbolOffset) { int slot = GetCurrentSlot(); if (slot < 0) return 0; @@ -153,7 +153,7 @@ namespace Montauk { } // Get library relocation base / load bias (for userspace symbol resolution) - static uint64_t Sys_GetLibBase(uint64_t handle) { + inline uint64_t Sys_GetLibBase(uint64_t handle) { int slot = GetCurrentSlot(); if (slot < 0) return 0; @@ -166,7 +166,7 @@ namespace Montauk { } // Cleanup library table for a process slot - static void CleanupLibTable(int slot) { + inline void CleanupLibTable(int slot) { if (slot < 0 || slot >= MaxProcesses) return; auto* proc = Sched::GetProcessSlot(slot); diff --git a/kernel/src/Api/Storage.hpp b/kernel/src/Api/Storage.hpp index 9356d0a..a70de83 100644 --- a/kernel/src/Api/Storage.hpp +++ b/kernel/src/Api/Storage.hpp @@ -11,6 +11,7 @@ #include #include #include +#include #include "Syscall.hpp" @@ -25,6 +26,7 @@ namespace Montauk { // Fill a user buffer with partition info. Returns the number of entries written. static int Sys_PartList(PartInfo* buf, int maxCount) { if (buf == nullptr || maxCount <= 0) return 0; + if (!UserMemory::Range((uint64_t)buf, (uint64_t)maxCount * sizeof(PartInfo), true)) return -1; int total = Drivers::Storage::Gpt::GetPartitionCount(); int count = total < maxCount ? total : maxCount; @@ -61,6 +63,7 @@ namespace Montauk { if (!dev) return -1; if (lba + count > dev->SectorCount) return -1; + if (!UserMemory::Range((uint64_t)buffer, (uint64_t)count * dev->SectorSize, true)) return -1; if (!dev->ReadSectors(dev->Ctx, lba, count, buffer)) return -1; @@ -76,6 +79,7 @@ namespace Montauk { if (!dev) return -1; if (lba + count > dev->SectorCount) return -1; + if (!UserMemory::Range((uint64_t)buffer, (uint64_t)count * dev->SectorSize, false)) return -1; if (!dev->WriteSectors(dev->Ctx, lba, count, buffer)) return -1; diff --git a/kernel/src/Api/Syscall.cpp b/kernel/src/Api/Syscall.cpp index 958e00b..097bbde 100644 --- a/kernel/src/Api/Syscall.cpp +++ b/kernel/src/Api/Syscall.cpp @@ -36,40 +36,25 @@ #include "LibSyscall.hpp" // SYS_LOAD_LIB, SYS_UNLOAD_LIB, SYS_DLSYM #include "CrashReportSyscall.hpp" // SYS_CRASH_REPORT #include "Clipboard.hpp" // SYS_CLIPBOARD_* +#include "UserMemory.hpp" // Assembly entry point extern "C" void SyscallEntry(); namespace Montauk { - // ---- User pointer validation ---- - // Reject pointers that fall in the kernel-half of the address space - // (canonical high addresses, i.e. >= 0x0000800000000000). - // This prevents userspace from tricking the kernel into reading/writing - // kernel memory via syscall arguments. - - static constexpr uint64_t USER_SPACE_END = 0x0000800000000000ULL; - - static bool IsUserPtr(uint64_t addr) { - return addr == 0 || addr < USER_SPACE_END; - } - - // Validate that a pointer is non-null and in user space - static bool ValidUserPtr(uint64_t addr) { - return addr != 0 && addr < USER_SPACE_END; - } + static constexpr uint64_t kMaxPrintableStringBytes = 4096; + static constexpr uint64_t kMaxPathBytes = 256; + static constexpr uint64_t kMaxArgsBytes = 256; + static constexpr uint64_t kMaxWindowTitleBytes = 256; + static constexpr uint64_t kMaxHostnameBytes = 256; + static constexpr uint64_t kMaxUserNameBytes = 32; // ---- Dispatch ---- extern "C" int64_t SyscallDispatch(SyscallFrame* frame) { switch (frame->syscall_nr) { case SYS_EXIT: { - int slot = GetCurrentSlot(); - auto* proc = Sched::GetCurrentProcessPtr(); - if (slot >= 0 && proc) - CleanupHeapForSlot(slot, proc->pml4Phys); - if (slot >= 0) - CleanupLibTable(slot); Sys_Exit((int)frame->arg1); return 0; } @@ -82,17 +67,17 @@ namespace Montauk { case SYS_GETPID: return (int64_t)Sys_GetPid(); case SYS_PRINT: - if (!ValidUserPtr(frame->arg1)) return -1; + if (!UserMemory::String(frame->arg1, kMaxPrintableStringBytes)) return -1; Sys_Print((const char*)frame->arg1); return 0; case SYS_PUTCHAR: Sys_Putchar((char)frame->arg1); return 0; case SYS_OPEN: - if (!ValidUserPtr(frame->arg1)) return -1; + if (!UserMemory::String(frame->arg1, kMaxPathBytes)) return -1; return (int64_t)Sys_Open((const char*)frame->arg1); case SYS_READ: - if (!ValidUserPtr(frame->arg2)) return -1; + if (!UserMemory::Range(frame->arg2, frame->arg4, true)) return -1; return (int64_t)Sys_Read((int)frame->arg1, (uint8_t*)frame->arg2, frame->arg3, frame->arg4); case SYS_GETSIZE: @@ -101,7 +86,13 @@ namespace Montauk { Sys_Close((int)frame->arg1); return 0; case SYS_READDIR: - if (!ValidUserPtr(frame->arg1) || !ValidUserPtr(frame->arg2)) return -1; + if ((int64_t)frame->arg3 < 0) return -1; + if (!UserMemory::String(frame->arg1, kMaxPathBytes)) return -1; + if (!UserMemory::Range(frame->arg2, + (uint64_t)((frame->arg3 > 256) ? 256 : frame->arg3) * sizeof(const char*), + true)) { + return -1; + } return (int64_t)Sys_ReadDir((const char*)frame->arg1, (const char**)frame->arg2, (int)frame->arg3); @@ -115,13 +106,13 @@ namespace Montauk { case SYS_GETMILLISECONDS: return (int64_t)Sys_GetMilliseconds(); case SYS_GETINFO: - if (!ValidUserPtr(frame->arg1)) return -1; + if (!UserMemory::Writable(frame->arg1)) return -1; Sys_GetInfo((SysInfo*)frame->arg1); return 0; case SYS_ISKEYAVAILABLE: return (int64_t)Sys_IsKeyAvailable(); case SYS_GETKEY: - if (!ValidUserPtr(frame->arg1)) return -1; + if (!UserMemory::Writable(frame->arg1)) return -1; Sys_GetKey((KeyEvent*)frame->arg1); return 0; case SYS_GETCHAR: @@ -129,14 +120,15 @@ namespace Montauk { case SYS_PING: return (int64_t)Sys_Ping((uint32_t)frame->arg1, (uint32_t)frame->arg2); case SYS_SPAWN: - if (!ValidUserPtr(frame->arg1)) return -1; + if (!UserMemory::String(frame->arg1, kMaxPathBytes)) return -1; + if (frame->arg2 != 0 && !UserMemory::String(frame->arg2, kMaxArgsBytes)) return -1; return (int64_t)Sys_Spawn((const char*)frame->arg1, - IsUserPtr(frame->arg2) ? (const char*)frame->arg2 : nullptr); + UserMemory::IsUserPtr(frame->arg2) ? (const char*)frame->arg2 : nullptr); case SYS_WAITPID: Sys_WaitPid((int)frame->arg1); return 0; case SYS_FBINFO: - if (!ValidUserPtr(frame->arg1)) return -1; + if (!UserMemory::Writable(frame->arg1)) return -1; Sys_FbInfo((FbInfo*)frame->arg1); return 0; case SYS_FBMAP: @@ -144,7 +136,7 @@ namespace Montauk { case SYS_TERMSIZE: return (int64_t)Sys_TermSize(); case SYS_GETARGS: - if (!ValidUserPtr(frame->arg1)) return -1; + if (!UserMemory::Range(frame->arg1, frame->arg2, true)) return -1; return (int64_t)Sys_GetArgs((char*)frame->arg1, frame->arg2); case SYS_RESET: Sys_Reset(); @@ -153,7 +145,7 @@ namespace Montauk { Sys_Shutdown(); return 0; case SYS_GETTIME: - if (!ValidUserPtr(frame->arg1)) return -1; + if (!UserMemory::Writable(frame->arg1)) return -1; Sys_GetTime((DateTime*)frame->arg1); return 0; case SYS_SOCKET: @@ -167,87 +159,94 @@ namespace Montauk { case SYS_ACCEPT: return (int64_t)Sys_Accept((int)frame->arg1); case SYS_SEND: - if (!ValidUserPtr(frame->arg2)) return -1; + if (!UserMemory::Range(frame->arg2, frame->arg3, false)) return -1; return (int64_t)Sys_Send((int)frame->arg1, (const uint8_t*)frame->arg2, (uint32_t)frame->arg3); case SYS_RECV: - if (!ValidUserPtr(frame->arg2)) return -1; + if (!UserMemory::Range(frame->arg2, frame->arg3, true)) return -1; return (int64_t)Sys_Recv((int)frame->arg1, (uint8_t*)frame->arg2, (uint32_t)frame->arg3); case SYS_CLOSESOCK: Sys_CloseSock((int)frame->arg1); return 0; case SYS_GETNETCFG: - if (!ValidUserPtr(frame->arg1)) return -1; + if (!UserMemory::Writable(frame->arg1)) return -1; Sys_GetNetCfg((NetCfg*)frame->arg1); return 0; case SYS_SETNETCFG: - if (!ValidUserPtr(frame->arg1)) return -1; + if (!UserMemory::Readable(frame->arg1)) return -1; return (int64_t)Sys_SetNetCfg((const NetCfg*)frame->arg1); case SYS_SENDTO: - if (!ValidUserPtr(frame->arg2)) return -1; + if (!UserMemory::Range(frame->arg2, frame->arg3, false)) return -1; return (int64_t)Sys_SendTo((int)frame->arg1, (const uint8_t*)frame->arg2, (uint32_t)frame->arg3, (uint32_t)frame->arg4, (uint16_t)frame->arg5); case SYS_RECVFROM: - if (!ValidUserPtr(frame->arg2)) return -1; + if (!UserMemory::Range(frame->arg2, frame->arg3, true)) return -1; + if (!UserMemory::OptionalRange(frame->arg4, sizeof(uint32_t), true)) return -1; + if (!UserMemory::OptionalRange(frame->arg5, sizeof(uint16_t), true)) return -1; return (int64_t)Sys_RecvFrom((int)frame->arg1, (uint8_t*)frame->arg2, (uint32_t)frame->arg3, - IsUserPtr(frame->arg4) ? (uint32_t*)frame->arg4 : nullptr, - IsUserPtr(frame->arg5) ? (uint16_t*)frame->arg5 : nullptr); + UserMemory::IsUserPtr(frame->arg4) ? (uint32_t*)frame->arg4 : nullptr, + UserMemory::IsUserPtr(frame->arg5) ? (uint16_t*)frame->arg5 : nullptr); case SYS_FWRITE: - if (!ValidUserPtr(frame->arg2)) return -1; + if (!UserMemory::Range(frame->arg2, frame->arg4, false)) return -1; return (int64_t)Sys_FWrite((int)frame->arg1, (const uint8_t*)frame->arg2, frame->arg3, frame->arg4); case SYS_FCREATE: - if (!ValidUserPtr(frame->arg1)) return -1; + if (!UserMemory::String(frame->arg1, kMaxPathBytes)) return -1; return (int64_t)Sys_FCreate((const char*)frame->arg1); case SYS_FDELETE: - if (!ValidUserPtr(frame->arg1)) return -1; + if (!UserMemory::String(frame->arg1, kMaxPathBytes)) return -1; return (int64_t)Sys_FDelete((const char*)frame->arg1); case SYS_FMKDIR: - if (!ValidUserPtr(frame->arg1)) return -1; + if (!UserMemory::String(frame->arg1, kMaxPathBytes)) return -1; return (int64_t)Sys_FMkdir((const char*)frame->arg1); case SYS_FRENAME: - if (!ValidUserPtr(frame->arg1)) return -1; - if (!ValidUserPtr(frame->arg2)) return -1; + if (!UserMemory::String(frame->arg1, kMaxPathBytes)) return -1; + if (!UserMemory::String(frame->arg2, kMaxPathBytes)) return -1; return (int64_t)Sys_FRename((const char*)frame->arg1, (const char*)frame->arg2); case SYS_DRIVELIST: - if (!ValidUserPtr(frame->arg1)) return -1; + if ((int64_t)frame->arg2 < 0) return -1; + if (!UserMemory::Range(frame->arg1, (uint64_t)frame->arg2 * sizeof(int), true)) return -1; return (int64_t)Sys_DriveList((int*)frame->arg1, (int)frame->arg2); case SYS_TERMSCALE: return Sys_TermScale(frame->arg1, frame->arg2); case SYS_RESOLVE: - if (!ValidUserPtr(frame->arg1)) return -1; + if (!UserMemory::String(frame->arg1, kMaxHostnameBytes)) return -1; return Sys_Resolve((const char*)frame->arg1); case SYS_GETRANDOM: - if (!ValidUserPtr(frame->arg1)) return -1; + if (!UserMemory::Range(frame->arg1, frame->arg2, true)) return -1; return Sys_GetRandom((uint8_t*)frame->arg1, frame->arg2); case SYS_KLOG: - if (!ValidUserPtr(frame->arg1)) return -1; + if (!UserMemory::Range(frame->arg1, frame->arg2, true)) return -1; return Kt::ReadKernelLog((char*)frame->arg1, frame->arg2); case SYS_MOUSESTATE: - if (!ValidUserPtr(frame->arg1)) return -1; + if (!UserMemory::Writable(frame->arg1)) return -1; Sys_MouseState((MouseState*)frame->arg1); return 0; case SYS_SETMOUSEBOUNDS: Sys_SetMouseBounds((int32_t)frame->arg1, (int32_t)frame->arg2); return 0; case SYS_SPAWN_REDIR: - if (!ValidUserPtr(frame->arg1)) return -1; + if (!UserMemory::String(frame->arg1, kMaxPathBytes)) return -1; + if (frame->arg2 != 0 && !UserMemory::String(frame->arg2, kMaxArgsBytes)) return -1; return (int64_t)Sys_SpawnRedir((const char*)frame->arg1, - IsUserPtr(frame->arg2) ? (const char*)frame->arg2 : nullptr); + UserMemory::IsUserPtr(frame->arg2) ? (const char*)frame->arg2 : nullptr); case SYS_CHILDIO_READ: - if (!ValidUserPtr(frame->arg2)) return -1; + if ((int64_t)frame->arg3 < 0) return -1; + if (!UserMemory::Range(frame->arg2, (uint64_t)frame->arg3, true)) return -1; return (int64_t)Sys_ChildIoRead((int)frame->arg1, (char*)frame->arg2, (int)frame->arg3); case SYS_CHILDIO_WRITE: - if (!ValidUserPtr(frame->arg2)) return -1; + if ((int64_t)frame->arg3 < 0) return -1; + if (!UserMemory::Range(frame->arg2, (uint64_t)frame->arg3, false)) return -1; return (int64_t)Sys_ChildIoWrite((int)frame->arg1, (const char*)frame->arg2, (int)frame->arg3); case SYS_CHILDIO_WRITEKEY: - if (!ValidUserPtr(frame->arg2)) return -1; + if (!UserMemory::Readable(frame->arg2)) return -1; return (int64_t)Sys_ChildIoWriteKey((int)frame->arg1, (const KeyEvent*)frame->arg2); case SYS_CHILDIO_SETTERMSZ: return (int64_t)Sys_ChildIoSetTermsz((int)frame->arg1, (int)frame->arg2, (int)frame->arg3); case SYS_WINCREATE: - if (!ValidUserPtr(frame->arg1) || !ValidUserPtr(frame->arg4)) return -1; + if (!UserMemory::String(frame->arg1, kMaxWindowTitleBytes)) return -1; + if (!UserMemory::Writable(frame->arg4)) return -1; return (int64_t)Sys_WinCreate((const char*)frame->arg1, (int)frame->arg2, (int)frame->arg3, (WinCreateResult*)frame->arg4); case SYS_WINDESTROY: @@ -255,39 +254,33 @@ namespace Montauk { case SYS_WINPRESENT: return (int64_t)Sys_WinPresent((int)frame->arg1); case SYS_WINPOLL: - if (!ValidUserPtr(frame->arg2)) return -1; + if (!UserMemory::Writable(frame->arg2)) return -1; return (int64_t)Sys_WinPoll((int)frame->arg1, (WinEvent*)frame->arg2); case SYS_WINENUM: - if (!ValidUserPtr(frame->arg1)) return -1; + if ((int64_t)frame->arg2 < 0) return -1; + if (!UserMemory::Range(frame->arg1, (uint64_t)frame->arg2 * sizeof(WinInfo), true)) return -1; return (int64_t)Sys_WinEnum((WinInfo*)frame->arg1, (int)frame->arg2); case SYS_WINMAP: return (int64_t)Sys_WinMap((int)frame->arg1); case SYS_WINUNMAP: return (int64_t)Sys_WinUnmap((int)frame->arg1); case SYS_WINSENDEVENT: - if (!ValidUserPtr(frame->arg2)) return -1; + if (!UserMemory::Readable(frame->arg2)) return -1; return (int64_t)Sys_WinSendEvent((int)frame->arg1, (const WinEvent*)frame->arg2); case SYS_WINRESIZE: return (int64_t)Sys_WinResize((int)frame->arg1, (int)frame->arg2, (int)frame->arg3); case SYS_PROCLIST: - if (!ValidUserPtr(frame->arg1)) return -1; + if ((int64_t)frame->arg2 < 0) return -1; + if (!UserMemory::Range(frame->arg1, (uint64_t)frame->arg2 * sizeof(ProcInfo), true)) return -1; return (int64_t)Sys_ProcList((ProcInfo*)frame->arg1, (int)frame->arg2); - case SYS_KILL: { - // Free heap allocations for the target process before killing it - auto* target = Sched::GetProcessByPid((int)frame->arg1); - if (target) { - auto* slot0 = Sched::GetProcessSlot(0); - int targetSlot = (int)(target - slot0); - CleanupHeapForSlot(targetSlot, target->pml4Phys); - CleanupLibTable(targetSlot); - } + case SYS_KILL: return (int64_t)Sys_Kill((int)frame->arg1); - } case SYS_DEVLIST: - if (!ValidUserPtr(frame->arg1)) return -1; + if ((int64_t)frame->arg2 < 0) return -1; + if (!UserMemory::Range(frame->arg1, (uint64_t)frame->arg2 * sizeof(DevInfo), true)) return -1; return (int64_t)Sys_DevList((DevInfo*)frame->arg1, (int)frame->arg2); case SYS_DISKINFO: - if (!ValidUserPtr(frame->arg1)) return -1; + if (!UserMemory::Writable(frame->arg1)) return -1; return (int64_t)Sys_DiskInfo((DiskInfo*)frame->arg1, (int)frame->arg2); case SYS_WINSETSCALE: return (int64_t)Sys_WinSetScale((int)frame->arg1); @@ -296,53 +289,54 @@ namespace Montauk { case SYS_WINSETCURSOR: return (int64_t)Sys_WinSetCursor((int)frame->arg1, (int)frame->arg2); case SYS_MEMSTATS: - if (!ValidUserPtr(frame->arg1)) return -1; + if (!UserMemory::Writable(frame->arg1)) return -1; Sys_MemStats((MemStats*)frame->arg1); return 0; case SYS_PARTLIST: - if (!ValidUserPtr(frame->arg1)) return -1; + if ((int64_t)frame->arg2 < 0) return -1; + if (!UserMemory::Range(frame->arg1, (uint64_t)frame->arg2 * sizeof(PartInfo), true)) return -1; return (int64_t)Sys_PartList((PartInfo*)frame->arg1, (int)frame->arg2); case SYS_DISKREAD: - if (!ValidUserPtr(frame->arg4)) return -1; return (int64_t)Sys_DiskRead((int)frame->arg1, frame->arg2, (uint32_t)frame->arg3, (void*)frame->arg4); case SYS_DISKWRITE: - if (!ValidUserPtr(frame->arg4)) return -1; return (int64_t)Sys_DiskWrite((int)frame->arg1, frame->arg2, (uint32_t)frame->arg3, (const void*)frame->arg4); case SYS_GPTINIT: return (int64_t)Sys_GptInit((int)frame->arg1); case SYS_GPTADD: - if (!ValidUserPtr(frame->arg1)) return -1; + if (!UserMemory::Readable(frame->arg1)) return -1; return (int64_t)Sys_GptAdd((const GptAddParams*)frame->arg1); case SYS_FSMOUNT: return (int64_t)Sys_FsMount((int)frame->arg1, (int)frame->arg2); case SYS_FSFORMAT: - if (!ValidUserPtr(frame->arg1)) return -1; + if (!UserMemory::Readable(frame->arg1)) return -1; return (int64_t)Sys_FsFormat((const FsFormatParams*)frame->arg1); case SYS_AUDIOOPEN: return Sys_AudioOpen((uint32_t)frame->arg1, (uint8_t)frame->arg2, (uint8_t)frame->arg3); case SYS_AUDIOCLOSE: return Sys_AudioClose((int)frame->arg1); case SYS_AUDIOWRITE: - if (!ValidUserPtr(frame->arg2)) return -1; + if (!UserMemory::Range(frame->arg2, frame->arg3, false)) return -1; return Sys_AudioWrite((int)frame->arg1, (const uint8_t*)frame->arg2, (uint32_t)frame->arg3); case SYS_AUDIOCTL: return Sys_AudioCtl((int)frame->arg1, (int)frame->arg2, (int)frame->arg3); case SYS_BTSCAN: - if (!ValidUserPtr(frame->arg1)) return -1; + if ((int64_t)frame->arg2 < 0) return -1; + if (!UserMemory::Range(frame->arg1, (uint64_t)frame->arg2 * sizeof(BtScanResult), true)) return -1; return Sys_BtScan((BtScanResult*)frame->arg1, (int)frame->arg2, (uint32_t)frame->arg3); case SYS_BTCONNECT: - if (!ValidUserPtr(frame->arg1)) return -1; + if (!UserMemory::Range(frame->arg1, 6, false)) return -1; return Sys_BtConnect((const uint8_t*)frame->arg1); case SYS_BTDISCONNECT: - if (!ValidUserPtr(frame->arg1)) return -1; + if (!UserMemory::Range(frame->arg1, 6, false)) return -1; return Sys_BtDisconnect((const uint8_t*)frame->arg1); case SYS_BTLIST: - if (!ValidUserPtr(frame->arg1)) return -1; + if ((int64_t)frame->arg2 < 0) return -1; + if (!UserMemory::Range(frame->arg1, (uint64_t)frame->arg2 * sizeof(BtDevInfo), true)) return -1; return Sys_BtList((BtDevInfo*)frame->arg1, (int)frame->arg2); case SYS_BTINFO: - if (!ValidUserPtr(frame->arg1)) return -1; + if (!UserMemory::Writable(frame->arg1)) return -1; return Sys_BtInfo((BtAdapterInfo*)frame->arg1); case SYS_SUSPEND: return Sys_Suspend(); @@ -351,48 +345,53 @@ namespace Montauk { case SYS_GETTZ: return Sys_GetTZ(); case SYS_SETUSER: - if (!ValidUserPtr(frame->arg2)) return -1; + if (!UserMemory::String(frame->arg2, kMaxUserNameBytes)) return -1; return Sys_SetUser((int)frame->arg1, (const char*)frame->arg2); case SYS_GETUSER: - if (!ValidUserPtr(frame->arg1)) return -1; + if (!UserMemory::Range(frame->arg1, frame->arg2, true)) return -1; return Sys_GetUser((char*)frame->arg1, frame->arg2); case SYS_GETCWD: - if (!ValidUserPtr(frame->arg1)) return -1; + if (!UserMemory::Range(frame->arg1, frame->arg2, true)) return -1; return Sys_GetCwd((char*)frame->arg1, frame->arg2); case SYS_CHDIR: - if (!ValidUserPtr(frame->arg1)) return -1; + if (!UserMemory::String(frame->arg1, kMaxPathBytes)) return -1; return Sys_Chdir((const char*)frame->arg1); case SYS_DUPHANDLE: return Sys_DupHandle((int)frame->arg1); case SYS_WAIT_HANDLE: return (int64_t)Sys_WaitHandle((int)frame->arg1, (uint32_t)frame->arg2, frame->arg3); case SYS_STREAM_CREATE: - if (!ValidUserPtr(frame->arg1) || !ValidUserPtr(frame->arg2)) return -1; + if (!UserMemory::Writable(frame->arg1) || !UserMemory::Writable(frame->arg2)) return -1; return Sys_StreamCreate((int*)frame->arg1, (int*)frame->arg2, (uint32_t)frame->arg3); case SYS_STREAM_READ: - if (!ValidUserPtr(frame->arg2)) return -1; + if ((int64_t)frame->arg3 < 0) return -1; + if (!UserMemory::Range(frame->arg2, (uint64_t)frame->arg3, true)) return -1; return Sys_StreamRead((int)frame->arg1, (uint8_t*)frame->arg2, (int)frame->arg3); case SYS_STREAM_WRITE: - if (!ValidUserPtr(frame->arg2)) return -1; + if ((int64_t)frame->arg3 < 0) return -1; + if (!UserMemory::Range(frame->arg2, (uint64_t)frame->arg3, false)) return -1; return Sys_StreamWrite((int)frame->arg1, (const uint8_t*)frame->arg2, (int)frame->arg3); case SYS_MAILBOX_CREATE: - if (!ValidUserPtr(frame->arg1) || !ValidUserPtr(frame->arg2)) return -1; + if (!UserMemory::Writable(frame->arg1) || !UserMemory::Writable(frame->arg2)) return -1; return Sys_MailboxCreate((int*)frame->arg1, (int*)frame->arg2); case SYS_MAILBOX_SEND: - if (frame->arg3 != 0 && !ValidUserPtr(frame->arg3)) return -1; + if (frame->arg4 > 0 && !UserMemory::Range(frame->arg3, frame->arg4, false)) return -1; return Sys_MailboxSend((int)frame->arg1, (uint32_t)frame->arg2, (const void*)frame->arg3, (uint16_t)frame->arg4, (int)frame->arg5); case SYS_MAILBOX_RECV: - if (frame->arg2 != 0 && !IsUserPtr(frame->arg2)) return -1; - if (frame->arg3 != 0 && !IsUserPtr(frame->arg3)) return -1; - if (!ValidUserPtr(frame->arg4)) return -1; - if (frame->arg5 != 0 && !IsUserPtr(frame->arg5)) return -1; + if (!UserMemory::Writable(frame->arg4)) return -1; + if (frame->arg2 != 0 && !UserMemory::Writable(frame->arg2)) return -1; + if (frame->arg3 != 0) { + uint16_t maxCopy = *(uint16_t*)frame->arg4; + if (!UserMemory::Range(frame->arg3, maxCopy, true)) return -1; + } + if (frame->arg5 != 0 && !UserMemory::Writable(frame->arg5)) return -1; return Sys_MailboxRecv((int)frame->arg1, - IsUserPtr(frame->arg2) ? (uint32_t*)frame->arg2 : nullptr, - IsUserPtr(frame->arg3) ? (void*)frame->arg3 : nullptr, + UserMemory::IsUserPtr(frame->arg2) ? (uint32_t*)frame->arg2 : nullptr, + UserMemory::IsUserPtr(frame->arg3) ? (void*)frame->arg3 : nullptr, (uint16_t*)frame->arg4, - IsUserPtr(frame->arg5) ? (int*)frame->arg5 : nullptr); + UserMemory::IsUserPtr(frame->arg5) ? (int*)frame->arg5 : nullptr); case SYS_WAITSET_CREATE: return Sys_WaitsetCreate(); case SYS_WAITSET_ADD: @@ -400,9 +399,9 @@ namespace Montauk { case SYS_WAITSET_REMOVE: return Sys_WaitsetRemove((int)frame->arg1, (int)frame->arg2); case SYS_WAITSET_WAIT: - if (frame->arg2 != 0 && !ValidUserPtr(frame->arg2)) return -1; + if (frame->arg2 != 0 && !UserMemory::Writable(frame->arg2)) return -1; return Sys_WaitsetWait((int)frame->arg1, - IsUserPtr(frame->arg2) ? (IpcWaitResult*)frame->arg2 : nullptr, + UserMemory::IsUserPtr(frame->arg2) ? (IpcWaitResult*)frame->arg2 : nullptr, frame->arg3); case SYS_PROC_OPEN: return Sys_ProcOpen((int)frame->arg1); @@ -413,7 +412,7 @@ namespace Montauk { case SYS_SURFACE_RESIZE: return Sys_SurfaceResize((int)frame->arg1, frame->arg2); case SYS_LOAD_LIB: - if (!ValidUserPtr(frame->arg1)) return 0; + if (!UserMemory::String(frame->arg1, 128)) return 0; return (int64_t)Sys_LoadLib((const char*)frame->arg1); case SYS_UNLOAD_LIB: return Sys_UnloadLib(frame->arg1); @@ -422,21 +421,22 @@ namespace Montauk { case SYS_GETLIBBASE: return (int64_t)Sys_GetLibBase(frame->arg1); case SYS_CRASH_REPORT: + if (!UserMemory::Writable(frame->arg1)) return -1; return Sys_CrashReport((CrashReportInfo*)frame->arg1); case SYS_CLIPBOARD_SET_TEXT: - if (frame->arg2 > 0 && !ValidUserPtr(frame->arg1)) return -1; + if (frame->arg2 > 0 && !UserMemory::Range(frame->arg1, frame->arg2, false)) return -1; return Sys_ClipboardSetText((const char*)frame->arg1, (uint32_t)frame->arg2); case SYS_CLIPBOARD_GET_INFO: - if (!ValidUserPtr(frame->arg1)) return -1; + if (!UserMemory::Writable(frame->arg1)) return -1; return Sys_ClipboardGetInfo((ClipboardInfo*)frame->arg1); case SYS_CLIPBOARD_GET_TEXT: - if (frame->arg2 > 0 && !ValidUserPtr(frame->arg1)) return -1; - if (!ValidUserPtr(frame->arg3)) return -1; - if (frame->arg4 != 0 && !IsUserPtr(frame->arg4)) return -1; - return Sys_ClipboardGetText(IsUserPtr(frame->arg1) ? (char*)frame->arg1 : nullptr, + if (frame->arg2 > 0 && !UserMemory::Range(frame->arg1, frame->arg2, true)) return -1; + if (!UserMemory::Writable(frame->arg3)) return -1; + if (frame->arg4 != 0 && !UserMemory::Writable(frame->arg4)) return -1; + return Sys_ClipboardGetText(UserMemory::IsUserPtr(frame->arg1) ? (char*)frame->arg1 : nullptr, (uint32_t)frame->arg2, (uint32_t*)frame->arg3, - IsUserPtr(frame->arg4) ? (uint64_t*)frame->arg4 : nullptr); + UserMemory::IsUserPtr(frame->arg4) ? (uint64_t*)frame->arg4 : nullptr); case SYS_CLIPBOARD_CLEAR: return Sys_ClipboardClear(); default: diff --git a/kernel/src/CppLib/Alloc.cpp b/kernel/src/CppLib/Alloc.cpp index abbc8c7..3497df3 100644 --- a/kernel/src/CppLib/Alloc.cpp +++ b/kernel/src/CppLib/Alloc.cpp @@ -24,10 +24,15 @@ void operator delete(void* block) void operator delete[](void* block) { - delete block; + Memory::g_heap->Free(block); } void operator delete(void* block, long unsigned int) { Memory::g_heap->Free(block); -} \ No newline at end of file +} + +void operator delete[](void* block, long unsigned int) +{ + Memory::g_heap->Free(block); +} diff --git a/kernel/src/CppLib/Vector.hpp b/kernel/src/CppLib/Vector.hpp index 4ea4750..786fe0e 100644 --- a/kernel/src/CppLib/Vector.hpp +++ b/kernel/src/CppLib/Vector.hpp @@ -20,21 +20,14 @@ namespace kcp template class vector { protected: - T* array; - std::size_t sz; - std::size_t capacity; + T* array = nullptr; + std::size_t sz = 0; + std::size_t capacity = 0; public: - vector() - { - sz = 0; - array = nullptr; - } + vector() = default; vector(std::initializer_list initList) { - sz = 0; - array = nullptr; - for (auto itr = initList.begin(); itr != initList.end(); itr++) { T item = *itr; push_back(item); @@ -43,15 +36,12 @@ namespace kcp T& operator[](std::size_t position) { - if (position > (capacity - 1)) { - kerr << "Vector out of bounds caught!" << Kt::newline; - - return at(0); - } - else - { - return array[position]; + if (position >= sz || array == nullptr) { + Panic("Vector out of bounds caught!", nullptr); + __builtin_unreachable(); } + + return array[position]; } T& at(std::int64_t position) { @@ -62,8 +52,14 @@ namespace kcp size_t _sz = sz; if (capacity < sz + 1) { - array = (T *)Memory::g_heap->Realloc(array, sizeof(T) * (sz + 1)); - capacity++; + size_t newCapacity = (capacity == 0) ? 4 : (capacity * 2); + T* newArray = (T*)Memory::g_heap->Realloc(array, sizeof(T) * newCapacity); + if (newArray == nullptr) { + Panic("Vector allocation failed!", nullptr); + __builtin_unreachable(); + } + array = newArray; + capacity = newCapacity; } array[sz++] = value; @@ -81,4 +77,4 @@ namespace kcp } }; -}; \ No newline at end of file +}; diff --git a/kernel/src/Hal/IDT.cpp b/kernel/src/Hal/IDT.cpp index 5fb077d..e44a6a5 100644 --- a/kernel/src/Hal/IDT.cpp +++ b/kernel/src/Hal/IDT.cpp @@ -17,7 +17,6 @@ namespace Hal { constexpr auto InterruptGate = 0x8E; - constexpr auto TrapGate = 0x8F; InterruptDescriptor* IDT; IDTRStruct IDTR{}; @@ -53,6 +52,7 @@ namespace Hal { "Hypervisor Injection Exception", "VMM Communication Exception", "Security Exception", + "Reserved", "Reserved" }; @@ -196,7 +196,7 @@ namespace Hal { // Use IST1 for NMI (2) and Double Fault (8) so they get a // known-good stack even if the kernel stack has overflowed. uint8_t ist = (I == 2 || I == 8) ? 1 : 0; - IDTEncodeInterrupt(I, (void*)ExceptionHandler, TrapGate, ist); + IDTEncodeInterrupt(I, (void*)ExceptionHandler, InterruptGate, ist); SetHandler::run(); } }; @@ -211,7 +211,7 @@ namespace Hal { IDTR.Base = (uint64_t)IDT; Kt::KernelLogStream(Kt::DEBUG, "IDT") << "Set IDTR Base to " << base::hex << IDTR.Base << " and Limit to " << base::hex << IDTR.Limit; - SetHandler<0, 31>::run(); + SetHandler<0, 32>::run(); Kt::KernelLogStream(Kt::OK, "Hal") << "Created exception interrupt vectors"; diff --git a/kernel/src/Ipc/Ipc.cpp b/kernel/src/Ipc/Ipc.cpp index ea4a7f5..a76ffeb 100644 --- a/kernel/src/Ipc/Ipc.cpp +++ b/kernel/src/Ipc/Ipc.cpp @@ -17,6 +17,7 @@ #include #include #include +#include namespace Ipc { @@ -697,6 +698,7 @@ namespace Ipc { int StreamReadHandle(int handle, uint8_t* out, int maxLen) { if (out == nullptr) return -1; + if (maxLen > 0 && !Montauk::UserMemory::Range((uint64_t)out, (uint64_t)maxLen, true)) return -1; HandleType type = HandleType::None; Object* object = nullptr; @@ -708,6 +710,7 @@ namespace Ipc { int StreamWriteHandle(int handle, const uint8_t* data, int len) { if (data == nullptr) return -1; + if (len > 0 && !Montauk::UserMemory::Range((uint64_t)data, (uint64_t)len, false)) return -1; HandleType type = HandleType::None; Object* object = nullptr; @@ -908,6 +911,8 @@ namespace Ipc { } int MailboxSendHandle(int handle, uint32_t msgType, const void* data, uint16_t len, int attachHandle) { + if (len > 0 && (data == nullptr || !Montauk::UserMemory::Range((uint64_t)data, len, false))) return -1; + HandleType type = HandleType::None; Object* object = nullptr; uint32_t rights = 0; @@ -918,6 +923,13 @@ namespace Ipc { } int MailboxRecvHandle(int handle, uint32_t* msgType, void* data, uint16_t* inOutLen, int* outAttachHandle) { + if (inOutLen == nullptr || !Montauk::UserMemory::Writable((uint64_t)inOutLen)) return -1; + + uint16_t requestedLen = *inOutLen; + if (msgType != nullptr && !Montauk::UserMemory::Writable((uint64_t)msgType)) return -1; + if (requestedLen > 0 && (data == nullptr || !Montauk::UserMemory::Range((uint64_t)data, requestedLen, true))) return -1; + if (outAttachHandle != nullptr && !Montauk::UserMemory::Writable((uint64_t)outAttachHandle)) return -1; + HandleType type = HandleType::None; Object* object = nullptr; uint32_t rights = 0; @@ -978,6 +990,7 @@ namespace Ipc { int FileReadHandle(int handle, uint8_t* buffer, uint64_t offset, uint64_t size) { if (buffer == nullptr) return -1; + if (size > 0 && !Montauk::UserMemory::Range((uint64_t)buffer, size, true)) return -1; HandleType type = HandleType::None; Object* object = nullptr; @@ -989,6 +1002,7 @@ namespace Ipc { int FileWriteHandle(int handle, const uint8_t* buffer, uint64_t offset, uint64_t size) { if (buffer == nullptr) return -1; + if (size > 0 && !Montauk::UserMemory::Range((uint64_t)buffer, size, false)) return -1; HandleType type = HandleType::None; Object* object = nullptr; @@ -1208,6 +1222,7 @@ namespace Ipc { int SocketSendHandle(int handle, const uint8_t* data, uint32_t len) { if (data == nullptr) return -1; + if (len > 0 && !Montauk::UserMemory::Range((uint64_t)data, len, false)) return -1; Socket* socket = nullptr; uint32_t rights = 0; @@ -1223,6 +1238,7 @@ namespace Ipc { int SocketRecvHandle(int handle, uint8_t* buffer, uint32_t maxLen) { if (buffer == nullptr) return -1; + if (maxLen > 0 && !Montauk::UserMemory::Range((uint64_t)buffer, maxLen, true)) return -1; Socket* socket = nullptr; uint32_t rights = 0; @@ -1241,6 +1257,7 @@ namespace Ipc { int SocketSendToHandle(int handle, const uint8_t* data, uint32_t len, uint32_t destIp, uint16_t destPort) { if (data == nullptr) return -1; + if (len > 0 && !Montauk::UserMemory::Range((uint64_t)data, len, false)) return -1; Socket* socket = nullptr; uint32_t rights = 0; @@ -1271,6 +1288,9 @@ namespace Ipc { int SocketRecvFromHandle(int handle, uint8_t* buffer, uint32_t maxLen, uint32_t* srcIp, uint16_t* srcPort) { if (buffer == nullptr) return -1; + if (maxLen > 0 && !Montauk::UserMemory::Range((uint64_t)buffer, maxLen, true)) return -1; + if (srcIp != nullptr && !Montauk::UserMemory::Writable((uint64_t)srcIp)) return -1; + if (srcPort != nullptr && !Montauk::UserMemory::Writable((uint64_t)srcPort)) return -1; Socket* socket = nullptr; uint32_t rights = 0; diff --git a/kernel/src/Memory/Paging.cpp b/kernel/src/Memory/Paging.cpp index 39ae8d9..8df2ad4 100644 --- a/kernel/src/Memory/Paging.cpp +++ b/kernel/src/Memory/Paging.cpp @@ -363,6 +363,53 @@ namespace Memory::VMM { } } + bool Paging::IsUserRangeAccessible(std::uint64_t pml4Phys, std::uint64_t virtualAddress, + std::uint64_t size, bool requireWrite) { + static constexpr uint64_t USER_SPACE_END = 0x0000800000000000ULL; + + if (size == 0) return true; + if (virtualAddress >= USER_SPACE_END) return false; + if (virtualAddress + size < virtualAddress) return false; + if (virtualAddress + size > USER_SPACE_END) return false; + + auto pageAccessible = [&](uint64_t pageVa) -> bool { + VirtualAddress va(pageVa); + PageTable* pml4 = (PageTable*)pml4Phys; + + PageTableEntry* pml4e = (PageTableEntry*)Memory::HHDM(&pml4->entries[va.GetL4Index()]); + if (!pml4e->Present || !pml4e->Supervisor) return false; + if (requireWrite && !pml4e->Writable) return false; + + PageTable* pml3 = (PageTable*)(pml4e->Address << 12); + PageTableEntry* pml3e = (PageTableEntry*)Memory::HHDM(&pml3->entries[va.GetL3Index()]); + if (!pml3e->Present || !pml3e->Supervisor) return false; + if (requireWrite && !pml3e->Writable) return false; + if (pml3e->LargerPages) return true; + + PageTable* pml2 = (PageTable*)(pml3e->Address << 12); + PageTableEntry* pml2e = (PageTableEntry*)Memory::HHDM(&pml2->entries[va.GetL2Index()]); + if (!pml2e->Present || !pml2e->Supervisor) return false; + if (requireWrite && !pml2e->Writable) return false; + if (pml2e->LargerPages) return true; + + PageTable* pml1 = (PageTable*)(pml2e->Address << 12); + PageTableEntry* pte = (PageTableEntry*)Memory::HHDM(&pml1->entries[va.GetPageIndex()]); + if (!pte->Present || !pte->Supervisor) return false; + if (requireWrite && !pte->Writable) return false; + return true; + }; + + uint64_t page = virtualAddress & ~0xFFFULL; + uint64_t lastPage = (virtualAddress + size - 1) & ~0xFFFULL; + for (;;) { + if (!pageAccessible(page)) return false; + if (page == lastPage) break; + page += 0x1000; + } + + return true; + } + // Mask to extract only the 40-bit physical address from a PTE Address field // (bits 12-51 of the PTE). The PageTableEntry::Address field is 52 bits wide // and includes NX, PK, and software-available bits that must be stripped. diff --git a/kernel/src/Memory/Paging.hpp b/kernel/src/Memory/Paging.hpp index 1d05aba..7cce132 100644 --- a/kernel/src/Memory/Paging.hpp +++ b/kernel/src/Memory/Paging.hpp @@ -117,6 +117,12 @@ public: // Skips MMIO/WC pages (WriteThrough or CacheDisabled set in PTE). static void FreeUserHalf(std::uint64_t pml4Phys); + // Validate that every page in the given user range is mapped with + // user permissions. If requireWrite is true, all pages must also be + // writable. + static bool IsUserRangeAccessible(std::uint64_t pml4Phys, std::uint64_t virtualAddress, + std::uint64_t size, bool requireWrite = false); + // Identity-map EFI runtime service regions so firmware code can // reference its own data at physical addresses. void MapEfiRuntime(limine_efi_memmap_response* efiMemmap); diff --git a/kernel/src/Sched/Scheduler.cpp b/kernel/src/Sched/Scheduler.cpp index c2eca79..8837697 100644 --- a/kernel/src/Sched/Scheduler.cpp +++ b/kernel/src/Sched/Scheduler.cpp @@ -18,6 +18,8 @@ #include #include #include +#include +#include #include // Assembly: context switch with CR3 and FPU state parameters @@ -135,6 +137,7 @@ namespace Sched { processTable[i].cwd[0] = '\0'; processTable[i].runningOnCpu = -1; processTable[i].killPending = false; + processTable[i].reapReady = false; processTable[i].waitingForPid = -1; processTable[i].sleepUntilTick = 0; processTable[i].waitingOnObject = nullptr; @@ -184,6 +187,7 @@ namespace Sched { // dispatch this half-initialized process. processTable[slot].state = ProcessState::Running; processTable[slot].runningOnCpu = -1; + processTable[slot].reapReady = false; schedLock.Release(); // Create per-process PML4 with kernel-half copied @@ -319,6 +323,7 @@ namespace Sched { proc.readdirCursor = 0; proc.runningOnCpu = -1; proc.killPending = false; + proc.reapReady = false; proc.waitingForPid = -1; proc.sleepUntilTick = 0; proc.waitingOnObject = nullptr; @@ -417,7 +422,7 @@ namespace Sched { static void ReclaimTerminated() { schedLock.Acquire(); for (int i = 0; i < MaxProcesses; i++) { - if (processTable[i].state == ProcessState::Terminated) { + if (processTable[i].state == ProcessState::Terminated && processTable[i].reapReady) { // Grab the pointers, mark Free, then free memory after releasing lock void* stackBase = (processTable[i].stackBase != 0) ? (void*)processTable[i].stackBase : nullptr; @@ -425,6 +430,7 @@ namespace Sched { ? (void*)Memory::HHDM(processTable[i].pml4Phys) : nullptr; processTable[i].stackBase = 0; processTable[i].pml4Phys = 0; + processTable[i].reapReady = false; processTable[i].state = ProcessState::Free; // Release lock during PFA::Free to minimize hold time @@ -610,6 +616,8 @@ namespace Sched { // Release process-scoped IPC handles/mappings before tearing down the address space. Ipc::CleanupProcessSlot(slot, exitingPid, proc.pml4Phys); + Montauk::CleanupHeapForSlot(slot, proc.pml4Phys); + Montauk::CleanupLibTable(slot); proc.waitingForPid = -1; proc.sleepUntilTick = 0; @@ -640,6 +648,7 @@ namespace Sched { proc.state = ProcessState::Terminated; proc.runningOnCpu = -1; + proc.reapReady = true; // Wake any processes blocked on this PID for (int i = 0; i < MaxProcesses; i++) { @@ -729,12 +738,14 @@ namespace Sched { } // Process is Ready or Blocked (not running on any CPU). - // Mark it Terminated so the scheduler won't pick it up. + // Mark it non-runnable so the scheduler won't pick it up, but do not + // expose it to the BSP reaper until teardown is complete. int killedPid = proc.pid; if (proc.state == ProcessState::Ready) readyCount--; proc.state = ProcessState::Terminated; proc.killPending = false; + proc.reapReady = false; proc.waitingForPid = -1; proc.sleepUntilTick = 0; proc.waitingOnObject = nullptr; @@ -756,6 +767,8 @@ namespace Sched { // Safe to clean up resources now -- process is not running anywhere. WinServer::CleanupProcess(killedPid); Ipc::CleanupProcessSlot(slot, killedPid, proc.pml4Phys); + Montauk::CleanupHeapForSlot(slot, proc.pml4Phys); + Montauk::CleanupLibTable(slot); proc.redirected = false; proc.parentPid = -1; @@ -778,6 +791,10 @@ namespace Sched { Memory::VMM::Paging::FreeUserHalf(proc.pml4Phys); + schedLock.Acquire(); + proc.reapReady = true; + schedLock.Release(); + // Kernel stack and PML4 freed by ReclaimTerminated on BSP tick. return 0; } diff --git a/kernel/src/Sched/Scheduler.hpp b/kernel/src/Sched/Scheduler.hpp index bfe74c8..921c4ca 100644 --- a/kernel/src/Sched/Scheduler.hpp +++ b/kernel/src/Sched/Scheduler.hpp @@ -53,6 +53,7 @@ namespace Sched { int runningOnCpu; // CPU index running this process (-1 if not running) bool killPending = false; // Set by Sys_Kill when target is running on another CPU + bool reapReady = false; // Set once teardown is complete and BSP may free slot resources // I/O redirection for GUI terminal bool redirected = false;