copy 算法doesn't check 迭代器范围,你的程序有heap-buffer-overflow 问题。
copy的实现很简单,就是一个简单的循环,可以查看libcxx的实现here。
这是一个普遍的内存问题,感谢编译器中强大的内存清理工具,我们可以快速定位问题。虽然你的程序现在用 SEGSEV 没有崩溃,但是它确实有内存缓冲区溢出,如果这是一个大程序,很可能导致其他代码崩溃(可能在不同的线程,不同的库中),并且很难排除故障原因,因为崩溃代码只是受害者。
使用-fsanitize=address构建,然后我们运行程序后得到清晰的报告:
==227==ERROR: AddressSanitizer: heap-buffer-overflow on address 0x602000000038 at pc 0x7f548b0f003d bp 0x7ffd43cf2ea0 sp 0x7ffd43cf2648 WRITE of size 16 at 0x602000000038 thread T0 #0 0x7f548b0f003c in __interceptor_memmove (/lib/x86_64-linux-gnu/libasan.so.5+0xa103c) #1 0x55afc64f43b5 in int* std::__copy_move<false, true, std::random_access_iterator_tag>::__copy_m<int>(int const*, int const*, int*) (/tmp/stackoverflow/a.out+0x43b5) #2 0x55afc64f3eeb in int* std::__copy_move_a<false, int*, int*>(int*, int*, int*) (/tmp/stackoverflow/a.out+0x3eeb) #3 0x55afc64f390c in __gnu_cxx::__normal_iterator<int*, std::vector<int, std::allocator<int> > > std::__copy_move_a2<false, __gnu_cxx::__normal_iterator<int*, std::vector<int, std::allocator<int> > >, __gnu_cxx::__normal_iterator<int*, std::vector<int, std::allocator<int> > > >(__gnu_cxx::__normal_iterator<int*, std::vector<int, std::allocator<int> > >, __gnu_cxx::__normal_iterator<int*, std::vector<int, std::allocator<int> > >, __gnu_cxx::__normal_iterator<int*, std::vector<int, std::allocator<int> > >) (/tmp/stackoverflow/a.out+0x390c) #4 0x55afc64f322e in __gnu_cxx::__normal_iterator<int*, std::vector<int, std::allocator<int> > > std::copy<__gnu_cxx::__normal_iterator<int*, std::vector<int, std::allocator<int> > >, __gnu_cxx::__normal_iterator<int*, std::vector<int, std::allocator<int> > > >(__gnu_cxx::__normal_iterator<int*, std::vector<int, std::allocator<int> > >, __gnu_cxx::__normal_iterator<int*, std::vector<int, std::allocator<int> > >, __gnu_cxx::__normal_iterator<int*, std::vector<int, std::allocator<int> > >) (/tmp/stackoverflow/a.out+0x322e) #5 0x55afc64f2834 in main (/tmp/stackoverflow/a.out+0x2834) #6 0x7f548ac880b2 in __libc_start_main (/lib/x86_64-linux-gnu/libc.so.6+0x270b2) #7 0x55afc64f238d in _start (/tmp/stackoverflow/a.out+0x238d)
0x602000000038 is located 0 bytes to the right of 8-byte region [0x602000000030,0x602000000038)
allocated by thread T0 here:
#0 0x7f548b15e947 in operator new(unsigned long) (/lib/x86_64-linux-gnu/libasan.so.5+0x10f947)
#1 0x55afc64f469c in __gnu_cxx::new_allocator<int>::allocate(unsigned long, void const*) (/tmp/stackoverflow/a.out+0x469c)
#2 0x55afc64f431d in std::allocator_traits<std::allocator<int> >::allocate(std::allocator<int>&, unsigned long) (/tmp/stackoverflow/a.out+0x431d)
#3 0x55afc64f3d35 in std::_Vector_base<int, std::allocator<int> >::_M_allocate(unsigned long) (/tmp/stackoverflow/a.out+0x3d35)
#4 0x55afc64f3582 in void std::vector<int, std::allocator<int> >::_M_range_initialize<int const*>(int const*, int const*, std::forward_iterator_tag) (/tmp/stackoverflow/a.out+0x3582)
#5 0x55afc64f2d98 in std::vector<int, std::allocator<int> >::vector(std::initializer_list<int>, std::allocator<int> const&) (/tmp/stackoverflow/a.out+0x2d98)
#6 0x55afc64f27bf in main (/tmp/stackoverflow/a.out+0x27bf)
#7 0x7f548ac880b2 in __libc_start_main (/lib/x86_64-linux-gnu/libc.so.6+0x270b2)
SUMMARY: AddressSanitizer: heap-buffer-overflow (/lib/x86_64-linux-gnu/libasan.so.5+0xa103c) in __interceptor_memmove
Shadow bytes around the buggy address:
0x0c047fff7fb0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
0x0c047fff7fc0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
0x0c047fff7fd0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
0x0c047fff7fe0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
0x0c047fff7ff0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
=>0x0c047fff8000: fa fa 00 00 fa fa 00[fa]fa fa fa fa fa fa fa fa
0x0c047fff8010: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa
0x0c047fff8020: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa
0x0c047fff8030: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa
0x0c047fff8040: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa
0x0c047fff8050: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa
Shadow byte legend (one shadow byte represents 8 application bytes):
Memory sanitizer 是一种通用的记忆工具。当专注于 STL 迭代器调试时,STL 库确实有一些有用的工具:
对于Visual Studio 2019,将#define _ITERATOR_DEBUG_LEVEL 1 添加到您的代码中,或添加到项目配置中。运行您的代码时,调用堆栈出现异常:
> iterator_check.exe!std::_Vector_const_iterator<std::_Vector_val<std::_Simple_types<int>>>::_Verify_offset(const __int64 _Off) Line 113 C++
iterator_check.exe!std::_Get_unwrapped_n<std::_Vector_iterator<std::_Vector_val<std::_Simple_types<int>>> &,__int64>(std::_Vector_iterator<std::_Vector_val<std::_Simple_types<int>>> & _It, const __int64 _Off) Line 1318 C++
iterator_check.exe!std::copy<std::_Vector_iterator<std::_Vector_val<std::_Simple_types<int>>>,std::_Vector_iterator<std::_Vector_val<std::_Simple_types<int>>>>(std::_Vector_iterator<std::_Vector_val<std::_Simple_types<int>>> _First, std::_Vector_iterator<std::_Vector_val<std::_Simple_types<int>>> _Last, std::_Vector_iterator<std::_Vector_val<std::_Simple_types<int>>> _Dest) Line 3813 C++
iterator_check.exe!main() Line 13 C++
[External Code]
我们可以看到调用了一个额外的检查函数_Verify_offset,然后捕获了错误。
对于来自gcc的libstdc++,还有similar debugging mode.:编译器标志-D_GLIBCXX_DEBUG
在 GCC 9 下使用 compiler flag -D_GLIBCXX_DEBUG 构建您的代码并运行它,我们会收到错误报告:
/usr/include/c++/9/bits/stl_algobase.h:471:
In function:
_OI std::copy(_II, _II, _OI) [with _II =
__gnu_debug::_Safe_iterator<__gnu_cxx::__normal_iterator<int*,
std::__cxx1998::vector<int, std::allocator<int> > >,
std::__debug::vector<int>, std::random_access_iterator_tag>; _OI =
__gnu_debug::_Safe_iterator<__gnu_cxx::__normal_iterator<int*,
std::__cxx1998::vector<int, std::allocator<int> > >,
std::__debug::vector<int>, std::random_access_iterator_tag>]
Error: attempt to subscript a dereferenceable (start-of-sequence) iterator 4
step from its current position, which falls outside its dereferenceable
range.
Objects involved in the operation:
iterator "__result" @ 0x0x7fff292a8dd0 {
type = __gnu_cxx::__normal_iterator<int*, std::__cxx1998::vector<int, std::allocator<int> > > (mutable iterator);
state = dereferenceable (start-of-sequence);
references sequence with type 'std::__debug::vector<int, std::allocator<int> >' @ 0x0x7fff292a8e70
}
Aborted
这也很有帮助!
对于来自 LLVM 的 libcxx,还有something similar。不过文档有些不完整,大家可以试试看。
请注意,所有工具都有性能下降,因此它仅在测试构建中有用,不适用于生产构建。