堆栈溢出后调用SymSetOptions时发生AccessViolation



我目前正在破解一个解决方案,以使错误更容易调试。

所以我有一个allready定义的main,用户需要定义wrappermain,它将取代main:的使用

int main(int ac, char** av)
{
__try {
return wrappermain(ac, av);
} __except(HandleException(GetExceptionCode(), GetExceptionInformation()) {
return 1;
}
}

现在在HandleException函数中,我加载符号信息并设置一些选项:

if (!SymInitialize(process, symSearchPath, false))
return;
DWORD symOptions = SymGetOptions();
symOptions |= SYMOPT_LOAD_LINES | SYMOPT_UNDNAME;
SymSetOptions(symOptions);

这个构造很有魅力,但当我用无休止的递归引发堆栈溢出时,我在调用SymSetOptions(symOptions)时会遇到访问冲突

那可能是什么?如何摆脱那场车祸?

这里的核心问题显然是堆栈。您有一个堆栈溢出(损坏/不可用),并且您希望使用基于堆栈的异常处理程序来处理它。这不会奏效。我甚至让你走到这一步感到惊讶;异常处理程序必须通过遍历调用堆栈来找到处理程序。如果出现溢出,并且堆栈的一部分在运行中丢失,那么遍历tat堆栈将是一个问题。

无论如何,我怀疑直接原因是堆栈上缺少保护页。堆栈未提前完全分配。相反,在分配的末尾有一个保护页。推送过多会导致页面错误,这通常会导致堆栈成功增长。但当没有空间时,溢出是致命的,保护页也就消失了。

修复方法很可能是使用另一个线程。它会轮询一个全局标志(DWORD是原子性的),如果什么都没发生,它就会休眠。您的异常处理程序只需要设置标志,而不需要工作堆栈。这将解锁助手线程,然后助手线程(使用其新堆栈)调用相关方法。

矢量异常处理看起来可能是一种变通方法,但它对处理程序中的操作有严格的限制。

"那可能是什么"-这必须是。因为没有足够的堆栈空间。在HandleException中,您在发生异常的同一堆栈点运行。您只有1页(4096字节)的堆栈空间。因此调用需要较大堆栈空间的函数再次引发异常。这个新的已经是致命的了——因为已经没有堆栈空间了,甚至一个页面都不存在。因此,如果您查看STATUS_STACK_OVERFLOW-您必须只返回EXCEPTION_EXECUTE_HANDLER并且已经在__except(){..}块中(此时堆栈指针已经恢复),请再次检查是否(GetExceptionCode()==STATUS_STACK _OVERFLOW)-取消分配一些堆栈空间,并在底部分配的页面中再次设置PAGE_UARD。如果不这样做-你的应用程序的下一个堆栈溢出将是致命的(你在堆栈中没有1页)

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);
}
}
}
}
}

最新更新