[CRITICAL] VEH Fails to Catch Access Violation / Null Pointer Dereference on MinGW-w64 in Mate++
Issue ID: #BUG-001
Priority: CRITICAL
Status: Open
Project: Mate++ Wallpaper Engine v2.0
Platform: Windows 10/11 (x64)
Compiler: MinGW-w64 (GCC 12.0+)
Reported: 2026-08-09
1. Executive Summary
The current exception handling system in Mate++ Wallpaper Engine using Vectored Exception Handler (VEH) does not work correctly on MinGW-w64. When the application encounters a hardware exception (access violation, null pointer dereference, invalid pointer access), the VEH handler either fails to catch it or cannot process it properly, resulting in an unhandled crash without proper cleanup, logging, or graceful exit.
Impact: Application crashes without recovery, no crash report generated, resources not cleaned up, poor user experience.
2. Problem Description
2.1 Current Broken Implementation
Mate++ currently uses a combination of signal() handlers and AddVectoredExceptionHandler() for crash handling:
// CURRENT BROKEN CODE - main.cpp
static void SignalHandler(int signal) {
// This NEVER fires for access violation on Windows
const char* sigName = "UNKNOWN";
switch(signal) {
case SIGABRT: sigName = "SIGABRT (Abort/assert)"; break;
case SIGTERM: sigName = "SIGTERM (Termination)"; break;
case SIGINT: sigName = "SIGINT (Interrupt)"; break;
}
LogToFile("[SIGNAL] Caught signal: %s (%d)", sigName, signal);
InterlockedIncrement(&g_exceptionCount);
WriteCrashReport(sigName);
if(g_hasJumpBuffer) {
longjmp(g_jumpBuffer, 1); // UNDEFINED BEHAVIOR
} else {
exit(1);
}
}
static LONG WINAPI VectoredCrashHandler(PEXCEPTION_POINTERS ExceptionInfo) {
DWORD code = ExceptionInfo->ExceptionRecord->ExceptionCode;
// ... handling ...
// Attempts to use longjmp() - UNSAFE
if (g_hasJumpBuffer) {
longjmp(g_jumpBuffer, 1); // UNDEFINED BEHAVIOR
}
return EXCEPTION_CONTINUE_SEARCH;
}
static void InitExceptionHandlers() {
signal(SIGABRT, SignalHandler);
signal(SIGTERM, SignalHandler);
signal(SIGINT, SignalHandler);
signal(SIGSEGV, SignalHandler); // DOES NOT WORK
AddVectoredExceptionHandler(1, VectoredCrashHandler);
}
2.2 Why This Fails
Issue 1: signal() Does Not Work for Hardware Exceptions on Windows
signal(SIGSEGV, ...) DOES NOT catch access violations on Windows
- This is a documented limitation of the Microsoft C Runtime (CRT)
- Only works for
SIGABRT (from abort() or assert()) and console events (SIGTERM, SIGINT)
Reference: https://docs.microsoft.com/en-us/cpp/c-runtime-library/reference/signal
Issue 2: longjmp() from VEH is Undefined Behavior
- VEH callbacks run in kernel-mode callback context
longjmp() is not designed to unwind from kernel context
- Causes stack corruption, memory leaks, or silent crashes
- Cannot guarantee proper resource cleanup
Issue 3: No Graceful Shutdown
- When crash occurs, resources are not cleaned up:
- D2D render targets not released
- File handles not closed
- Threads not terminated properly
- Crash log incomplete or not written
Issue 4: MinGW-w64 Compiler Limitations
- MinGW-w64 does NOT support
__try/__except (MSVC extension)
- Only uses SEH internally for C++ exceptions with
-fseh flag
- Cannot use MSVC-style exception handling syntax
3. Technical Analysis
3.1 Exception Types in Windows
| Exception Type |
Description |
How to Catch |
| Hardware Exceptions |
Access violation, div-by-zero, stack overflow |
VEH only |
| C++ Exceptions |
throw std::exception, throw "error" |
try/catch |
| CRT Signals |
SIGABRT, SIGTERM, SIGINT |
signal() |
| SEH Exceptions |
__try/__except (MSVC only) |
Not available in MinGW |
3.2 VEH vs Other Methods
| Method |
MSVC |
MinGW |
Catch Access Violation |
Safe |
signal(SIGSEGV) |
Yes |
No |
No |
N/A |
__try/__except |
Yes |
No |
Yes |
Yes |
AddVectoredExceptionHandler |
Yes |
Yes |
Yes |
No (with longjmp) |
SetUnhandledExceptionFilter |
Yes |
Yes |
No |
Yes |
Conclusion: AddVectoredExceptionHandler is the ONLY viable option for MinGW-w64 to catch hardware exceptions, but must be used WITHOUT longjmp().
4. Proposed Solution
4.1 Architecture Overview
Hardware Exception (Access Violation)
|
v
VEH Handler
|
v
Log + Write Crash Report
|
v
Set g_exceptionFlag = 1
|
v
Wake Up All Threads
|
v
Terminate Threads (Forced)
|
v
ExitProcess(1)
4.2 Key Changes Required
- Remove all
signal() calls - Useless on Windows for hardware exceptions
- Remove
jmp_buf and longjmp() - Unsafe from VEH
- Add
g_exceptionFlag - Signal for threads to exit
- Add
std::set_terminate() - Catch unhandled C++ exceptions
- Add
try/catch in WinMain and all threads - Handle C++ exceptions properly
- Add exception flag checks in all thread loops - Clean exit on crash
- Use
ExitProcess(1) in VEH - Force exit after signaling
4.3 Fixed Code
// ============================================================
// EXCEPTION HANDLING - FIXED IMPLEMENTATION
// ============================================================
static volatile LONG g_exceptionFlag = 0;
// Write crash report with exception details
static void WriteCrashReport(const char* reason, PEXCEPTION_POINTERS info = nullptr) {
FILE* f = fopen("crash_report.txt", "w");
if (f) {
SYSTEMTIME st;
GetLocalTime(&st);
fprintf(f, "=== Mate++ Wallpaper Engine Crash Report ===\n");
fprintf(f, "Date: %04d-%02d-%02d %02d:%02d:%02d\n",
st.wYear, st.wMonth, st.wDay, st.wHour, st.wMinute, st.wSecond);
fprintf(f, "Reason: %s\n", reason);
if (info) {
fprintf(f, "Exception Code: 0x%08lX\n",
info->ExceptionRecord->ExceptionCode);
fprintf(f, "Exception Address: 0x%p\n",
info->ExceptionRecord->ExceptionAddress);
if (info->ExceptionRecord->ExceptionCode == EXCEPTION_ACCESS_VIOLATION) {
fprintf(f, "Access Violation: %s at address 0x%p\n",
info->ExceptionRecord->ExceptionInformation[0] == 0 ? "Read" : "Write",
(void*)info->ExceptionRecord->ExceptionInformation[1]);
}
}
// Stack trace
void* stack[64];
USHORT frames = CaptureStackBackTrace(0, 64, stack, NULL);
fprintf(f, "Call Stack:\n");
for (USHORT i = 0; i < frames; i++) {
fprintf(f, " #%d: 0x%p\n", i, stack[i]);
}
fclose(f);
}
}
// VEH Handler - ONLY for hardware exceptions
static LONG WINAPI VectoredCrashHandler(PEXCEPTION_POINTERS ExceptionInfo) {
DWORD code = ExceptionInfo->ExceptionRecord->ExceptionCode;
// Only handle REAL hardware exceptions
// Skip C++ EH codes (0xE06D7363), debugger breaks, etc.
switch (code) {
case EXCEPTION_ACCESS_VIOLATION:
case EXCEPTION_ILLEGAL_INSTRUCTION:
case EXCEPTION_INT_DIVIDE_BY_ZERO:
case EXCEPTION_FLT_DIVIDE_BY_ZERO:
case EXCEPTION_ARRAY_BOUNDS_EXCEEDED:
case EXCEPTION_PRIV_INSTRUCTION:
case EXCEPTION_IN_PAGE_ERROR:
break;
case EXCEPTION_STACK_OVERFLOW:
_resetstkoflw(); // Restore guard page
break;
default:
// Not a hardware exception - let Windows handle it
return EXCEPTION_CONTINUE_SEARCH;
}
// Log exception
LogToFile("[VEH] Caught: 0x%08lX at 0x%p", code,
ExceptionInfo->ExceptionRecord->ExceptionAddress);
InterlockedIncrement(&g_exceptionCount);
// Write crash report
WriteCrashReport("Hardware Exception", ExceptionInfo);
// Signal all threads to exit
g_running = false;
InterlockedExchange(&g_exceptionFlag, 1);
// Wake up waiting threads
if (g_hKickEvent) SetEvent(g_hKickEvent);
if (g_hFrameConsumed) SetEvent(g_hFrameConsumed);
// Terminate threads - we're about to exit
if (g_hDecodeThread) {
TerminateThread(g_hDecodeThread, 1);
CloseHandle(g_hDecodeThread);
g_hDecodeThread = NULL;
}
if (g_hRenderThread) {
TerminateThread(g_hRenderThread, 1);
CloseHandle(g_hRenderThread);
g_hRenderThread = NULL;
}
// Allow time for cleanup
Sleep(500);
// Force exit - cannot recover safely
ExitProcess(1);
return EXCEPTION_EXECUTE_HANDLER;
}
// C++ Terminate Handler - for unhandled C++ exceptions
static void TerminateHandler() {
LogToFile("[TERMINATE] std::terminate called - unhandled C++ exception");
WriteCrashReport("std::terminate (unhandled C++ exception)");
ExitProcess(1);
}
// Initialize exception handlers
static void InitExceptionHandlers() {
// Register VEH - catches hardware exceptions
PVOID handle = AddVectoredExceptionHandler(1, VectoredCrashHandler);
if (handle) {
LogToFile("[EXCEPTION] VEH registered successfully");
} else {
LogToFile("[EXCEPTION] VEH registration failed: %d", GetLastError());
}
// Register C++ terminate handler
std::set_terminate(TerminateHandler);
// DO NOT use signal() - does not work on Windows
LogToFile("[EXCEPTION] Exception handlers initialized");
}
4.4 Thread Loop Updates
// Decode Thread (video_decoder.cpp)
static DWORD WINAPI DecodeThreadProc(LPVOID pArg) {
try {
// ... initialization ...
while (!isStale() && !g_exceptionFlag) {
// ... decode loop ...
}
// ... cleanup ...
} catch (const std::exception& e) {
LogToFile("[Decode] C++ exception: %s", e.what());
} catch (...) {
LogToFile("[Decode] Unknown C++ exception");
}
return 0;
}
// Render Thread (main.cpp)
static DWORD WINAPI RenderThread(LPVOID) {
try {
// ... initialization ...
while(g_running && !g_exceptionFlag) {
// ... render loop ...
}
// ... cleanup ...
} catch (const std::exception& e) {
LogToFile("[Render] C++ exception: %s", e.what());
} catch (...) {
LogToFile("[Render] Unknown C++ exception");
}
return 0;
}
// Main Message Loop (main.cpp - WinMain)
MSG msg;
while(g_running && GetMessage(&msg, NULL, 0, 0)) {
TranslateMessage(&msg);
DispatchMessage(&msg);
// Check exception flag
if (g_exceptionFlag) {
LogToFile("[Main] Exception flag detected, exiting");
break;
}
}
5. Files to Modify
5.1 main.cpp
| Section |
Change |
| Globals |
Add volatile LONG g_exceptionFlag = 0; |
| Globals |
Move g_running and g_hKickEvent before VEH |
| Exception Handlers |
Replace entire section with new implementation |
| Signal Handlers |
REMOVE entirely |
| Render Thread |
Add !g_exceptionFlag to while loop |
| Render Thread |
Wrap with try/catch |
| WinMain |
Add try/catch around entire function |
| WinMain |
Add exception flag check in message loop |
5.2 common.h
| Section |
Change |
| Globals |
Add extern volatile LONG g_exceptionFlag; |
5.3 video_decoder.cpp
| Section |
Change |
| Includes |
Add extern volatile LONG g_exceptionFlag; |
| Decode Thread |
Add !g_exceptionFlag to while loop |
| Decode Thread |
Wrap with try/catch |
6. Testing
6.1 Test Cases
| Test Case |
Description |
Expected Result |
| TC-01 |
Null pointer dereference |
VEH catches, logs crash, exits |
| TC-02 |
Divide by zero |
VEH catches, logs crash, exits |
| TC-03 |
C++ exception with catch |
Handled by try/catch |
| TC-04 |
C++ exception without catch |
Terminate handler catches |
| TC-05 |
Stack overflow |
VEH catches, restores guard page, exits |
| TC-06 |
Corrupt video decode |
Handled gracefully |
6.2 Test Code
// Test null pointer dereference
void TestAccessViolation() {
int* p = nullptr;
*p = 123; // Should trigger VEH
}
// Test divide by zero
void TestDivByZero() {
int a = 5;
int b = 0;
int c = a / b; // Should trigger VEH
}
// Test C++ exception
void TestCppException() {
throw std::runtime_error("Test exception");
}
7. Environment
7.1 Development Environment
- OS: Windows 10/11 (x64)
- Compiler: MinGW-w64 (x86_64-w64-mingw32-g++)
- GCC Version: 12.0+
- Build System: Code::Blocks / Makefile
- Debugger: GDB
7.2 Build Flags
CFLAGS = -fexceptions -mwindows -O2 -g
LDFLAGS = -lwinhttp -lpsapi -lavformat -lavcodec -lavutil -lswscale
7.3 Dependencies
- FFmpeg libraries (avformat, avcodec, avutil, swscale)
- Windows SDK (user32, gdi32, d2d1, dwrite, dwmapi)
- WinHTTP (for network features)
8. Additional Notes
8.1 Important Observations
-
signal(SIGSEGV, ...) DOES NOT work on Windows - This is documented by Microsoft and cannot be fixed.
-
__try/__except is MSVC-only - Not available in MinGW-w64. Cannot use this approach.
-
VEH is the ONLY reliable method - For catching hardware exceptions on Windows with MinGW-w64.
-
DO NOT use longjmp() from VEH - Causes undefined behavior. Use ExitProcess() instead.
-
C++ exceptions are separate - Hardware exceptions (access violation) are different from C++ exceptions (throw). Handle them separately.
8.2 References
9. Risk Assessment
| Risk |
Probability |
Impact |
Mitigation |
| VEH not registered |
Low |
High |
Check return value, log error |
| Thread termination hangs |
Low |
Medium |
Use timeout on WaitForSingleObject |
| Crash report not written |
Medium |
Medium |
Verify file write, use fallback logging |
| ExitProcess too aggressive |
Low |
Medium |
Allow time for cleanup (500ms) |
10. Timeline
| Phase |
Duration |
Description |
| Implementation |
2 hours |
Apply code changes |
| Testing |
2 hours |
Run test cases |
| Integration |
1 hour |
Merge with main branch |
| QA |
4 hours |
Full regression testing |
| Total |
9 hours |
- |
11. Checklist
12. Labels
bug, critical, exception-handling, mingw64, windows, mate++, crash, stability, seh, veh
Reported by: @PmgTeam
Assigned to: @pmgdev64
Priority: CRITICAL
Severity: Critical - System Crash
[CRITICAL] VEH Fails to Catch Access Violation / Null Pointer Dereference on MinGW-w64 in Mate++
Issue ID: #BUG-001
Priority: CRITICAL
Status: Open
Project: Mate++ Wallpaper Engine v2.0
Platform: Windows 10/11 (x64)
Compiler: MinGW-w64 (GCC 12.0+)
Reported: 2026-08-09
1. Executive Summary
The current exception handling system in Mate++ Wallpaper Engine using Vectored Exception Handler (VEH) does not work correctly on MinGW-w64. When the application encounters a hardware exception (access violation, null pointer dereference, invalid pointer access), the VEH handler either fails to catch it or cannot process it properly, resulting in an unhandled crash without proper cleanup, logging, or graceful exit.
Impact: Application crashes without recovery, no crash report generated, resources not cleaned up, poor user experience.
2. Problem Description
2.1 Current Broken Implementation
Mate++ currently uses a combination of
signal()handlers andAddVectoredExceptionHandler()for crash handling:2.2 Why This Fails
Issue 1:
signal()Does Not Work for Hardware Exceptions on Windowssignal(SIGSEGV, ...)DOES NOT catch access violations on WindowsSIGABRT(fromabort()orassert()) and console events (SIGTERM,SIGINT)Reference: https://docs.microsoft.com/en-us/cpp/c-runtime-library/reference/signal
Issue 2:
longjmp()from VEH is Undefined Behaviorlongjmp()is not designed to unwind from kernel contextIssue 3: No Graceful Shutdown
Issue 4: MinGW-w64 Compiler Limitations
__try/__except(MSVC extension)-fsehflag3. Technical Analysis
3.1 Exception Types in Windows
throw std::exception,throw "error"try/catchSIGABRT,SIGTERM,SIGINTsignal()__try/__except(MSVC only)3.2 VEH vs Other Methods
signal(SIGSEGV)__try/__exceptAddVectoredExceptionHandlerSetUnhandledExceptionFilterConclusion:
AddVectoredExceptionHandleris the ONLY viable option for MinGW-w64 to catch hardware exceptions, but must be used WITHOUTlongjmp().4. Proposed Solution
4.1 Architecture Overview
4.2 Key Changes Required
signal()calls - Useless on Windows for hardware exceptionsjmp_bufandlongjmp()- Unsafe from VEHg_exceptionFlag- Signal for threads to exitstd::set_terminate()- Catch unhandled C++ exceptionstry/catchin WinMain and all threads - Handle C++ exceptions properlyExitProcess(1)in VEH - Force exit after signaling4.3 Fixed Code
4.4 Thread Loop Updates
5. Files to Modify
5.1 main.cpp
volatile LONG g_exceptionFlag = 0;g_runningandg_hKickEventbefore VEH!g_exceptionFlagto while looptry/catchtry/catcharound entire function5.2 common.h
extern volatile LONG g_exceptionFlag;5.3 video_decoder.cpp
extern volatile LONG g_exceptionFlag;!g_exceptionFlagto while looptry/catch6. Testing
6.1 Test Cases
6.2 Test Code
7. Environment
7.1 Development Environment
7.2 Build Flags
7.3 Dependencies
8. Additional Notes
8.1 Important Observations
signal(SIGSEGV, ...)DOES NOT work on Windows - This is documented by Microsoft and cannot be fixed.__try/__exceptis MSVC-only - Not available in MinGW-w64. Cannot use this approach.VEH is the ONLY reliable method - For catching hardware exceptions on Windows with MinGW-w64.
DO NOT use
longjmp()from VEH - Causes undefined behavior. UseExitProcess()instead.C++ exceptions are separate - Hardware exceptions (access violation) are different from C++ exceptions (
throw). Handle them separately.8.2 References
9. Risk Assessment
10. Timeline
11. Checklist
signal()callsjmp_bufandlongjmp()g_exceptionFlagstd::set_terminate()handlertry/catchin WinMaintry/catchin RenderThreadtry/catchin DecodeThread12. Labels
Reported by: @PmgTeam
Assigned to: @pmgdev64
Priority: CRITICAL
Severity: Critical - System Crash