前言:记录这个案例的目的,是为了说明:
- 堆内存溢出这样的错误,可能是由于错误地自定义了一个 compare 函数而引起的。
这两者之间似乎没什么直接联系,若不是碰巧有 Address Sanitizer 的帮助,基于之前的认知,可能发现问题和定位原因都很困难。
之前在 LeetCode 上做【179. 最大数 】这道题时写了如下代码:
1bool comp3(const int a, const int b) {
2 if (a == b) {
3 return true;
4 }
5 string sa = to_string(a);
6 string sb = to_string(b);
7 return sa + sb > sb + sa;
8}
9class Solution {
10public:
11 string largestNumber(vector<int>& nums) {
12 sort(nums.begin(), nums.end(), comp3);
13 //...
14 }
15};
可以看到,此处涉及自定义 std::sort
的比对方法。此段代码提交后,平台报“执行错误”:
1AddressSanitizer: heap-buffer-overflow on address ...
可以看出错误是由 Address Sanitizer 报出来的。在 Linux 上通过如下方式编译后运行,可以复现该错误:
1g++ -std=c++14 -O -g -fsanitize=address ./main.cpp
2./a.out
1=================================================================
2==129056==ERROR: AddressSanitizer: heap-buffer-overflow on address 0x61400000ffd0 at pc 0x403093 bp 0x7fff13dbe9b0 sp 0x7fff13dbe9a0
3READ of size 4 at 0x61400000ffd0 thread T0
4 #0 0x403092 in operator()<__gnu_cxx::__normal_iterator<int*, std::vector<int> >, __gnu_cxx::__normal_iterator<int*, std::vector<int> > > /usr/include/c++/4.9/bits/predefined_ops.h:121
5 #1 0x403092 in __unguarded_partition<__gnu_cxx::__normal_iterator<int*, std::vector<int> >, __gnu_cxx::__ops::_Iter_comp_iter<bool (*)(int, int)> > /usr/include/c++/4.9/bits/stl_algo.h:1901
6 #2 0x403092 in __unguarded_partition_pivot<__gnu_cxx::__normal_iterator<int*, std::vector<int> >, __gnu_cxx::__ops::_Iter_comp_iter<bool (*)(int, int)> > /usr/include/c++/4.9/bits/stl_algo.h:1922
7 #3 0x403092 in void std::__introsort_loop<__gnu_cxx::__normal_iterator<int*, std::vector<int, std::allocator<int> > >, long, __gnu_cxx::__ops::_Iter_comp_iter<bool (*)(int, int)> >(__gnu_cxx::__normal_iterator<int*, std::vector<int, std::allocator<int> > >, __gnu_cxx::__normal_iterator<int*, std::vector<int, std::allocator<int> > >, long, __gnu_cxx::__ops::_Iter_comp_iter<bool (*)(int, int)>) /usr/include/c++/4.9/bits/stl_algo.h:1952
8 #4 0x40329d in __sort<__gnu_cxx::__normal_iterator<int*, std::vector<int> >, __gnu_cxx::__ops::_Iter_comp_iter<bool (*)(int, int)> > /usr/include/c++/4.9/bits/stl_algo.h:1967
9 #5 0x40329d in sort<__gnu_cxx::__normal_iterator<int*, std::vector<int> >, bool (*)(int, int)> /usr/include/c++/4.9/bits/stl_algo.h:4717
10 #6 0x40329d in Solution::largestNumber(std::vector<int, std::allocator<int> >&) main.cpp:23
11 #7 0x401d62 in main main.cpp:46
12 #8 0x7fa8defec83f in __libc_start_main (/lib/x86_64-linux-gnu/libc.so.6+0x2083f)
13 #9 0x401508 in _start (/home/g00535345/a.out+0x401508)
140x61400000ffd0 is located 0 bytes to the right of 400-byte region [0x61400000fe40,0x61400000ffd0)
15allocated by thread T0 here:
16 #0 0x7fa8df9e8eaf in operator new(unsigned long) (/usr/lib/x86_64-linux-gnu/libasan.so.1+0x57eaf)
17 #1 0x401c44 in __gnu_cxx::new_allocator<int>::allocate(unsigned long, void const*) /usr/include/c++/4.9/ext/new_allocator.h:104
18 #2 0x401c44 in std::allocator_traits<std::allocator<int> >::allocate(std::allocator<int>&, unsigned long) /usr/include/c++/4.9/bits/alloc_traits.h:488
19 #3 0x401c44 in std::_Vector_base<int, std::allocator<int> >::_M_allocate(unsigned long) /usr/include/c++/4.9/bits/stl_vector.h:170
20 #4 0x401c44 in _M_range_initialize<int const*> /usr/include/c++/4.9/bits/stl_vector.h:1287
21 #5 0x401c44 in vector /usr/include/c++/4.9/bits/stl_vector.h:377
22 #6 0x401c44 in main main.cpp:43
23SUMMARY: AddressSanitizer: heap-buffer-overflow /usr/include/c++/4.9/bits/predefined_ops.h:121 operator()<__gnu_cxx::__normal_iterator<int*, std::vector<int> >, __gnu_cxx::__normal_iterator<int*, std::vector<int> > >
24Shadow bytes around the buggy address:
25 0x0c287fff9fa0: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa
26 0x0c287fff9fb0: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa
27 0x0c287fff9fc0: fa fa fa fa fa fa fa fa 00 00 00 00 00 00 00 00
28 0x0c287fff9fd0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
29 0x0c287fff9fe0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
30=>0x0c287fff9ff0: 00 00 00 00 00 00 00 00 00 00[fa]fa fa fa fa fa
31 0x0c287fffa000: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa
32 0x0c287fffa010: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa
33 0x0c287fffa020: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa
34 0x0c287fffa030: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa
35 0x0c287fffa040: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa
36Shadow byte legend (one shadow byte represents 8 application bytes):
37 Addressable: 00
38 Partially addressable: 01 02 03 04 05 06 07
39 Heap left redzone: fa
40 Heap right redzone: fb
41 Freed heap region: fd
42 Stack left redzone: f1
43 Stack mid redzone: f2
44 Stack right redzone: f3
45 Stack partial redzone: f4
46 Stack after return: f5
47 Stack use after scope: f8
48 Global redzone: f9
49 Global init order: f6
50 Poisoned by user: f7
51 Contiguous container OOB:fc
52 ASan internal: fe
53==129056==ABORTING
可见这是一个堆越界的错误,从堆栈中可以看出是 std::sort
出了问题,那么出问题的只可能是 comp3()
这个函数,但是看来看去都不知道问题出在哪里。无奈之下使出夹逼法,发现删去开头的 if 判断,问题就消失了。
仔细查阅 std::sort
的文档,发现其在对入参的描述处有提到:
1template< class RandomIt, class Compare > 2void sort( RandomIt first, RandomIt last, Compare comp );
Parameters
Type requirements
- ……
- Compare must meet the requirements of Compare .
然后顺着这个链接就可以找到对 comp
的要求,其中一个就是:
For all a,
comp(a,a)==false
破案了,这里的 comp3()
不符合这个要求……
所以最后的最后,把那块去掉就好了:
1class Solution {
2public:
3 string largestNumber(vector<int>& nums) {
4 sort(nums.begin(), nums.end(), [](int a, int b){
5 return to_string(a) + to_string(b) > to_string(b) + to_string(a);
6 });
7 //...
8 }
9};