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:
@@ -7,6 +7,8 @@
|
||||
#include <kernel/Terminal/TTY.h>
|
||||
#include <kernel/Timer/Timer.h>
|
||||
|
||||
#include <BAN/ScopeGuard.h>
|
||||
|
||||
#include <LibDEFLATE/Compressor.h>
|
||||
#include <LibQR/QRCode.h>
|
||||
|
||||
@@ -14,6 +16,13 @@
|
||||
|
||||
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
|
||||
{
|
||||
|
||||
@@ -45,48 +54,25 @@ namespace Debug
|
||||
|
||||
SpinLockGuard _(s_debug_lock);
|
||||
|
||||
const stackframe* frame = reinterpret_cast<const stackframe*>(bp);
|
||||
|
||||
void* first_ip = frame->ip;
|
||||
void* last_ip = 0;
|
||||
bool first = true;
|
||||
const bool temp = g_safe_user_alloc_nonexisting;
|
||||
g_safe_user_alloc_nonexisting = false;
|
||||
BAN::ScopeGuard alloc_restore([temp] { g_safe_user_alloc_nonexisting = temp; });
|
||||
|
||||
BAN::Formatter::print(Debug::putchar, "\e[36mStack trace:\r\n");
|
||||
|
||||
if (ip != 0)
|
||||
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))
|
||||
{
|
||||
derrorln("invalid pointer {H}", (vaddr_t)frame);
|
||||
BAN::Formatter::print(Debug::putchar, " {}\r\n", reinterpret_cast<void*>(frame.ip));
|
||||
if (!safe_user_memcpy(&frame, frame.bp, sizeof(stackframe)))
|
||||
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");
|
||||
}
|
||||
|
||||
|
||||
@@ -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)
|
||||
{
|
||||
if (g_paniced)
|
||||
@@ -214,6 +216,16 @@ namespace Kernel
|
||||
PageFaultError page_fault_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);
|
||||
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);
|
||||
@@ -234,8 +246,7 @@ namespace Kernel
|
||||
if (result.value())
|
||||
return;
|
||||
|
||||
const uint8_t* ip = reinterpret_cast<const uint8_t*>(interrupt_stack->ip);
|
||||
for (const auto& safe_user : s_safe_user_page_faults)
|
||||
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;
|
||||
|
||||
Reference in New Issue
Block a user