如何正确解读AddressSanitizer报错?C++跨平台服务端内存排查
客户端-服务端程序跨平台ASan报错排查与修复
问题背景
用C++(包含大量C风格代码)编写的小型客户端-服务端程序,在macOS上通过AddressSanitizer(ASan)编译运行无报错,但在Ubuntu Docker环境执行测试时触发ASan报错。ASan提示线程T1对线程T0栈帧中偏移192的newsockfd变量(对应代码第59行)执行非法读操作,但该行代码size_t newsockfd = accept(sockfd, (struct sockaddr *)&cli_addr, &clilen);无明显语法异常。
ASan完整报错日志
# ./single_client.sh clang -shared -fPIC -ldl -O3 -o monkey.so monkey.c clang++ single_client.cpp -std=c++17 -g -O3 -Werror -Wall -Wextra -pthread -pedantic -o single_client clang++ multiple_client.cpp -std=c++17 -g -O3 -Werror -Wall -Wextra -pthread -pedantic -o multiple_client clang++ random_clients.cpp -std=c++17 -g -O3 -Werror -Wall -Wextra -pthread -pedantic -o random_clients clang++ simple_server.cpp -std=c++17 -g -O3 -Werror -Wall -Wextra -pthread -pedantic -o simple_server clang++ server.cpp -std=c++17 -g -O3 -g -fsanitize=address -Werror -Wall -Wextra -pthread -pedantic -o server clang++ client.cpp -std=c++17 -g -O3 -g -fsanitize=address -Werror -Wall -Wextra -pthread -pedantic -o client kill: usage: kill [-s sigspec | -n signum | -sigspec] pid | jobspec ... or kill -l [sigspec] [TEST] Send [TEST] Human readeable: 4 alex [TEST] Send binary hex: 00000004 616c6578 [TEST] Send [TEST] Human readeable: 11 hello world [TEST] Send binary hex: 0000000b 68656c6c 6f20776f 726c64 [TEST] Read ================================================================= ==61==ERROR: AddressSanitizer: unknown-crash on address 0xffffd05639c0 at pc 0x00000050a6a4 bp 0xffffabcfe8f0 sp 0xffffabcfe908 READ of size 8 at 0xffffd05639c0 thread T1 #0 0x50a6a3 (/mess/my_data/artifacts/server+0x50a6a3) #1 0xffffaf87d087 (/lib/aarch64-linux-gnu/libpthread.so.0+0x7087) Address 0xffffd05639c0 is located in stack of thread T0 at offset 192 in frame #0 0x50a07b (/mess/my_data/artifacts/server+0x50a6a3) This frame has 6 object(s): [32, 36) 'clilen' (line 24) [48, 64) 'serv_addr' (line 25) [80, 96) 'cli_addr' (line 25) [112, 160) 'm1' (line 27) [192, 200) 'newsockfd' (line 59) <== Memory access at offset 192 is inside this variable [224, 232) 'thread' (line 61) HINT: this may be a false positive if your program uses some custom stack unwind mechanism or swapcontext (longjmp and C++ exceptions *are* supported) SUMMARY: AddressSanitizer: unknown-crash (/mess/my_data/artifacts/server+0x50a6a3) Shadow bytes around the buggy address: 0x200ffa0ac6e0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 0x200ffa0ac6f0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 0x200ffa0ac700: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 0x200ffa0ac710: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 0x200ffa0ac720: f1 f1 f1 f1 04 f2 00 00 f2 f2 00 00 f2 f2 00 00 =>0x200ffa0ac730: 00 00 00 00 f2 f2 f2 f2[00]f2 f2 f2 f8 f3 f3 f3 0x200ffa0ac740: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 0x200ffa0ac750: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 0x200ffa0ac760: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 0x200ffa0ac770: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 0x200ffa0ac780: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 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 Thread T1 created by T0 here: #0 0x4441ab (/mess/my_data/artifacts/server+0x4441ab) #1 0x50a33f (/mess/my_data/artifacts/server+0x50a33f) #2 0xffffaf6ed79f (/lib/aarch64-linux-gnu/libc.so.6+0x2079f) #3 0x41f697 (/mess/my_data/artifacts/server+0x41f697) ==61==ABORTING terminate called after throwing an instance of 'std::runtime_error' what(): could not read message from server: Connection reset by peer single_client_impl.sh: line 23: 63 Aborted ./simple-messanger-tests/src/single_client 8081 single_client_impl.sh: line 1: kill: (61) - No such process
字节地址定位错误位置的方法
- 用addr2line工具:在Docker环境中执行
addr2line -e server 0x50a6a3,直接将内存地址映射到代码行(编译时已添加-g调试选项,满足工具要求) - 用gdb调试:运行
gdb ./server,执行break *0x50a6a3设置断点,启动程序触发报错后,用bt命令查看完整调用栈,定位具体代码位置
报错成因分析
- 核心问题:线程T1访问了线程T0的栈变量
newsockfd,但T0的栈帧可能已被销毁(比如T0创建T1后退出了当前函数,栈内存被系统回收或覆盖) - 典型场景:创建线程T1时,将T0栈上的
newsockfd地址作为参数传递给T1,但T0后续退出函数,栈内存失效,T1再访问就触发非法读操作 - 跨平台差异原因:macOS与Linux的栈内存管理策略不同,macOS可能未立即回收栈帧内存,或者ASan在macOS上的检测阈值更宽松,导致问题未被触发
修复建议
- 内存生命周期调整:将
newsockfd从栈变量改为堆分配(用malloc/new),传递指针给线程T1,待线程执行完毕后再释放内存,确保生命周期覆盖线程运行周期 - 线程同步保障:修改逻辑,确保T0在T1完成所有操作后再退出当前函数,保证栈变量在T1运行期间始终有效
- 参数传递检查:检查第61行的线程创建代码,确认传递给线程的参数是否为栈上临时变量,若存在此类情况,替换为全局变量或堆分配变量
内容的提问来源于stack exchange,提问作者student422
相关产品推荐
相关产品推荐

