堆栈溢出后调用SymSetOptions时的AccessViolation

时间:2016-06-01 11:48:13

标签: c++ winapi

我目前正在破解解决方案,使错误更易于调试。

所以我已经定义了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)时遇到访问冲突

那可能是什么以及如何摆脱那次崩溃?

3 个答案:

答案 0 :(得分:1)

这里的核心问题显然是堆栈。您有堆栈溢出(损坏/不可用),并且您希望使用基于堆栈的异常处理程序来处理它。这不会很好。我甚至惊讶你这么远;必须通过遍历调用堆栈找到处理程序来找到您的异常处理程序。如果存在溢出并且该堆栈的一部分在操作中缺失,那么行走堆栈将成为一个问题。

无论如何,我怀疑直接原因是堆栈上没有防护页面。堆栈未预先完全分配。相反,在分配结束时有一个保护页面。推送过多会导致页面错误,这通常会导致堆栈成功增长。但是当没有剩余空间时,溢出是致命的,并且防护页面消失了。

修复很可能是另一个线程。它将轮询一个全局标志(DWORD如此原子)并在没有任何反应时休眠。您的异常处理程序只需要设置标志,并且不需要工作堆栈。这解锁了帮助程序线程,然后(使用其新堆栈)调用相关方法。

向量异常处理可能看起来像一种变通方法,但它对您在处理程序中可以执行的操作有严格的限制。

答案 1 :(得分:1)

“那可能是什么” - 这一定是必须的。因为没有足够的堆栈空间。在HandleException中,您在发生异常的相同堆栈点运行。你只有1页(4096字节)的堆栈空间。因此需要大堆栈空间的函数调用再次引发异常。而这个新的已经是致命的 - 因为根本没有堆栈空间,甚至1页也不存在。所以,如果你查看STATUS_STACK_OVERFLOW - 你必须只返回EXCEPTION_EXECUTE_HANDLER并且已经在__except(){..}块中(此时堆栈指针已经将恢复)再次检查if(GetExceptionCode()== STATUS_STACK_OVERFLOW) - de allocate some stack空格并在底部分配的页面中再次设置PAGE_GUARD。如果不这样做 - 你的应用程序的下一个堆栈溢出已经是致命的(你的堆栈中没有1页)

答案 2 :(得分:0)

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

                        }
                    }
                }
            }