Kernel: Implement stack trace dump with safe memcpy

This fixes kernel panic if the stack trace cannot be read. Manually
validating pointers is definitely not safe
This commit is contained in:
2026-04-21 21:20:52 +03:00
parent b74812d669
commit 6b43cadf3a
2 changed files with 32 additions and 35 deletions

View File

@@ -7,6 +7,8 @@
#include <kernel/Terminal/TTY.h> #include <kernel/Terminal/TTY.h>
#include <kernel/Timer/Timer.h> #include <kernel/Timer/Timer.h>
#include <BAN/ScopeGuard.h>
#include <LibDEFLATE/Compressor.h> #include <LibDEFLATE/Compressor.h>
#include <LibQR/QRCode.h> #include <LibQR/QRCode.h>
@@ -14,6 +16,13 @@
bool g_disable_debug = false; bool g_disable_debug = false;
extern "C" bool safe_user_memcpy(void*, const void*, size_t);
namespace Kernel
{
extern bool g_safe_user_alloc_nonexisting;
}
namespace Debug namespace Debug
{ {
@@ -45,48 +54,25 @@ namespace Debug
SpinLockGuard _(s_debug_lock); SpinLockGuard _(s_debug_lock);
const stackframe* frame = reinterpret_cast<const stackframe*>(bp); const bool temp = g_safe_user_alloc_nonexisting;
g_safe_user_alloc_nonexisting = false;
void* first_ip = frame->ip; BAN::ScopeGuard alloc_restore([temp] { g_safe_user_alloc_nonexisting = temp; });
void* last_ip = 0;
bool first = true;
BAN::Formatter::print(Debug::putchar, "\e[36mStack trace:\r\n"); BAN::Formatter::print(Debug::putchar, "\e[36mStack trace:\r\n");
if (ip != 0) if (ip != 0)
BAN::Formatter::print(Debug::putchar, " {}\r\n", reinterpret_cast<void*>(ip)); BAN::Formatter::print(Debug::putchar, " {}\r\n", reinterpret_cast<void*>(ip));
while (frame) stackframe frame;
if (!safe_user_memcpy(&frame, reinterpret_cast<void*>(bp), sizeof(stackframe)))
;
else for (size_t depth = 0; depth < 64; depth++)
{ {
if (!PageTable::is_valid_pointer((vaddr_t)frame)) BAN::Formatter::print(Debug::putchar, " {}\r\n", reinterpret_cast<void*>(frame.ip));
{ if (!safe_user_memcpy(&frame, frame.bp, sizeof(stackframe)))
derrorln("invalid pointer {H}", (vaddr_t)frame);
break; break;
}
if (PageTable::current().is_page_free((vaddr_t)frame & PAGE_ADDR_MASK))
{
derrorln(" {} not mapped", frame);
break;
}
BAN::Formatter::print(Debug::putchar, " {}\r\n", (void*)frame->ip);
if (!first && frame->ip == first_ip)
{
derrorln("looping kernel panic :(");
break;
}
else if (!first && frame->ip == last_ip)
{
derrorln("repeating stack trace");
break;
}
last_ip = frame->ip;
frame = frame->bp;
first = false;
} }
BAN::Formatter::print(Debug::putchar, "\e[m"); BAN::Formatter::print(Debug::putchar, "\e[m");
} }

View File

@@ -191,6 +191,8 @@ namespace Kernel
}, },
}; };
bool g_safe_user_alloc_nonexisting { true };
extern "C" void cpp_isr_handler(uint32_t isr, uint32_t error, InterruptStack* interrupt_stack, const Registers* regs) extern "C" void cpp_isr_handler(uint32_t isr, uint32_t error, InterruptStack* interrupt_stack, const Registers* regs)
{ {
if (g_paniced) if (g_paniced)
@@ -214,6 +216,16 @@ namespace Kernel
PageFaultError page_fault_error; PageFaultError page_fault_error;
page_fault_error.raw = error; page_fault_error.raw = error;
const uint8_t* ip = reinterpret_cast<const uint8_t*>(interrupt_stack->ip);
if (!g_safe_user_alloc_nonexisting) for (const auto& safe_user : s_safe_user_page_faults)
{
if (ip < safe_user.ip_start || ip >= safe_user.ip_end)
continue;
interrupt_stack->ip = reinterpret_cast<vaddr_t>(safe_user.ip_fault);
return;
}
Processor::set_interrupt_state(InterruptState::Enabled); Processor::set_interrupt_state(InterruptState::Enabled);
auto result = Process::current().allocate_page_for_demand_paging(regs->cr2, page_fault_error.write, page_fault_error.instruction); auto result = Process::current().allocate_page_for_demand_paging(regs->cr2, page_fault_error.write, page_fault_error.instruction);
Processor::set_interrupt_state(InterruptState::Disabled); Processor::set_interrupt_state(InterruptState::Disabled);
@@ -234,8 +246,7 @@ namespace Kernel
if (result.value()) if (result.value())
return; return;
const uint8_t* ip = reinterpret_cast<const uint8_t*>(interrupt_stack->ip); if (g_safe_user_alloc_nonexisting) for (const auto& safe_user : s_safe_user_page_faults)
for (const auto& safe_user : s_safe_user_page_faults)
{ {
if (ip < safe_user.ip_start || ip >= safe_user.ip_end) if (ip < safe_user.ip_start || ip >= safe_user.ip_end)
continue; continue;