UE4 内存写坏导致异常崩溃问题记录

科技资讯 投稿 8100 0 评论

UE4 内存写坏导致异常崩溃问题记录

1. 问题表现

原因基本上都是 read write memory 时触发了异常,盘查后初步怀疑是内存写坏了。

2. 排查期

    TBB
  • Ansi
  • Jemalloc
  • Stomp
    还有自带的内存分配器:
  • Binned
  • Binned2
  • Binned3
    可以参考文章 UE 中的内存分配器。
    其中 Stomp 是引擎提供的排查内存写坏的工具之一,通过增加参数 -stompmalloc 可以让 UE 默认采用该内存分配器,启用了之后崩溃的第一现场就是内存写坏的代码地址。
    通过排查发现崩溃原因是遍历迭代器时删除元素后没有及时 continue,大致示例如下:
for(TArray<Actor*>::TIterator Iter(Actors; Iter; ++ Iter
{
if (xxx
{
Iter.RemoveCurrent(; //没有 continue
}

if (xxx
{
Iter.RemoveCurrent(;//没有 continue
}
}

当数组元素只剩一个时,如果触发了两次 RemoveCurrent,就会导致写到数组之外的内存空间,RemoveCurrent 的机制会把后面的数组元素迁移到删除的位置上,保证数据连贯。同时 RemoveCurrent 完毕后会自动把迭代器的下标前移一位。

3. Stomp 原理

3.1 内存覆盖

大部分情况下有内存池的技术,且操作系统分配内存往往会向上按页对其分配,所以一时的内存越界读写有可能不会马上出现问题。而 Stomp 是在内存越界时就对其抛出异常。

3.2 实现原理

FMallocStomp::Malloc 和 FMallocStomp::Free 接管。

void* FMallocStomp::Malloc(SIZE_T Size, uint32 Alignment
void FMallocStomp::Free(void* InPtr

要做到写坏内存后能直接触发异常,需要在内存分配上做手脚,这里主要用到了两点:

    操作系统支持的 Pagefault 和 Page 权限控制
  • 哨兵机制
    Stomp 在给用户分配内存的时候会额外分配 2 个 Page 出来,分别在返回给用户的指针地址空间前后。当用户超出分配给他的内存上读写时,就会触发异常。其分配内存的流程大致如下:
    从 Page2 写数据一直写到 Page3,由于 Page3 被标记为不可读不可写,因此一旦出现越界,就会直接抛出异常
  1. 从 Page1 写数据一直写到 Page0,由于 Page0 末端分配了一个 FAllocationData,因此一旦越界,哨兵值会被覆盖,当释放内存时FMallocStomp::Free 就会对内存块的 FAllocationData 进行检查,一旦哨兵比对异常就抛出异常

编程笔记 » UE4 内存写坏导致异常崩溃问题记录

赞同 (41) or 分享 (0)
游客 发表我的评论   换个身份
取消评论

表情
(0)个小伙伴在吐槽