我正在分析一个相当复杂的项目中的罕见但一致的崩溃,这是这些“不可能的堆栈跟踪”之一,其含义是:乍一看,一切看起来都还不错。
崩溃本身是“引发了未处理的异常:读取访问冲突。这是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/