fix: lock crash report ring buffer (SMP race) and guard crashpad crash loop
This commit is contained in:
@@ -12,4 +12,4 @@
|
|||||||
|
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#define MONTAUK_BUILD_NUMBER 128
|
#define MONTAUK_BUILD_NUMBER 129
|
||||||
|
|||||||
@@ -13,38 +13,40 @@ namespace montauk::abi {
|
|||||||
|
|
||||||
static int64_t Sys_CrashReport(CrashReportInfo* out) {
|
static int64_t Sys_CrashReport(CrashReportInfo* out) {
|
||||||
if (!out) return 0;
|
if (!out) return 0;
|
||||||
if (CrashReport::g_reportCount == 0) return 0;
|
|
||||||
|
|
||||||
int oldestIdx = (CrashReport::g_reportHead - CrashReport::g_reportCount + CrashReport::MaxReports) % CrashReport::MaxReports;
|
// Pop the oldest report into a kernel-local copy under the ring lock,
|
||||||
auto* rep = &CrashReport::g_reports[oldestIdx];
|
// then marshal into user memory with the lock released.
|
||||||
|
CrashReport::Report rep;
|
||||||
|
int had = CrashReport::TakeOldest(rep);
|
||||||
|
if (had == 0) return 0;
|
||||||
|
|
||||||
out->pid = rep->pid;
|
out->pid = rep.pid;
|
||||||
int pn;
|
int pn;
|
||||||
for (pn = 0; pn < 63 && rep->processName[pn]; pn++) out->processName[pn] = rep->processName[pn];
|
for (pn = 0; pn < 63 && rep.processName[pn]; pn++) out->processName[pn] = rep.processName[pn];
|
||||||
out->processName[pn] = '\0';
|
out->processName[pn] = '\0';
|
||||||
out->exceptionVector = rep->exceptionVector;
|
out->exceptionVector = rep.exceptionVector;
|
||||||
int en;
|
int en;
|
||||||
for (en = 0; en < 31 && rep->exceptionName[en]; en++) out->exceptionName[en] = rep->exceptionName[en];
|
for (en = 0; en < 31 && rep.exceptionName[en]; en++) out->exceptionName[en] = rep.exceptionName[en];
|
||||||
out->exceptionName[en] = '\0';
|
out->exceptionName[en] = '\0';
|
||||||
out->faultingAddress = rep->faultingAddress;
|
out->faultingAddress = rep.faultingAddress;
|
||||||
out->instructionPointer = rep->instructionPointer;
|
out->instructionPointer = rep.instructionPointer;
|
||||||
out->stackPointer = rep->stackPointer;
|
out->stackPointer = rep.stackPointer;
|
||||||
out->codeSegment = rep->codeSegment;
|
out->codeSegment = rep.codeSegment;
|
||||||
out->flags = rep->flags;
|
out->flags = rep.flags;
|
||||||
out->stackSegment = rep->stackSegment;
|
out->stackSegment = rep.stackSegment;
|
||||||
out->pfPresent = rep->pfPresent;
|
out->pfPresent = rep.pfPresent;
|
||||||
out->pfWrite = rep->pfWrite;
|
out->pfWrite = rep.pfWrite;
|
||||||
out->pfUser = rep->pfUser;
|
out->pfUser = rep.pfUser;
|
||||||
out->pfReservedWrite = rep->pfReservedWrite;
|
out->pfReservedWrite = rep.pfReservedWrite;
|
||||||
out->pfInstructionFetch = rep->pfInstructionFetch;
|
out->pfInstructionFetch = rep.pfInstructionFetch;
|
||||||
out->pfProtectionKey = rep->pfProtectionKey;
|
out->pfProtectionKey = rep.pfProtectionKey;
|
||||||
out->pfShadowStack = rep->pfShadowStack;
|
out->pfShadowStack = rep.pfShadowStack;
|
||||||
out->pfSGX = rep->pfSGX;
|
out->pfSGX = rep.pfSGX;
|
||||||
out->timestampTick = rep->timestampTick;
|
out->timestampTick = rep.timestampTick;
|
||||||
rep->valid = false;
|
|
||||||
CrashReport::g_reportCount--;
|
|
||||||
|
|
||||||
return CrashReport::g_reportCount + 1;
|
// Preserve the original contract: count of reports present before this
|
||||||
|
// call (i.e. including the one just returned).
|
||||||
|
return had;
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -5,6 +5,7 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
#include "CrashReport.hpp"
|
#include "CrashReport.hpp"
|
||||||
|
#include <CppLib/Spinlock.hpp>
|
||||||
|
|
||||||
namespace CrashReport {
|
namespace CrashReport {
|
||||||
|
|
||||||
@@ -12,10 +13,31 @@ Report g_reports[MaxReports];
|
|||||||
int g_reportHead = 0;
|
int g_reportHead = 0;
|
||||||
int g_reportCount = 0;
|
int g_reportCount = 0;
|
||||||
|
|
||||||
|
// Serializes ring-buffer access. Interrupt-disabling: the writer runs inside
|
||||||
|
// the exception handler (IF=0) while readers come from syscall context.
|
||||||
|
static kcp::Spinlock g_lock;
|
||||||
|
|
||||||
void AddReport(const Report& report) {
|
void AddReport(const Report& report) {
|
||||||
|
g_lock.Acquire();
|
||||||
g_reports[g_reportHead] = report;
|
g_reports[g_reportHead] = report;
|
||||||
g_reportHead = (g_reportHead + 1) % MaxReports;
|
g_reportHead = (g_reportHead + 1) % MaxReports;
|
||||||
if (g_reportCount < MaxReports) g_reportCount++;
|
if (g_reportCount < MaxReports) g_reportCount++;
|
||||||
|
g_lock.Release();
|
||||||
|
}
|
||||||
|
|
||||||
|
int TakeOldest(Report& out) {
|
||||||
|
g_lock.Acquire();
|
||||||
|
int had = g_reportCount;
|
||||||
|
if (had == 0) {
|
||||||
|
g_lock.Release();
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
int oldestIdx = (g_reportHead - g_reportCount + MaxReports) % MaxReports;
|
||||||
|
out = g_reports[oldestIdx];
|
||||||
|
g_reports[oldestIdx].valid = false;
|
||||||
|
g_reportCount--;
|
||||||
|
g_lock.Release();
|
||||||
|
return had;
|
||||||
}
|
}
|
||||||
|
|
||||||
} // namespace CrashReport
|
} // namespace CrashReport
|
||||||
|
|||||||
@@ -39,12 +39,20 @@ struct Report {
|
|||||||
bool valid;
|
bool valid;
|
||||||
};
|
};
|
||||||
|
|
||||||
// Kernel-side ring buffer (written by exception handlers, read by SYS_CRASH_REPORT)
|
// Kernel-side ring buffer (written by exception handlers, read by SYS_CRASH_REPORT).
|
||||||
|
// All access is serialized by an internal interrupt-disabling spinlock: the
|
||||||
|
// writer (AddReport) runs in exception/interrupt context and the reader
|
||||||
|
// (TakeOldest) runs in syscall context, possibly on different CPUs.
|
||||||
extern Report g_reports[MaxReports];
|
extern Report g_reports[MaxReports];
|
||||||
extern int g_reportHead;
|
extern int g_reportHead;
|
||||||
extern int g_reportCount;
|
extern int g_reportCount;
|
||||||
|
|
||||||
// Add a report to the kernel ring buffer
|
// Add a report to the kernel ring buffer (called from exception handlers).
|
||||||
void AddReport(const Report& report);
|
void AddReport(const Report& report);
|
||||||
|
|
||||||
|
// Pop the oldest report into `out`. Returns the number of reports that were
|
||||||
|
// present before this call (0 if the buffer was empty, in which case `out` is
|
||||||
|
// left untouched). Safe against concurrent AddReport / TakeOldest.
|
||||||
|
int TakeOldest(Report& out);
|
||||||
|
|
||||||
} // namespace CrashReport
|
} // namespace CrashReport
|
||||||
|
|||||||
@@ -10,6 +10,7 @@
|
|||||||
#include <Memory/Paging.hpp>
|
#include <Memory/Paging.hpp>
|
||||||
#include <Memory/HHDM.hpp>
|
#include <Memory/HHDM.hpp>
|
||||||
#include <Libraries/Memory.hpp>
|
#include <Libraries/Memory.hpp>
|
||||||
|
#include <Libraries/String.hpp>
|
||||||
#include <Terminal/Terminal.hpp>
|
#include <Terminal/Terminal.hpp>
|
||||||
#include <CppLib/Stream.hpp>
|
#include <CppLib/Stream.hpp>
|
||||||
#include <CppLib/Spinlock.hpp>
|
#include <CppLib/Spinlock.hpp>
|
||||||
@@ -1399,6 +1400,17 @@ namespace Sched {
|
|||||||
// handle to that same out-stream so it can surface a textual crash
|
// handle to that same out-stream so it can surface a textual crash
|
||||||
// report to the console user when no desktop is running.
|
// report to the console user when no desktop is running.
|
||||||
Process* crasher = GetProcessByPid(crasherPid);
|
Process* crasher = GetProcessByPid(crasherPid);
|
||||||
|
|
||||||
|
// Crash-loop guard: never spawn a crashpad to report on the crashpad
|
||||||
|
// itself. If crashpad.elf faults (startup bug, OOM, etc.) we would
|
||||||
|
// otherwise spawn another crashpad, which can fault again, ad infinitum
|
||||||
|
// -- a fork bomb that also exhausts process slots and report buffers.
|
||||||
|
if (crasher && Lib::strcmp(crasher->name, crashpadPath) == 0) {
|
||||||
|
Kt::KernelLogStream(Kt::ERROR, "Sched")
|
||||||
|
<< "crashpad faulted; not spawning a nested crashpad";
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
int crasherSlot = Ipc::SlotForPid(crasherPid);
|
int crasherSlot = Ipc::SlotForPid(crasherPid);
|
||||||
bool inheritRedir = crasher && crasher->redirected &&
|
bool inheritRedir = crasher && crasher->redirected &&
|
||||||
crasher->ioOutHandle >= 0 && crasherSlot >= 0;
|
crasher->ioOutHandle >= 0 && crasherSlot >= 0;
|
||||||
|
|||||||
Reference in New Issue
Block a user