Kernel: Guard against recursive panics

Previously, when we panicked (page-fault for example) while dumping a kernel panic, it would just loop over and over again.

Now, we check if we were already in a panic, and limit the dump:
- No stack trace.
- Only a few registers.
- Only serial (since we can page fault while writing to the framebuffer)

This should make recursive panics much more difficult to achieve.
If we page-fault while writing to console, we don't even see a panic (not even in serial) and eventually triple-fault.
So this patch is actually more user-friendly.
This commit is contained in:
apio 2022-10-18 21:08:21 +02:00
parent 59506b8852
commit bb00e3c112

View File

@ -13,6 +13,26 @@
#include "thread/Scheduler.h"
#include "trace/StackTracer.h"
static bool g_is_in_panic = false;
static bool g_is_in_double_panic = false;
static void panic_prepare_keyboard_triple_fault()
{
PIC::enable_master(0b11111101); // enable keyboard only
PIC::enable_slave(0b11111111);
IDTR idtr;
idtr.limit = 0x0000;
idtr.offset = 0x0000;
asm volatile(
"lidt %0"
:
: "m"(
idtr)); // when an interrupt arrives, triple-fault (the only interrupt that should come is the keyboard one)
asm volatile("sti");
}
void dump_registers(Context* context)
{
kinfoln("-- Registers:");
@ -25,6 +45,32 @@ void dump_registers(Context* context)
kinfoln("ia32_efer: %lx", MSR::read_from(IA32_EFER_MSR));
}
void fatal_dump_registers(Context* context)
{
printf("-- Possibly Relevant Registers:\n");
printf("rbp: %lx, rsp: %lx, rip: %lx, cs: %lx, ss: %lx, rflags: %lx, cr2: %lx\n", context->rbp, context->rsp,
context->rip, context->cs, context->ss, context->rflags, context->cr2);
}
[[noreturn]] void __panic_fatal_stub(Context* context)
{
if (context) fatal_dump_registers(context);
printf("-- No stack trace available\n");
KernelLog::enable_log_backend(Backend::Console);
KernelLog::toggle_log_backend(
Backend::Console); // disable console logging, as we can page fault while writing to the framebuffer.
Scheduler::current_task()->id = 0; // we're panicking, we don't even care anymore. This is so KernelLog shows
// (kernel) instead of (taskname: PID) in the log.
panic_prepare_keyboard_triple_fault();
printf("Press any key to restart.\n");
while (1) asm volatile("hlt");
}
[[noreturn]] void __panic_stub(Context* context)
{
VMM::switch_back_to_kernel_address_space();
@ -50,21 +96,7 @@ void dump_registers(Context* context)
}
else { kinfoln("-- No stack trace available"); }
PIC::enable_master(0b11111101); // enable keyboard only
PIC::enable_slave(0b11111111);
IDTR idtr;
idtr.limit = 0x0000;
idtr.offset = 0x0000;
asm volatile(
"lidt %0"
:
: "m"(
idtr)); // when an interrupt arrives, triple-fault (the only interrupt that should come is the keyboard one)
asm volatile("sti");
kinfoln("Press any key to restart.");
panic_prepare_keyboard_triple_fault();
while (1) asm volatile("hlt");
}
@ -73,6 +105,22 @@ extern "C" [[noreturn]] bool __do_int_panic(Context* context, const char* file,
{
asm volatile("cli");
if (g_is_in_double_panic)
{
panic_prepare_keyboard_triple_fault();
while (1) asm volatile("hlt");
}
if (g_is_in_panic)
{
g_is_in_double_panic = true;
printf("Kernel panic while panicking (showing vital information only) at %s, line %d: %s\n", file, line,
message);
__panic_fatal_stub(context);
}
g_is_in_panic = true;
KernelLog::enable_log_backend(Backend::Console);
TextRenderer::reset();
@ -95,6 +143,22 @@ extern "C" [[noreturn]] bool __do_panic(const char* file, int line, const char*
{
asm volatile("cli");
if (g_is_in_double_panic)
{
panic_prepare_keyboard_triple_fault();
while (1) asm volatile("hlt");
}
if (g_is_in_panic)
{
g_is_in_double_panic = true;
printf("Kernel panic while panicking (showing vital information only) at %s, line %d: %s\n", file, line,
message);
__panic_fatal_stub(nullptr);
}
g_is_in_panic = true;
KernelLog::enable_log_backend(Backend::Console);
TextRenderer::reset();