Reputation: 2522
I am currently hacking around a solution to make errors easier to debug.
so i have a allready defined main
and the user needs to define wrappermain
that will replace the use of main
:
int main(int ac, char** av)
{
__try {
return wrappermain(ac, av);
} __except(HandleException(GetExceptionCode(), GetExceptionInformation()) {
return 1;
}
}
now in the HandleException
function i load the symbol information and set some options:
if (!SymInitialize(process, symSearchPath, false))
return;
DWORD symOptions = SymGetOptions();
symOptions |= SYMOPT_LOAD_LINES | SYMOPT_UNDNAME;
SymSetOptions(symOptions);
this construct works like a charm but when i provoke a stack overflow with an endless recursion, i get a access violation on calling SymSetOptions(symOptions)
what could that be and how to get rid of that crash?
Upvotes: 0
Views: 198
Reputation: 33744
EXCEPTION_POINTERS ep;
__try {
return wrappermain(ac, av);
}
__except(memcpy(&ep,GetExceptionInformation(), sizeof(EXCEPTION_POINTERS)), HandleException(GetExceptionCode(),GetExceptionInformation())){
if (GetExceptionCode() == STATUS_STACK_OVERFLOW)
{
#ifdef _WIN64
#define GUARD_PAGE_COUNT 3
#define COMMIT_PAGE_COUNT 6
#else
#define GUARD_PAGE_COUNT 1
#define COMMIT_PAGE_COUNT 2
#endif
//in ntdll exist special api for this - RtlResetStackOverflow, but it not exported
BOOL fOk = FALSE;
::MEMORY_BASIC_INFORMATION mbi;
if (VirtualQuery(&mbi, &mbi, sizeof(mbi)))
{
if ((GUARD_PAGE_COUNT + COMMIT_PAGE_COUNT) << PAGE_SHIFT < RtlPointerToOffset(mbi.AllocationBase, mbi.BaseAddress))
{
mbi.BaseAddress = (PBYTE)mbi.BaseAddress - ((GUARD_PAGE_COUNT + COMMIT_PAGE_COUNT) << PAGE_SHIFT);
if (mbi.AllocationBase < mbi.BaseAddress)
{
ULONG OldProtect;
fOk = VirtualFree(mbi.AllocationBase, RtlPointerToOffset(mbi.AllocationBase, mbi.BaseAddress), MEM_DECOMMIT)
&&
VirtualProtect(mbi.BaseAddress, GUARD_PAGE_COUNT << PAGE_SHIFT, PAGE_READWRITE|PAGE_GUARD, &OldProtect);
}
}
}
}
}
Upvotes: 0
Reputation: 33744
"what could that be" - this and must be. because not enough stack space. in HandleException you run at same stack point where exception occur. you have only 1 page (4096 bytes) of stack space. so call of function which require big stack space again raise exception. and this new already will be fatal - because already no stack space at all, even 1 page not exist. so if you view STATUS_STACK_OVERFLOW - you must just return EXCEPTION_EXECUTE_HANDLER and already in __except(){..} block (in this point stack pointer already will be revert) again check for if (GetExceptionCode()==STATUS_STACK_OVERFLOW) - de allocate some stack space and set PAGE_GUARD again in bottom allocated page. if not do this - next stack overflow for your app already will be fatal(you not have 1 page in stack)
Upvotes: 1
Reputation: 179907
The core problem here is the stack, obviously. You have a stack overflow (corrupt/unusable), and you want to handle it with a stack-based exception handler. That's not going to work well. I've even surprised you get this far; your exception handler has to be found by walking the call stack back to find a handler. And walking tat stack is going to be a problem if there's been an overflow and part of that stack is missing in action.
Anyway, I suspect the direct cause is the lack of a guard page on the stack. The stack isn't fully allocated up front. Instead, there's a guard page at the end of the allocation. Pushing too much causes a page fault, which usually leads to a successful stack growth. But when there's no space left, the overflow is fatal and the guard page is gone.
The fix may very well be to have another thread. It would poll a global flag (DWORD
so atomic) and sleep if nothing happens. Your exception handler would just need to set the flag, and that doesn't require a working stack. This unlocks the helper thread, who then (using its fresh stack) calls the relevant methods.
Vectored Exception Handling may look like a workaround, but it has severe restrictions on what you can do in a handler.
Upvotes: 1