一、代码准备
1 score.h
#ifndef SCORE_H
#define SCORE_H
#ifdef __cplusplus
extern "C" {
#endif
void AddStudent(const char *name, int score);
int GetScore(const char *name);
void PrintStudentsScores();
#ifdef __cplusplus
}
#endif
#endif
2 score.cpp
#include "score.h"
#include <iostream>
#include <string>
#include <unordered_map>
static std::unordered_map<std::string, int> g_scores;
void AddStudent(const char *name, int score)
{
if (name) {
g_scores[std::string(name)] = score;
}
}
int GetScore(const char *name)
{
if (!name) {
return -1;
}
std::string key(name);
auto it = g_scores.find(key);
return (it != g_scores.end()) ? it->second : -1;
}
void PrintStudentsScores()
{
std::cout << "Students scores:" << std::endl;
for (const auto &pair : g_scores) {
std::cout << pair.first << " -> " << pair.second << std::endl;
}
}
3 main.cpp
#include <iostream>
#include <unordered_map>
#include "score.h"
int main()
{
// 随便构造1个unordered_map
std::unordered_map<std::string, int> wordCount;
wordCount["apple"] = 10;
wordCount["banana"] = 5;
wordCount["orange"] = 8;
// 调用libscore.so中的函数
AddStudent("A", 95);
AddStudent("B", 87);
AddStudent("C", 92);
std::cout << "B score: " << GetScore("B") << std::endl;
std::cout << "D score: " << GetScore("D") << std::endl;
PrintStudentsScores();
}
二、问题构造
1 在5.10内核(gcc 10.3)编译libscore.so
g++ -fPIC -c score.cpp -g -o score.o
g++ -shared score.o -o libscore.so
2 在6.6内核(gcc 12.3)编译main
g++ -o main main.cpp -L. -lscore -Wl,-rpath,.
3 执行main
运行进程会异常。
4 部署asan
g++ -fsanitize=address -o main main.cpp -L. -lscore -lasan -Wl,-rpath,.
5 执行main,在std::unordered_map发生异常
[root@3558d81212aa /]# ./main
B score: 87
D score: -1
Students scores:
``▒``C▒▒▒▒▒▒▒▒▒▒▒▒▒▒\▒▒▒▒4 4M▒▒▒=================================================================
==172==ERROR: AddressSanitizer: heap-buffer-overflow on address 0x6060000002f8 at pc 0x7f185d173572 bp 0x7ffc1162f7d0 sp 0x7ffc1162ef78
READ of size 1024 at 0x6060000002f8 thread T0
#0 0x7f185d173571 in __interceptor_fwrite ../../../../libsanitizer/sanitizer_common/sanitizer_common_interceptors.inc:1157
#1 0x7f185d016d04 in std::basic_streambuf<char, std::char_traits<char> >::sputn(char const*, long) /usr/src/debug/gcc-12.3.1-98.oe2403sp2.x86_64/obj-x86_64-openEuler-linux/x86_64-openEuler-linux/libstdc++-v3/include/streambuf:456
#2 0x7f185d016d04 in void std::__ostream_write<char, std::char_traits<char> >(std::basic_ostream<char, std::char_traits<char> >&, char const*, long) /usr/src/debug/gcc-12.3.1-98.oe2403sp2.x86_64/obj-x86_64-openEuler-linux/x86_64-openEuler-linux/libstdc++-v3/include/bits/ostream_insert.h:51
#3 0x7f185d016d04 in std::basic_ostream<char, std::char_traits<char> >& std::__ostream_insert<char, std::char_traits<char> >(std::basic_ostream<char, std::char_traits<char> >&, char const*, long) /usr/src/debug/gcc-12.3.1-98.oe2403sp2.x86_64/obj-x86_64-openEuler-linux/x86_64-openEuler-linux/libstdc++-v3/include/bits/ostream_insert.h:102
#4 0x7f185d11dcfc in PrintStudentsScores /root/work/leetcode/cpp/score.cpp:30
#5 0x4078cc in main (/main+0x4078cc)
#6 0x7f185cc24a0f (/lib64/libc.so.6+0x23a0f)
#7 0x7f185cc24ac8 in __libc_start_main (/lib64/libc.so.6+0x23ac8)
#8 0x4073f4 in _start (/main+0x4073f4)
0x6060000002f8 is located 0 bytes to the right of 56-byte region [0x6060000002c0,0x6060000002f8)
allocated by thread T0 here:
#0 0x7f185d1e51f8 in operator new(unsigned long) ../../../../libsanitizer/asan/asan_new_delete.cpp:95
#1 0x40a6a3 in std::__new_allocator<std::__detail::_Hash_node<std::pair<std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> > const, int>, true> >::allocate(unsigned long, void const*) (/main+0x40a6a3)
#2 0x409eb2 in std::allocator_traits<std::allocator<std::__detail::_Hash_node<std::pair<std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> > const, int>, true> > >::allocate(std::allocator<std::__detail::_Hash_node<std::pair<std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> > const, int>, true> >&, unsigned long) (/main+0x409eb2)
#3 0x4097ef in std::__detail::_Hash_node<std::pair<std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> > const, int>, true>* std::__detail::_Hashtable_alloc<std::allocator<std::__detail::_Hash_node<std::pair<std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> > const, int>, true> > >::_M_allocate_node<std::piecewise_construct_t const&, std::tuple<std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >&&>, std::tuple<> >(std::piecewise_construct_t const&, std::tuple<std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >&&>&&, std::tuple<>&&) (/main+0x4097ef)
#4 0x408ef5 in std::_Hashtable<std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >, std::pair<std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> > const, int>, std::allocator<std::pair<std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> > const, int> >, std::__detail::_Select1st, std::equal_to<std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> > >, std::hash<std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> > >, std::__detail::_Mod_range_hashing, std::__detail::_Default_ranged_hash, std::__detail::_Prime_rehash_policy, std::__detail::_Hashtable_traits<true, false, true> >::_Scoped_node::_Scoped_node<std::piecewise_construct_t const&, std::tuple<std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >&&>, std::tuple<> >(std::__detail::_Hashtable_alloc<std::allocator<std::__detail::_Hash_node<std::pair<std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> > const, int>, true> > >*, std::piecewise_construct_t const&, std::tuple<std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >&&>&&, std::tuple<>&&) (/main+0x408ef5)
#5 0x408a29 in std::__detail::_Map_base<std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >, std::pair<std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> > const, int>, std::allocator<std::pair<std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> > const, int> >, std::__detail::_Select1st, std::equal_to<std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> > >, std::hash<std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> > >, std::__detail::_Mod_range_hashing, std::__detail::_Default_ranged_hash, std::__detail::_Prime_rehash_policy, std::__detail::_Hashtable_traits<true, false, true>, true>::operator[](std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >&&) (/main+0x408a29)
#6 0x408345 in std::unordered_map<std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >, int, std::hash<std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> > >, std::equal_to<std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> > >, std::allocator<std::pair<std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> > const, int> > >::operator[](std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >&&) (/main+0x408345)
#7 0x7f185d11db25 in AddStudent /root/work/leetcode/cpp/score.cpp:11
#8 0x407861 in main (/main+0x407861)
#9 0x7f185cc24a0f (/lib64/libc.so.6+0x23a0f)
SUMMARY: AddressSanitizer: heap-buffer-overflow ../../../../libsanitizer/sanitizer_common/sanitizer_common_interceptors.inc:1157 in __interceptor_fwrite
Shadow bytes around the buggy address:
0x0c0c7fff8000: fa fa fa fa fd fd fd fd fd fd fd fa fa fa fa fa
0x0c0c7fff8010: 00 00 00 00 00 00 06 fa fa fa fa fa 00 00 00 00
0x0c0c7fff8020: 00 00 00 fa fa fa fa fa 00 00 00 00 00 00 00 fa
0x0c0c7fff8030: fa fa fa fa 00 00 00 00 00 00 00 fa fa fa fa fa
0x0c0c7fff8040: 00 00 00 00 00 00 00 fa fa fa fa fa 00 00 00 00
=>0x0c0c7fff8050: 00 00 00 fa fa fa fa fa 00 00 00 00 00 00 00[fa]
0x0c0c7fff8060: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa
0x0c0c7fff8070: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa
0x0c0c7fff8080: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa
0x0c0c7fff8090: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa
0x0c0c7fff80a0: 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):
Addressable: 00
Partially addressable: 01 02 03 04 05 06 07
Heap left redzone: fa
Freed heap region: fd
Stack left redzone: f1
Stack mid redzone: f2
Stack right redzone: f3
Stack after return: f5
Stack use after scope: f8
Global redzone: f9
Global init order: f6
Poisoned by user: f7
Container overflow: fc
Array cookie: ac
Intra object redzone: bb
ASan internal: fe
Left alloca redzone: ca
Right alloca redzone: cb
==172==ABORTING
三、问题分析
libscore.so是基于gcc 10编译的,main是基于gcc 12编译的,两者在std::unordered_map的内部实现上差异较大。main链接libscore.so运行后,std::unordered_map的逻辑混乱导致异常。
如何识别到是哪个gcc版本编译的,可以通过strings main | grep ^GLIBCXX 查看GLIBCXX的版本,进而分析出gcc版本。