From 88ff98c32589e25139212416e7b4302ee18c57f6 Mon Sep 17 00:00:00 2001 From: Daniel Hammer Date: Fri, 27 Mar 2026 20:15:37 +0100 Subject: [PATCH] fix: SYS_READDIR memory leak --- kernel/src/Api/Filesystem.hpp | 31 ++++++++++++++++++++++++------- kernel/src/Sched/Scheduler.cpp | 2 ++ kernel/src/Sched/Scheduler.hpp | 4 ++++ 3 files changed, 30 insertions(+), 7 deletions(-) diff --git a/kernel/src/Api/Filesystem.hpp b/kernel/src/Api/Filesystem.hpp index 67f236b..6b822e5 100644 --- a/kernel/src/Api/Filesystem.hpp +++ b/kernel/src/Api/Filesystem.hpp @@ -48,16 +48,33 @@ namespace Montauk { auto* proc = Sched::GetCurrentProcessPtr(); if (proc == nullptr) return -1; - void* page = Memory::g_pfa->AllocateZeroed(); - if (page == nullptr) return -1; - uint64_t physAddr = Memory::SubHHDM((uint64_t)page); - uint64_t userVa = proc->heapNext; - proc->heapNext += 0x1000; - if (!Memory::VMM::Paging::MapUserIn(proc->pml4Phys, physAddr, userVa)) return -1; + // Use a rotating ring of scratch pages below the heap instead of + // bumping heapNext on every call. This keeps repeated directory scans + // from leaking user heap space while still allowing nested callers to + // hold multiple readdir results at once. + uint32_t slot = proc->readdirCursor % Sched::UserReadDirSlots; + proc->readdirCursor = (slot + 1) % Sched::UserReadDirSlots; + + uint64_t userVa = Sched::UserReadDirBase + (uint64_t)slot * 0x1000ULL; + uint64_t physAddr = Memory::VMM::Paging::GetPhysAddr(proc->pml4Phys, userVa); + uint8_t* pageBuf = nullptr; + + if (physAddr == 0) { + void* page = Memory::g_pfa->AllocateZeroed(); + if (page == nullptr) return -1; + physAddr = Memory::SubHHDM((uint64_t)page); + if (!Memory::VMM::Paging::MapUserIn(proc->pml4Phys, physAddr, userVa)) { + Memory::g_pfa->Free(page); + return -1; + } + pageBuf = (uint8_t*)page; + } else { + pageBuf = (uint8_t*)Memory::HHDM(physAddr); + memset(pageBuf, 0, 0x1000); + } // Copy strings into the user page and write pointers to outNames uint64_t offset = 0; - uint8_t* pageBuf = (uint8_t*)Memory::HHDM(physAddr); int copied = 0; for (int i = 0; i < count; i++) { int len = Lib::strlen(kernelNames[i]) + 1; diff --git a/kernel/src/Sched/Scheduler.cpp b/kernel/src/Sched/Scheduler.cpp index a8e14b7..4476286 100644 --- a/kernel/src/Sched/Scheduler.cpp +++ b/kernel/src/Sched/Scheduler.cpp @@ -89,6 +89,7 @@ namespace Sched { processTable[i].kernelStackTop = 0; processTable[i].userStackTop = 0; processTable[i].heapNext = 0; + processTable[i].readdirCursor = 0; processTable[i].args[0] = '\0'; processTable[i].user[0] = '\0'; processTable[i].cwd[0] = '\0'; @@ -270,6 +271,7 @@ namespace Sched { proc.kernelStackTop = kernelStackTop; proc.userStackTop = UserStackTop - 8; proc.heapNext = UserHeapBase; + proc.readdirCursor = 0; proc.runningOnCpu = -1; proc.killPending = false; proc.waitingForPid = -1; diff --git a/kernel/src/Sched/Scheduler.hpp b/kernel/src/Sched/Scheduler.hpp index 93ac46a..6331bbe 100644 --- a/kernel/src/Sched/Scheduler.hpp +++ b/kernel/src/Sched/Scheduler.hpp @@ -17,6 +17,9 @@ namespace Sched { static constexpr uint64_t UserStackSize = UserStackPages * 0x1000; static constexpr uint64_t UserStackTop = 0x7FFFFFF000ULL; // User stack top VA static constexpr uint64_t UserHeapBase = 0x40000000ULL; // User heap start VA + static constexpr uint32_t UserReadDirSlots = 64; // rotating scratch pages for SYS_READDIR + static constexpr uint64_t UserReadDirBase = + UserHeapBase - (uint64_t)UserReadDirSlots * 0x1000ULL; static constexpr uint64_t ExitStubAddr = 0x3FF000ULL; // User-space exit stub page static constexpr uint64_t TimeSliceMs = 10; // 10 ms time slice @@ -42,6 +45,7 @@ namespace Sched { uint64_t kernelStackTop; // Top of kernel stack (for TSS RSP0 / SYSCALL) uint64_t userStackTop; // User-space stack top uint64_t heapNext; // Simple bump allocator for user heap + uint32_t readdirCursor; // Next SYS_READDIR scratch slot char args[256]; // Command-line arguments (set by parent via Spawn) char user[32]; // Owner user name (inherited from parent on spawn) char cwd[256]; // Absolute current working directory