mirror of
https://github.com/simplysanatori/LegendFrame.git
synced 2026-10-29 04:02:42 +01:00
69 lines
2.7 KiB
C++
69 lines
2.7 KiB
C++
// Runtime check of shims/seh_compat.h (no game involved). Built with -include seh_compat.h.
|
|
// usage: seh_test.exe <logfile> (stdout is swallowed by Proton, so results go to the file too)
|
|
#include <windows.h>
|
|
#include <cstdio>
|
|
|
|
static FILE* g_log;
|
|
static void say(const char* fmt, int v = 0)
|
|
{
|
|
char b[256]; snprintf(b, sizeof b, fmt, v);
|
|
if (g_log) { fputs(b, g_log); fputc('\n', g_log); fflush(g_log); }
|
|
puts(b); fflush(stdout);
|
|
}
|
|
|
|
static volatile int* volatile g_null = nullptr;
|
|
static int __attribute__((noinline)) faulting_callee(int x) { return *g_null + x; }
|
|
typedef int (*Fn)(int);
|
|
static volatile Fn g_fn = faulting_callee;
|
|
static int __attribute__((noinline)) good_callee(int x) { return x * 2; }
|
|
|
|
static bool peek(volatile int* p, int* out)
|
|
{
|
|
__try { *out = *p; return true; }
|
|
__except (EXCEPTION_EXECUTE_HANDLER) { return false; }
|
|
}
|
|
|
|
int main(int argc, char** argv)
|
|
{
|
|
if (argc > 1) g_log = fopen(argv[1], "w");
|
|
int failures = 0;
|
|
say("start");
|
|
|
|
// 1. null read inside __try
|
|
int got = 0;
|
|
if (!peek(g_null, &got)) say("recovered (null read)"); else { say("FAIL null read not caught"); ++failures; }
|
|
|
|
// 2. guarded call through a function pointer into faulting code
|
|
volatile int r2 = 0;
|
|
__try { r2 = g_fn(1); say("FAIL call did not fault"); ++failures; }
|
|
__except (EXCEPTION_EXECUTE_HANDLER) { r2 = -1; }
|
|
if (r2 == -1) say("recovered (faulting call through function pointer)");
|
|
|
|
// 3. no fault: body runs, `return` leaves the frame cleanly
|
|
int ok = 0;
|
|
if (peek(&got, &ok) && ok == got) say("ok (no-fault path returns from inside __try)"); else { say("FAIL no-fault path"); ++failures; }
|
|
|
|
// 4. nested: inner fault is caught by the inner handler, outer body continues
|
|
int stage = 0;
|
|
__try {
|
|
__try { (void)*g_null; stage = 1; }
|
|
__except (EXCEPTION_EXECUTE_HANDLER) { stage = 2; }
|
|
stage += 10;
|
|
}
|
|
__except (EXCEPTION_EXECUTE_HANDLER) { stage = -99; }
|
|
if (stage == 12) say("recovered (nested)"); else { say("FAIL nested stage=%d", stage); ++failures; }
|
|
|
|
// 5. many faults in a row (frame chain must not leak or corrupt)
|
|
int n = 0;
|
|
for (int i = 0; i < 2000; ++i) { int t; if (!peek(g_null, &t)) ++n; }
|
|
if (n == 2000) say("recovered (2000 consecutive faults)"); else { say("FAIL repeated faults n=%d", n); ++failures; }
|
|
|
|
// 6. FS:[0] chain intact afterwards: a function pointer call that does NOT fault still works
|
|
if (g_fn != nullptr && good_callee(21) == 42) say("ok (chain intact)"); else ++failures;
|
|
|
|
// 7. unguarded fault still reaches the unhandled path? skipped on purpose (would kill the process)
|
|
say(failures ? "RESULT FAIL" : "RESULT PASS");
|
|
if (g_log) fclose(g_log);
|
|
return failures ? 1 : 0;
|
|
}
|