From 1d0f18cab91d1f30817f0827248b6151420c13e9 Mon Sep 17 00:00:00 2001 From: apio Date: Wed, 1 May 2024 10:52:08 +0200 Subject: [PATCH] kernel: Move stack checking and expansion into an architecture-independent file --- kernel/src/arch/x86_64/CPU.cpp | 62 +--------------------------------- kernel/src/thread/Thread.cpp | 61 +++++++++++++++++++++++++++++++++ kernel/src/thread/Thread.h | 2 ++ 3 files changed, 64 insertions(+), 61 deletions(-) diff --git a/kernel/src/arch/x86_64/CPU.cpp b/kernel/src/arch/x86_64/CPU.cpp index 9c5e2256..2e2ac01e 100644 --- a/kernel/src/arch/x86_64/CPU.cpp +++ b/kernel/src/arch/x86_64/CPU.cpp @@ -14,7 +14,6 @@ #include "video/TextConsole.h" #include #include -#include #include #include #include @@ -57,8 +56,6 @@ void FPData::restore() asm volatile("fxrstor (%0)" : : "r"(m_data)); } -static constexpr usize MAX_STACK_SIZE = 8 * 1024 * 1024; // 8 MB - // Interrupt handling #define FIXME_UNHANDLED_INTERRUPT(name) \ @@ -78,63 +75,6 @@ void decode_page_fault_error_code(u64 code) (code & PF_RESERVED) ? " | Reserved bits set" : "", (code & PF_NX_VIOLATION) ? " | NX violation" : ""); } -static bool check_stack(Thread* current, Registers* regs) -{ - if (regs->rsp < current->stack.bottom() || regs->rsp >= current->stack.top()) - kerrorln("Abnormal stack (RSP outside the normal range, %.16lx-%.16lx)", current->stack.bottom(), - current->stack.top()); - - if (regs->rsp >= (current->stack.bottom() - (ARCH_PAGE_SIZE * 8)) && regs->rsp < current->stack.bottom()) - { - kerrorln("Likely stack overflow (CPU exception a few pages below the stack)"); - // Try to grow the stack - // FIXME: This should be extracted into an architecture-independent file. - usize stack_space_remaining = MAX_STACK_SIZE - current->stack.bytes(); - if (!stack_space_remaining) - { - kwarnln("Failed to grow stack: this thread already used up all its stack space"); - return false; - } - - usize exceeded_bytes = align_up(current->stack.bottom() - regs->rsp); - if (exceeded_bytes > stack_space_remaining) - { - kwarnln("Failed to grow stack: this thread needs more space than the one it has remaining (%zu bytes out " - "of %zu remaining)", - exceeded_bytes, stack_space_remaining); - return false; - } - - // If we can, we'll add 2 more pages of buffer space, otherwise we use whatever we can. - usize bytes_to_grow = min(stack_space_remaining, exceeded_bytes + 2 * ARCH_PAGE_SIZE); - auto maybe_base = - current->address_space->grow_region(current->stack.bottom(), bytes_to_grow / ARCH_PAGE_SIZE, true); - if (maybe_base.has_error()) - { - kwarnln("Failed to grow stack: could not allocate virtual memory space (%s)", maybe_base.error_string()); - return false; - } - - u64 base = maybe_base.release_value(); - auto result = MemoryManager::alloc_at_zeroed(base, bytes_to_grow / ARCH_PAGE_SIZE, - MMU::ReadWrite | MMU::NoExecute | MMU::User); - if (result.has_error()) - { - current->address_space->free_region(base, bytes_to_grow / ARCH_PAGE_SIZE); - kwarnln("Failed to grow stack: could not allocate physical pages (%s)", result.error_string()); - return false; - } - - kinfoln("Stack expanded from %lx (%zu bytes) to %lx (%zu bytes)", current->stack.bottom(), - current->stack.bytes(), base, current->stack.bytes() + bytes_to_grow); - - current->stack = Stack { base, current->stack.bytes() + bytes_to_grow }; - return true; - } - - return false; -} - void handle_cpu_exception(int signo, const char* err, Registers* regs) { if (err) kerrorln("Caught CPU exception: %s", err); @@ -148,7 +88,7 @@ void handle_cpu_exception(int signo, const char* err, Registers* regs) if (!is_in_kernel(regs)) { auto* current = Scheduler::current(); - if (check_stack(current, regs)) return; + if (current->check_stack_on_exception(regs->rsp)) return; current->send_signal(signo); current->process_pending_signals(regs); diff --git a/kernel/src/thread/Thread.cpp b/kernel/src/thread/Thread.cpp index 61956fdf..b5062693 100644 --- a/kernel/src/thread/Thread.cpp +++ b/kernel/src/thread/Thread.cpp @@ -7,6 +7,7 @@ #include #include #include +#include #include #include #include @@ -265,6 +266,66 @@ void Thread::send_signal(int signo) } } +static constexpr usize MAX_STACK_SIZE = 8 * 1024 * 1024; // 8 MB + +bool Thread::check_stack_on_exception(u64 stack_pointer) +{ + if (stack_pointer < stack.bottom() || stack_pointer >= stack.top()) + kwarnln("Abnormal stack (Stack pointer outside the normal range, %.16lx-%.16lx)", stack.bottom(), stack.top()); + + // Check whether the stack pointer is within 8 pages of the bottom of the stack + u64 threshold = stack.bottom() - (ARCH_PAGE_SIZE * 8); + + if (stack_pointer >= threshold && stack_pointer < stack.bottom()) + { + kwarnln("Likely stack overflow (CPU exception a few pages below the stack)"); + + // Try to grow the stack + usize stack_space_remaining = MAX_STACK_SIZE - stack.bytes(); + if (!stack_space_remaining) + { + kwarnln("Failed to grow stack: this thread already used up all its stack space"); + return false; + } + + usize exceeded_bytes = align_up(stack.bottom() - stack_pointer); + if (exceeded_bytes > stack_space_remaining) + { + kwarnln("Failed to grow stack: this thread needs more space than the one it has remaining (%zu bytes out " + "of %zu remaining)", + exceeded_bytes, stack_space_remaining); + return false; + } + + // If we can, we'll add 2 more pages of buffer space, otherwise we use whatever we can. + usize bytes_to_grow = min(stack_space_remaining, exceeded_bytes + 2 * ARCH_PAGE_SIZE); + auto maybe_base = address_space->grow_region(stack.bottom(), bytes_to_grow / ARCH_PAGE_SIZE, true); + if (maybe_base.has_error()) + { + kwarnln("Failed to grow stack: could not allocate virtual memory space (%s)", maybe_base.error_string()); + return false; + } + + u64 base = maybe_base.release_value(); + auto result = MemoryManager::alloc_at_zeroed(base, bytes_to_grow / ARCH_PAGE_SIZE, + MMU::ReadWrite | MMU::NoExecute | MMU::User); + if (result.has_error()) + { + address_space->free_region(base, bytes_to_grow / ARCH_PAGE_SIZE); + kwarnln("Failed to grow stack: could not allocate physical pages (%s)", result.error_string()); + return false; + } + + kinfoln("Stack expanded from %lx (%zu bytes) to %lx (%zu bytes)", stack.bottom(), stack.bytes(), base, + stack.bytes() + bytes_to_grow); + + stack = Stack { base, stack.bytes() + bytes_to_grow }; + return true; + } + + return false; +} + void Thread::stop() { state = ThreadState::Stopped; diff --git a/kernel/src/thread/Thread.h b/kernel/src/thread/Thread.h index 9b189c01..3b5a2106 100644 --- a/kernel/src/thread/Thread.h +++ b/kernel/src/thread/Thread.h @@ -169,6 +169,8 @@ struct Thread : public LinkedListNode Result push_mem_on_stack(const u8* mem, usize size); Result pop_mem_from_stack(u8* mem, usize size); + bool Thread::check_stack_on_exception(u64 stack_pointer); + void stop(); void resume();