调试堆栈损坏问题

Ami*_*nen 5 c++ callstack corruption systemc

我正在使用C++(Visual Studio 2015)调试大型应用程序上的"访问冲突"异常.该应用程序是由几个库构建的,其中一个(SystemC)会出现问题,尽管我怀疑问题的根源在其他地方.

我看到的是一个函数调用,它破坏了调用者的成员函数的地址.

m_update_phase = true;
m_prim_channel_registry->perform_update();
m_update_phase = false;
Run Code Online (Sandbox Code Playgroud)
inline
void
sc_prim_channel_registry::perform_update()
{
    for( int i = m_update_last; i >= 0; -- i ) {
    m_update_array[i]->perform_update();
    }
    m_update_last = -1;
}
Run Code Online (Sandbox Code Playgroud)

(这些是从摘录systemc\kernel\sc_simcontext.cppsystemc\communication\sc_prim_channel.h,所述的部分SystemC的库)

通过上面的代码多次迭代后发生错误.调用m_prim_channel_registry->perform_update()抛出0xC0000005: Access violation writing location 0x0F4CD9E9.异常.
只有在Release配置中构建应用程序时才会发生这种情况

看看汇编代码,我看到函数sc_prim_channel_registry::perform_update()是内联的,内部函数调用m_update_array[i]->perform_update()似乎破坏了调用函数的堆栈框架.
m_update_last = -1;被执行时,与m_update_last不再指向有效的存储位置,并抛出异常.
(m_update_last是类的一个简单的天然成员sc_prim_channel_registry具有类型int)

    m_update_phase = true;
    m_prim_channel_registry->perform_update();
1034D99E  mov         eax,dword ptr [esi+10h]  
1034D9A1  mov         byte ptr [esi+0A3h],1  
1034D9A8  mov         dword ptr [ebp-18h],eax  
1034D9AB  mov         ebx,dword ptr [eax+28h]  
1034D9AE  test        ebx,ebx  
1034D9B0  js          $LN163+0FEh (1034D9D0h)  
1034D9B2  mov         esi,eax  
1034D9B4  mov         eax,dword ptr [esi+20h]  
1034D9B7  mov         edi,dword ptr [eax+ebx*4]  
1034D9BA  mov         ecx,edi  
1034D9BC  mov         eax,dword ptr [edi]  
1034D9BE  call        dword ptr [eax+14h]  
1034D9C1  sub         ebx,1  
1034D9C4  mov         byte ptr [edi+1Ch],0  
1034D9C8  jns         $LN163+0E2h (1034D9B4h)  
1034D9CA  mov         esi,dword ptr [this]  
1034D9CD  mov         eax,dword ptr [ebp-18h]  
1034D9D0  mov         dword ptr [eax+28h],0FFFFFFFFh  
    m_update_phase = false;
Run Code Online (Sandbox Code Playgroud)

地址抛出异常1034D9D0 所以最后执行的指令是

0F97D9CD  mov         eax,dword ptr [ebp-18h]  
0F97D9D0  mov         dword ptr [eax+28h],0FFFFFFFFh  
Run Code Online (Sandbox Code Playgroud)

m_prim_channel_registry地址在[ebp-18h]和eax中,[eax + 28h]是m_update_last.

在内线呼叫之前在esp和ebp的观察窗口中perform_update()查看,我看到:

    ebp-18h 0x0022fd5c  unsigned int
    esp 0x0022fd60  unsigned int
Run Code Online (Sandbox Code Playgroud)

这很奇怪.它们之间的差异只有4个字节,下一次推送到堆栈将使它们相等并覆盖[ebp-18h]!
[ebp-18h]持有一份副本this->m_prim_channel_registry.1034D9BE call dword ptr [eax+14h]当它推动堆栈时,调用会破坏ebp-18h的内容.看起来堆栈已经过度增长(向下),并破坏了前一帧.

我的问题是:

  • 我正确地分析了这个问题吗?我在这里错过了什么吗?
  • 什么可能导致这样的腐败?我认为这个问题与编译器或SystemC库无关,可能是早先在其他地方发生的事情.
  • 调试此类损坏的技术有哪些?

更新

我相信我发现了问题,但我不能说我完全明白这一点.
在调用sc_simcontext::crunchexternal 的同一function()中,perform_update()调用systemc方法:

    // execute method processes

    sc_method_handle method_h = pop_runnable_method();
    while( method_h != 0 ) {
    try {
        method_h->execute();
    }
    catch( const sc_exception& ex ) {
        cout << "\n" << ex.what() << endl;
        m_error = true;
        return;
    }
    method_h = pop_runnable_method();
    }
Run Code Online (Sandbox Code Playgroud)

这些方法是先前注册的延迟函数调用.
其中一种方法是通过执行ret 4每次调用时缩小堆栈帧来返回到上述损坏发生的程度.

我是如何管理注册损坏的systemc方法的?
显然,SC_METHOD(f)当f是模块的虚函数时,使用它是一个坏主意.这样做会导致调用不同的,无关的"随机"函数.
我不确定为什么会发生这种情况以及存在这种限制的原因.此外,我不记得看到有关使用虚拟成员函数作为systemc方法的任何警告,但它肯定是问题所在.在SC_METHOD调用本身中调试方法注册时,我可以看到内部的函数指针指向与给予SC_METHOD宏不同的函数.

为了解决我所调用的问题SC_METHOD(wrapper_f),模块的wrapper_f一个简单的非虚拟成员函数,它所做的就是调用f原始的虚函数.而已.

Isr*_*man 0

看来你知道自己在做什么。

我可以给你一个建议,而不是一个解决方案,但这是我多次遇到的事情,它会破坏堆栈。

检查导致损坏的功能perform_update()。它是否将大数组定义为局部变量?如果是这样,它可能超出堆栈并覆盖那里的返回数据和其他重要数据。这是我遇到的最常见的堆栈损坏问题。

这是一个偷偷摸摸的问题,因为它取决于本地数组的大小和堆栈的数量。这会因系统而异。