通过分配大量内存来跟踪堆损坏

Track down heap corruption by allocating lots of memory?

本文关键字:跟踪 损坏 内存 分配      更新时间:2023-10-16

在我的程序中,我遇到了以下错误:

free(): invalid size
Aborted (core dumped)

运行GDB时,我发现这发生在向量的析构函数中:

#0  0x00007ffff58e8c01 in free () from /lib/x86_64-linux-gnu/libc.so.6
#1  0x0000555555dd44e2 in __gnu_cxx::new_allocator<int>::deallocate (this=0x7fffffff6bf0, __p=0x555557117810) at /usr/include/c++/7/ext/new_allocator.h:125
#2  0x0000555555dcfbd7 in std::allocator_traits<std::allocator<int> >::deallocate (__a=..., __p=0x555557117810, __n=1) at /usr/include/c++/7/bits/alloc_traits.h:462
#3  0x0000555555dc85e6 in std::_Vector_base<int, std::allocator<int> >::_M_deallocate (this=0x7fffffff6bf0, __p=0x555557117810, __n=1)
at /usr/include/c++/7/bits/stl_vector.h:180
#4  0x0000555555dc49e1 in std::_Vector_base<int, std::allocator<int> >::~_Vector_base (this=0x7fffffff6bf0, __in_chrg=<optimized out>)
at /usr/include/c++/7/bits/stl_vector.h:162
#5  0x0000555555dbc5c9 in std::vector<int, std::allocator<int> >::~vector (this=0x7fffffff6bf0, __in_chrg=<optimized out>) at /usr/include/c++/7/bits/stl_vector.h:435
#6  0x0000555556338081 in Gambit::Printers::HDF5Printer2::get_buffer_idcodes[abi:cxx11](std::vector<Gambit::Printers::HDF5MasterBuffer*, std::allocator<Gambit::Printers::HDF5MasterBuffer*> > const&) (this=0x555556fd8820, masterbuffers=...) at /home/farmer/repos/gambit/copy3/Printers/src/printers/hdf5printer_v2/hdf5printer_v2.cpp:2183

最后一行代码就是:

std::vector<int> alllens(myComm.Get_size());

首先,我不太明白为什么在这里调用析构函数,但假设它是向量动态构建的正常部分,那么我想这个错误一定是由于某种堆损坏造成的。

不过,我还不完全理解,是不是代码的其他部分以前非法访问了应该分配给这个向量的内存?

其次,我尝试过通过Intel Inspector运行它,我确实发现了一堆"无效内存访问"answers"未初始化内存访问"问题,但在我使用的库中,比如HDF5,它们看起来都像误报。

是否有一些代码内的方法可以缩小问题的确切来源?例如,由于它是由动态内存分配触发的,我可以在代码中越来越早地开始分配巨大的数组,以尝试在更接近崩溃源的地方触发崩溃吗?我试着四处寻找这样的东西是否有效或有用,但没有找到任何信息,所以也许这不是一个好主意?

因此,我通过一些MPI例程破坏了堆,即缓冲区长度等参数不正确。不幸的是,MPI库中发生了很多疯狂的事情,所以像Intel Inspector这样的内存分析器在查找它时没有那么有用。

然而,我了解了Address Sanitizer(https://en.wikipedia.org/wiki/AddressSanitizer)它与现代GNU编译器一起提供,结果证明它很棒!在我的CMake项目中根据它进行编译(来自https://gist.github.com/jlblancoc/44be9d4d466f0a973b1f3808a8e56782)

cmake .. -DCMAKE_CXX_FLAGS="-fsanitize=address  -fsanitize=leak -g" 
-DCMAKE_C_FLAGS="-fsanitize=address  -fsanitize=leak -g"
-DCMAKE_EXE_LINKER_FLAGS="-fsanitize=address  -fsanitize=leak"
-DCMAKE_MODULE_LINKER_FLAGS="-fsanitize=address  -fsanitize=leak"

使用运行

export ASAN_OPTIONS=fast_unwind_on_malloc=0

(不知道这是否真的必要(,当我的堆损坏发生时,收到了一个极好的回溯:

==12748==ERROR: AddressSanitizer: heap-buffer-overflow on address 0x602000521340 at pc 0x7fda5011577a bp 0x7ffe231c55e0 sp 0x7ffe231c4d88
WRITE of size 32 at 0x602000521340 thread T0
#0 0x7fda50115779  (/usr/lib/x86_64-linux-gnu/libasan.so.4+0x79779)
#1 0x7fda4fcd84e3  (/usr/lib/x86_64-linux-gnu/libmpich.so.0+0xf24e3)
#2 0x7fda4fc228d7  (/usr/lib/x86_64-linux-gnu/libmpich.so.0+0x3c8d7)
#3 0x7fda4fc23a26  (/usr/lib/x86_64-linux-gnu/libmpich.so.0+0x3da26)
#4 0x7fda4fc2316c  (/usr/lib/x86_64-linux-gnu/libmpich.so.0+0x3d16c)
#5 0x7fda4fc2406c in PMPI_Gather (/usr/lib/x86_64-linux-gnu/libmpich.so.0+0x3e06c)
#6 0x55e0c18586b0 in void Gambit::GMPI::Comm::Gather<unsigned long>(std::vector<unsigned long, std::allocator<unsigned long> >&, std::vector<unsigned long, std::allocator<unsigned long> >&, int) /home/farmer/repos/gambit/copy3/Utils/include/gambit/Utils/mpiwrapper.hpp:450
...etc... 

它直接指向我搞砸的MPI调用。太神了

但为了回答我的OP问题,我分配大量堆内存以触发更接近问题的崩溃的想法并没有真正奏效。不知道为什么。我想我只是不明白引擎盖下面发生了什么。事实上,我看到崩溃的地方是代码中MPI调用之前的,所以这很令人困惑。我猜编译器移动了一些东西?我确实关闭了优化,但我想二进制中的操作顺序仍然可能与我预期的不同?