diff --git a/kernel/src/Api/BuildNo.hpp b/kernel/src/Api/BuildNo.hpp index 888cadb..4e12ae4 100644 --- a/kernel/src/Api/BuildNo.hpp +++ b/kernel/src/Api/BuildNo.hpp @@ -12,4 +12,4 @@ #pragma once -#define MONTAUK_BUILD_NUMBER 106 +#define MONTAUK_BUILD_NUMBER 108 diff --git a/kernel/src/Api/CrashReportSyscall.hpp b/kernel/src/Api/CrashReportSyscall.hpp index 2841acb..20a7c8d 100644 --- a/kernel/src/Api/CrashReportSyscall.hpp +++ b/kernel/src/Api/CrashReportSyscall.hpp @@ -34,6 +34,9 @@ namespace montauk::abi { out->codeSegment = rep.codeSegment; out->flags = rep.flags; out->stackSegment = rep.stackSegment; + out->stackWordCount = rep.stackWordCount; + for (int i = 0; i < rep.stackWordCount && i < CrashReport::StackSnapshotWords; i++) + out->stackWords[i] = rep.stackWords[i]; out->pfPresent = rep.pfPresent; out->pfWrite = rep.pfWrite; out->pfUser = rep.pfUser; diff --git a/kernel/src/Api/Syscall.hpp b/kernel/src/Api/Syscall.hpp index f5caa9f..20ed932 100644 --- a/kernel/src/Api/Syscall.hpp +++ b/kernel/src/Api/Syscall.hpp @@ -780,6 +780,8 @@ namespace montauk::abi { uint64_t codeSegment; uint64_t flags; uint64_t stackSegment; + uint64_t stackWords[8]; + uint8_t stackWordCount; uint8_t pfPresent : 1; uint8_t pfWrite : 1; uint8_t pfUser : 1; diff --git a/kernel/src/Hal/IDT.cpp b/kernel/src/Hal/IDT.cpp index 496779e..1a0a7ad 100644 --- a/kernel/src/Hal/IDT.cpp +++ b/kernel/src/Hal/IDT.cpp @@ -127,6 +127,21 @@ namespace Hal { rep.stackPointer = regs->SP; rep.stackSegment = regs->SS; + // An indirect CALL pushes its real continuation address before a + // bad target faults on instruction fetch. Preserve a small stack + // snapshot while the dying process address space is still active; + // stackWords[0] will usually identify the caller even when RIP is + // corrupted data and cannot itself be symbolicated. + constexpr uint64_t snapshotBytes = + CrashReport::StackSnapshotWords * sizeof(uint64_t); + if (Memory::VMM::Paging::IsUserRangeAccessible( + proc->pml4Phys, regs->SP, snapshotBytes, false)) { + auto* userStack = (const volatile uint64_t*)regs->SP; + for (int word = 0; word < CrashReport::StackSnapshotWords; word++) + rep.stackWords[word] = userStack[word]; + rep.stackWordCount = CrashReport::StackSnapshotWords; + } + if (i == 0x0E) { // Page fault: read CR2 for faulting address asm volatile("mov %%cr2, %0" : "=r"(rep.faultingAddress)); diff --git a/kernel/src/Sched/CrashReport.hpp b/kernel/src/Sched/CrashReport.hpp index 2667215..32140ae 100644 --- a/kernel/src/Sched/CrashReport.hpp +++ b/kernel/src/Sched/CrashReport.hpp @@ -11,6 +11,7 @@ namespace CrashReport { static constexpr int MaxReports = 8; +static constexpr int StackSnapshotWords = 8; struct Report { int pid; @@ -24,6 +25,8 @@ struct Report { uint64_t codeSegment; uint64_t flags; uint64_t stackSegment; + uint64_t stackWords[StackSnapshotWords]; + uint8_t stackWordCount; // Page fault error code bits (vector 0xE / page fault only) uint8_t pfPresent : 1; diff --git a/programs/include/Api/Syscall.hpp b/programs/include/Api/Syscall.hpp index 5f20e52..c1a96f5 100644 --- a/programs/include/Api/Syscall.hpp +++ b/programs/include/Api/Syscall.hpp @@ -718,6 +718,8 @@ namespace montauk::abi { uint64_t codeSegment; uint64_t flags; uint64_t stackSegment; + uint64_t stackWords[8]; + uint8_t stackWordCount; uint8_t pfPresent : 1; uint8_t pfWrite : 1; uint8_t pfUser : 1; diff --git a/programs/include/gui/mtk/widgets.hpp b/programs/include/gui/mtk/widgets.hpp index 1b71e4c..b77abe2 100644 --- a/programs/include/gui/mtk/widgets.hpp +++ b/programs/include/gui/mtk/widgets.hpp @@ -40,6 +40,8 @@ struct ContextMenuItem { const char* label; int id; bool enabled; + const char* shortcut; + bool separator_after; }; enum ContextMenuResult : int { @@ -226,6 +228,19 @@ inline void draw_context_menu(Canvas& c, Color label_color = items[i].enabled ? theme.text : theme.text_muted; context_menu_draw_label(c, menu.x + 12, item.y + (item.h - fh) / 2, items[i].label, label_color, font, font_size); + if (items[i].shortcut && items[i].shortcut[0]) { + int shortcut_w = font && font->valid && font_size > 0 + ? font->measure_text(items[i].shortcut, font_size) + : text_width(items[i].shortcut); + context_menu_draw_label(c, menu.x + menu.w - 12 - shortcut_w, + item.y + (item.h - fh) / 2, + items[i].shortcut, theme.text_muted, + font, font_size); + } + if (items[i].separator_after) { + c.hline(menu.x + 10, item.y + item.h - 1, + menu.w - 20, theme.border); + } } } diff --git a/programs/src/crashpad/main.cpp b/programs/src/crashpad/main.cpp index 6c70ce0..ad00a64 100644 --- a/programs/src/crashpad/main.cpp +++ b/programs/src/crashpad/main.cpp @@ -212,6 +212,28 @@ static void render() { y += SECTION_GAP; } + if (g_report.stackWordCount != 0) { + section_header(c, y, w, fh, "Stack Snapshot"); + for (int i = 0; i < g_report.stackWordCount && i < 8; i++) { + char label[24] = "Stack + 0x00"; + static const char digits[] = "0123456789ABCDEF"; + unsigned offset = (unsigned)i * 8; + label[10] = digits[(offset >> 4) & 0xF]; + label[11] = digits[offset & 0xF]; + if (i == 0) { + label[12] = ' '; + label[13] = '('; + label[14] = 'r'; + label[15] = 'e'; + label[16] = 't'; + label[17] = ')'; + label[18] = '\0'; + } + format_hex16(hexbuf, g_report.stackWords[i]); + table_row(c, y, row_h, COL1_W, w, rowIdx++, label, hexbuf, VALUE_COL); + } + } + g_win.present(); } @@ -248,6 +270,14 @@ static void render_console() { emit_kv_hex("RFLAGS", g_report.flags); if (g_report.exceptionVector == 0x0E) emit_kv_hex("CR2 (fault addr)", g_report.faultingAddress); + for (int i = 0; i < g_report.stackWordCount && i < 8; i++) { + char key[] = "Stack + 0x00"; + static const char digits[] = "0123456789ABCDEF"; + unsigned offset = (unsigned)i * 8; + key[10] = digits[(offset >> 4) & 0xF]; + key[11] = digits[offset & 0xF]; + emit_kv_hex(key, g_report.stackWords[i]); + } emit("\n"); }