c++ - 不可能的堆栈跟踪?尽管已检查,但使用this = nullptr的访问冲突

标签 c++ assembly stack-trace access-violation

我正在分析一个相当复杂的项目中的罕见但一致的崩溃,这是这些“不可能的堆栈跟踪”之一,其含义是:乍一看,一切看起来都还不错。

崩溃本身是“引发了未处理的异常:读取访问冲突。这是nullptr。”。

堆栈当前帧中的代码如下所示:

FT6VehiclePathCheckpoint AT6Building::GetVehicleCheckpoint() const
{
    FT6VehiclePathCheckpoint targetCheckpoint = FT6VehiclePathCheckpoint();
    if (GetBusStopComponent() != nullptr)
    {
        ...

崩溃发生在“if(Get(BusStopComponent()!= nullptr))”行中。函数“GetBusStopComponent()”本身是该对象的成员变量的内联 setter/getter ,因此它不会显示在跟踪中。另外,在内存转储中,“this”的确为0。到目前为止,还不错。

但是,其正上方的堆栈框架如下所示:
const AT6Building* portalBuildingByLocation = pathFinder != nullptr ? pathFinder->GetPortalBuildingByLocation(startLocation) : GetNearestDrivewayBuilding();

if (portalBuildingByLocation != nullptr)
{
    startCheckpoint = portalBuildingByLocation->GetVehicleCheckpoint();
}

(指向“portalBuildingByLocation-> GetVehicleCheckpoint”行)。显然,在调用该函数之前,需要执行nullptr-check。乍看之下^^。

接下来,我怀疑“GetVehicleCheckpoint”内的某些内存损坏弄乱了堆栈跟踪?但是那里真的没有什么。 “FT6VehiclePathCheckpoint”初始化甚至没有构造函数-只是一堆直接初始化的字段(它们都像nullptr和整数文字)。

该代码是使用cl.exe编译的,并且启用了优化功能,因此我怀疑我在某个地方有一些UB,并且编译器依赖于某种东西...

因此,尽管在阅读汇编代码方面我不是一个菜鸟,但是我还是尝试了。这是GetVehicleCheckpoint函数的反汇编的第一部分:
FT6VehiclePathCheckpoint AT6Building::GetVehicleCheckpoint() const
{
00007FF7304714B0  mov         qword ptr [rsp+8],rbx  
00007FF7304714B5  mov         qword ptr [rsp+10h],rsi  
00007FF7304714BA  push        rdi  
00007FF7304714BB  sub         rsp,40h  
    FT6VehiclePathCheckpoint targetCheckpoint = FT6VehiclePathCheckpoint();
00007FF7304714BF  xor         eax,eax  
00007FF7304714C1  mov         qword ptr [rsp+28h],0FFFFFFFFFFFFFFFFh  
00007FF7304714CA  mov         qword ptr [rsp+30h],rax  
00007FF7304714CF  mov         rsi,rcx  

    if (GetBusStopComponent() != nullptr)
00007FF7304714D2  mov         rcx,qword ptr [rcx+5F8h]  

在最后一行崩溃。好吧,如果我正确地知道了它,它试图从rcx + 5F8h读取一个qword,可能rcx为0? (但是,如果是这样,该错误不应该是“访问冲突,尝试从0x5f8读取”吗?嗯,也许Visual Studio希望提供更多帮助。)

另外,我验证了,是的:BusStopComponent确实位于该类的偏移量0x5F8处。

好的,所以我尝试将rcx跟踪到倒数第二个stackframe:
            const AT6Building* portalBuildingByLocation = pathFinder != nullptr ? pathFinder->GetPortalBuildingByLocation(startLocation) : GetNearestDrivewayBuilding();
00007FF730410F21  lea         rdx,[rbp-29h]  
00007FF730410F25  mov         rcx,r12  
00007FF730410F28  call        AT6AStarPathfinder::GetPortalBuildingByLocation (07FF73059CD30h)  
00007FF730410F2D  jmp         UT6Agent::HandleAgentArrivedToUseCar+34Ah (07FF730410F4Ah)  
00007FF730410F2F  test        r12,r12  
00007FF730410F32  je          UT6Agent::HandleAgentArrivedToUseCar+342h (07FF730410F42h)  
00007FF730410F34  lea         rdx,[rbp-29h]  
00007FF730410F38  mov         rcx,r12  
00007FF730410F3B  call        AT6AStarPathfinder::GetPortalBuildingByLocation (07FF73059CD30h)  
00007FF730410F40  jmp         UT6Agent::HandleAgentArrivedToUseCar+34Ah (07FF730410F4Ah)  
00007FF730410F42  mov         rcx,rsi  
00007FF730410F45  call        UT6Agent::GetNearestDrivewayBuilding (07FF73040DA50h)  

            if (portalBuildingByLocation != nullptr)
00007FF730410F4A  test        rax,rax  
00007FF730410F4D  je          UT6Agent::HandleAgentArrivedToUseCar+36Ch (07FF730410F6Ch)  
            {
                startCheckpoint = portalBuildingByLocation->GetVehicleCheckpoint();
00007FF730410F4F  mov         rcx,rax  
00007FF730410F52  lea         rdx,[rbp-9]  
00007FF730410F56  call        AT6Building::GetVehicleCheckpoint (07FF7304714B0h)  
00007FF730410F5B  movups      xmm0,xmmword ptr [rax]  

再说一遍:我对解释程序集并不十分有信心,但是从眼神来看,我发现rcx设置为rax中的任何值,并且rax被正确测试为0。因此,没有幻想“编译器在优化时删除了nullptr检查”。

那么,有什么见解可能导致访问冲突?有什么可疑之处可以给我带头吗?

干杯,伊美

最佳答案

知道了! (或者更确切地说是David Wohlferd找到了)。

“上面的堆栈框架”具有误导性,因为代码在同一函数中(在不同的if分支中)多次重复使用此调用。当查看局部变量时,分支路径实际上是不可能的,我将问题确定为另一个数组中的nullptr。

感谢David Wohlferd!死了! :)

关于c++ - 不可能的堆栈跟踪?尽管已检查,但使用this = nullptr的访问冲突,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/62188480/

相关文章:

c++ - Windows 套接字发送字符串流

c++ - 为什么ARM的这条分支指令不起作用

assembly - 实现ARM SMC的简单方法是什么

gcc - 使用 GCC 4.8.2 的 Asan 时堆栈跟踪中未解析的符号

c++ - 在 GCC 上的 GTEST 中测试失败时是否有打印堆栈跟踪的选项?

java - "Software caused connection abort: socket write error"的官方原因

c++ - Windows xp 32 位 d3d9.dll 与 Windows 7 64 位 d3d9.dll

c++ - 三角形三角形重叠(但不是边缘)

c++ - 头文件包含在纯虚类中

c++ - C/C++ 内联汇编不正确的操作数类型