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/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");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -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;
|
||||||
|
|||||||
Reference in New Issue
Block a user