Skip to content

AddressSanitizer

1、地址消毒

sanitizer:(食物加工设备所用的)消毒杀菌剂 AddressSanitizer:一种快速地址健全性检查器

https://docs.microsoft.com/en-us/cpp/sanitizers/asan?view=msvc-160

内存访问问题,如buffer越界访问,使用已经释放的内存等,一直都是诸如C,C++等编程语言所面对的大问题。目前有很多内存错误检测方法,但可能慢,也可能功能太有限,或者两者都有。 这篇论文论述地址消毒技术,一种新的内存错误检测技术。我们的工具检查对于堆,栈,全局变量的越界访问,以及释放后使用问题。它使用了一种特殊而简单的内存分配器以及代码实现,可以很方便的在任何编译器,二进制转换系统甚至硬件中实现。 地址消毒兼顾运行效率和功能的全面性,它平均仅降低系统73%的运行速度,并且它在错误发生的时刻(而不是之后)精确定位问题。它目前已经发现了chromium浏览器超过300个隐藏问题,以及其他软件的问题。

ASAN ,全称 AddressSanitizer,也即地址消毒技术。可以用来检测内存问题,例如缓冲区溢出或对悬空指针的非法访问等。根据谷歌的工程师介绍 ASAN 已经在 chromium 项目上检测出了300多个潜在的未知bug,而且在使用 ASAN 作为内存错误检测工具对程序性能损耗也是及其可观的。根据检测结果显示可能导致性能降低 2 倍左右,比 Valgrind (官方给的数据大概是降低 10-50 倍)快了一个数量级。而且相比于 Valgrind 只能检查到堆内存的越界访问和悬空指针的访问, ASAN 不仅可以检测到堆内存的越界和悬空指针的访问,还能检测到栈和全局对象的越界访问。这也是 ASAN 在众多内存检测工具的比较上出类拔萃的重要原因,基本上现在 C/C++ 项目都会使用 ASAN 来保证产品质量,尤其是大项目中更为需要。

AddressSanitizer (ASan) 是一种内存错误检测工具,用于检测 C/C++ 程序中的内存错误,例如使用已释放的内存、缓冲区溢出等。它是由 Google 开发的,现在已经被集成到许多编译器中,例如 Clang 和 GCC。

ASan 使用了一种内存隔离技术,它会在程序运行时动态地分配内存,并在内存块之间留下一些空间,这些空间被称为“防护带”。当程序访问这些防护带时,ASan 会检测到这个错误,并输出错误信息,包括错误的位置和类型。

ASan 可以检测到许多常见的内存错误,例如缓冲区溢出、使用已释放的内存、使用未初始化的内存等。它还可以检测到一些难以发现的内存错误,例如使用已经被修改的内存等。

使用 ASan 可以帮助开发人员及时发现和修复内存错误,从而提高程序的稳定性和安全性。

为了提高产品质量保证,正常在软件转测试和预发布前需要对软件的内存使用进行检查,防止有内存泄漏、内存溢出、双重释放等问题,一般通过valgrind、asan等工具来检查。由于valgrind资源消耗比较大,严重影响了软件的正常运行,故一般采用gcc自带的asan来对软件进行内存检测,asan是gcc 4.8版本开始支持,其配置也比较简单。

Sanitizers 是谷歌发起的开源工具集,其功能非常强大,包括了:

AddressSanitizer 检查内存地址相关问题,包括释放后使用、重复释放、堆溢出、栈溢出等等问题。 MemorySanitizer 检查使用未初始化内存问题。 ThreadSanitizer 检查线程数据竞争和死锁问题。 LeakSanitizer 检查内存泄漏问题。 内核Sanitizer包括KASAN和KMSAN

Sanitizers 项目本是 LLVM 项目的一部分,但 GNU 也将该系列工具加入到了自家的GCC编译器中。

GCC从4.8版本开始支持 Address和 Thread Sanitizer 4.9版本开始支持 Leak Sanitizer 和 UB Sanitizer。 gcc 7.2 : Asan中集成了LSan。

2、asan使用步骤

1、编译配置: LDFLAGS +="-fsanitize=address -fsanitize-recover=address,all -fno-omit-frame-pointer -fsanitize=leak"

静态库关联: LDFLAGS += “-static-libasan”

动态库关联: LDFLAGS += “-lasan”

-fsanitize=address:开启内存越界检测。

-fsanitize-recover=address:一般后台程序为保证稳定性,不能遇到错误就简单退出,而是继续运行,采用该选项支持内存出错之后程序继续运行,需要叠加设置ASAN_OPTIONS=halt_on_error=0才会生效;若未设置此选项,则内存出错即报错退出,但该配置在gcc 6.2版本才开始支持。

-fno-omit-frame-pointer:去使能栈溢出保护,即不忽略栈指针,栈的可读性更高

2、程序启动参数 ASAN_OPTIONS是Address-Sanitizier的运行选项环境变量。

halt_on_error=0:检测内存错误后继续运行

detect_leaks=1:使能内存泄露检测

malloc_context_size=15:内存错误发生时,显示的调用栈层数为15

log_path=/home/xos/asan.log:内存检查问题日志存放文件路径

suppressions=$SUPP_FILE:屏蔽打印某些内存错误

比如: export ASAN_OPTIONS=“halt_on_error=1:disable_coredump=0:unmap_shadow_on_exit=1:log_path=/home/test/asan:malloc_context_size=15”

注意事项: 不开-g编译选项,不能定位到具体的代码行号

[root@ubuntu0006:~] #/usr/bin/g++-5 k.cpp -Wl,-rpath,/usr/local/gcc-8.1.0/lib64/ -fsanitize=address -fsanitize=leak -g
[root@ubuntu0006:~] #./a.out
x = 0x60200000eff0
xx = 0x60200000efd0
xxx = 0x60400000dfd0
=================================================================
==32668==ERROR: AddressSanitizer: alloc-dealloc-mismatch (operator new [] vs operator delete) on 0x60b00000af90
    #0 0x7f80cc87bb2a in operator delete(void*) (/usr/lib/x86_64-linux-gnu/libasan.so.2+0x99b2a)
    #1 0x400c7a in main /root/k.cpp:15
    #2 0x7f80cc0b483f in __libc_start_main (/lib/x86_64-linux-gnu/libc.so.6+0x2083f)
    #3 0x400aa8 in _start (/root/a.out+0x400aa8)

0x60b00000af90 is located 0 bytes inside of 100-byte region [0x60b00000af90,0x60b00000aff4)
allocated by thread T0 here:
    #0 0x7f80cc87b6b2 in operator new[](unsigned long) (/usr/lib/x86_64-linux-gnu/libasan.so.2+0x996b2)
    #1 0x400c6a in main /root/k.cpp:14
    #2 0x7f80cc0b483f in __libc_start_main (/lib/x86_64-linux-gnu/libc.so.6+0x2083f)

SUMMARY: AddressSanitizer: alloc-dealloc-mismatch ??:0 operator delete(void*)
==32668==HINT: if you don't care about these warnings you may set ASAN_OPTIONS=alloc_dealloc_mismatch=0
==32668==ABORTING
[root@ubuntu0006:~] #/usr/bin/g++-5 k.cpp -Wl,-rpath,/usr/local/gcc-8.1.0/lib64/ -fsanitize=address -fsanitize=leak
[root@ubuntu0006:~] #./a.out
x = 0x60200000eff0
xx = 0x60200000efd0
xxx = 0x60400000dfd0
=================================================================
==594==ERROR: AddressSanitizer: alloc-dealloc-mismatch (operator new [] vs operator delete) on 0x60b00000af90
    #0 0x7f52f9a18b2a in operator delete(void*) (/usr/lib/x86_64-linux-gnu/libasan.so.2+0x99b2a)
    #1 0x400c7a in main (/root/a.out+0x400c7a)
    #2 0x7f52f925183f in __libc_start_main (/lib/x86_64-linux-gnu/libc.so.6+0x2083f)
    #3 0x400aa8 in _start (/root/a.out+0x400aa8)

0x60b00000af90 is located 0 bytes inside of 100-byte region [0x60b00000af90,0x60b00000aff4)
allocated by thread T0 here:
    #0 0x7f52f9a186b2 in operator new[](unsigned long) (/usr/lib/x86_64-linux-gnu/libasan.so.2+0x996b2)
    #1 0x400c6a in main (/root/a.out+0x400c6a)
    #2 0x7f52f925183f in __libc_start_main (/lib/x86_64-linux-gnu/libc.so.6+0x2083f)

SUMMARY: AddressSanitizer: alloc-dealloc-mismatch ??:0 operator delete(void*)
==594==HINT: if you don't care about these warnings you may set ASAN_OPTIONS=alloc_dealloc_mismatch=0
==594==ABORTING

开启-O编译优化选项,会导致asan无法定位:D:\Users\Administrator\Desktop\asan_O0123.cpp

[root@ubuntu0006:~] #/usr/bin/g++-5 -g -O0 l.cpp -Wl,-rpath,/lib/x86_64-linux-gnu/  -lasan
[root@ubuntu0006:~] #./a.out

=================================================================
==18269==ERROR: LeakSanitizer: detected memory leaks

Direct leak of 4096 byte(s) in 1 object(s) allocated from:
    #0 0x7fa8e5195602 in malloc (/usr/lib/x86_64-linux-gnu/libasan.so.2+0x98602)
    #1 0x4006ba in main /root/l.cpp:6
    #2 0x7fa8e4d5383f in __libc_start_main (/lib/x86_64-linux-gnu/libc.so.6+0x2083f)

SUMMARY: AddressSanitizer: 4096 byte(s) leaked in 1 allocation(s).
[root@ubuntu0006:~] #/usr/bin/g++-5 -g -O1 l.cpp -Wl,-rpath,/lib/x86_64-linux-gnu/  -lasan
[root@ubuntu0006:~] #./a.out

int *x = (int *)malloc(1024 * sizeof(int));
//printf("%p\n", x);    // 如果使用了x指针后则-O编译优化选项无法影响,但是未使用则会影响

3、ASAN_OPTIONS所有的配置项及其含义

ASAN_OPTIONS是用于配置ASAN的选项的环境变量,可以用来控制ASAN的行为。ASAN_OPTIONS的值是一个用冒号分隔的字符串,每个部分都是一个配置项。下面列出了ASAN_OPTIONS所有的配置项及其含义:

halt_on_error=1 :当ASAN检测到内存错误时,立即终止程序。 allow_user_segv_handler=1 :允许用户自定义SIGSEGV信号的处理程序。 allocator_may_return_null=1 :分配内存失败时,返回NULL而不是崩溃。 alloc_dealloc_mismatch=0 :禁用分配和释放不匹配的检测。 alloc_dealloc_mismatch=1 :开启分配和释放不匹配的检测,但是不会导致程序终止。 alloc_dealloc_mismatch=2 :开启分配和释放不匹配的检测,同时会导致程序终止。 check_initialization_order=1 :检测全局变量的初始化顺序。 check_malloc_usable_size=0 :禁用malloc_usable_size函数的检测。 detect_container_overflow=1 :检测容器类的溢出,例如vector、map等。 detect_leaks=1 :检测内存泄漏,ASAN会在程序结束时输出内存泄漏的信息。 detect_odr_violation=1 :检测ODR(One Definition Rule)违规,例如同一符号在不同编译单元中的定义不一致等。 detect_stack_use_after_return=1 :检测函数返回后使用栈上的变量。 detect_invalid_pointer_pairs=2 :检测指针对的无效组合,例如使用已经释放的指针等。 detect_invalid_pointer_derference=1 :检测无效的指针解引用。 handle_abort=1 :当程序调用abort函数时,ASAN会输出错误信息并终止程序。 handle_sigbus=1 :当程序收到SIGBUS信号时,ASAN会输出错误信息并终止程序。 handle_sigfpe=1 :当程序收到SIGFPE信号时,ASAN会输出错误信息并终止程序。 handle_sigill=1 :当程序收到SIGILL信号时,ASAN会输出错误信息并终止程序。 handle_sigsegv=1 :当程序收到SIGSEGV信号时,ASAN会输出错误信息并终止程序。 handle_sigtrap=1 :当程序收到SIGTRAP信号时,ASAN会输出错误信息并终止程序。 max_redzone=2048 :设置红区的最大大小。 new_delete_type_mismatch=0 :禁用new和delete类型不匹配的检测。 new_delete_type_mismatch=1 :开启new和delete类型不匹配的检测,但是不会导致程序终止。 new_delete_type_mismatch=2 :开启new和delete类型不匹配的检测,同时会导致程序终止。 print_stats=1 :打印内存使用统计信息。 quarantine_size_mb=256 :设置quarantine(隔离区)的大小。 redzone=16 :设置红区的大小。 replace_intrin=1 :替换内置函数,例如memcpy、memset等,以便检测内存错误。 replace_str=1 :替换字符串相关函数,例如strcpy、strcat等,以便检测内存错误。 strict_memcmp=1 :开启memcmp函数的严格模式,以便检测内存错误。 symbolize=1 :打印可读的堆栈信息,需要配合LLVM的llvm-symbolizer工具使用。 verbosity=1 :设置ASAN的输出级别,值越大输出的信息越详细。

以上是ASAN_OPTIONS的所有配置项及其含义,可以根据需要进行配置。

ASAN_OPTIONS的使用:

export ASAN_OPTIONS="halt_on_error=1:disable_coredump=0:unmap_shadow_on_exit=1:log_path=/home/test/asan:malloc_context_size=15"
对单一的二进制文件立即生效,会生成/home/test/asan.10418等文件。

4、举例使用

demo见:D:\Github\Storage\c++\内存泄露工具\address_sanitizer infect vt.传染;使携带病菌;使感染(计算机病毒);使感染(某种感情)

[root@ubuntu0006:/media/hankin/vdb/perl] #vim address_deinfect.cpp
[root@ubuntu0006:/media/hankin/vdb/perl] #g++ address_deinfect.cpp
[root@ubuntu0006:/media/hankin/vdb/perl] #./a.out
num[5] = 100
[root@ubuntu0006:/media/hankin/vdb/perl] #cat address_deinfect.cpp
#include <iostream>
#include <stdio.h>
using namespace std;

int main()
{
    // 被测代码:预埋一个栈溢出bug
    int num[5] = {0};
    num[5] = 100;
    printf("num[5] = %d\n", num[5]);
    return 0;
}

上述代码居然运行没有一点毛病。。。。。。

这时候如果加入地址消毒编译选项:

[root@ubuntu0006:/media/hankin/vdb/perl] #g++ address_deinfect.cpp -fsanitize=address
[root@ubuntu0006:/media/hankin/vdb/perl] #./a.out
=================================================================
==848==ERROR: AddressSanitizer: stack-buffer-overflow on address 0x7fffb2f0aa44 at pc 0x000000400bdb bp 0x7fffb2f0aa00 sp 0x7fffb2f0a9f0
WRITE of size 4 at 0x7fffb2f0aa44 thread T0
    #0 0x400bda in main (/media/hankin/vdb/perl/a.out+0x400bda)
    #1 0x7f2bc141c83f in __libc_start_main (/lib/x86_64-linux-gnu/libc.so.6+0x2083f)
    #2 0x400a38 in _start (/media/hankin/vdb/perl/a.out+0x400a38)

Address 0x7fffb2f0aa44 is located in stack of thread T0 at offset 52 in frame
    #0 0x400b15 in main (/media/hankin/vdb/perl/a.out+0x400b15)

  This frame has 1 object(s):
    [32, 52) 'num' <== Memory access at offset 52 overflows this variable
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: stack-buffer-overflow ??:0 main
Shadow bytes around the buggy address:
  0x1000765d94f0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
  0x1000765d9500: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
  0x1000765d9510: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
  0x1000765d9520: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
  0x1000765d9530: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
=>0x1000765d9540: 00 00 f1 f1 f1 f1 00 00[04]f4 f3 f3 f3 f3 00 00
  0x1000765d9550: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
  0x1000765d9560: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
  0x1000765d9570: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
  0x1000765d9580: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
  0x1000765d9590: 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
  Heap right redzone:      fb
  Freed heap region:       fd
  Stack left redzone:      f1
  Stack mid redzone:       f2
  Stack right redzone:     f3
  Stack partial redzone:   f4
  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
==848==ABORTING

-fno-omit-frame-pointer : 保留栈指针,打印有用的堆栈信息,方便asan定位问题时追踪 -fsanitize-recover=address : 在触发asan时,继续运行程序,需要配合环境变量ASAN_OPTIONS=halt_on_error=0:report_path=xxx使用 -O0 : 不适用编译期优化,避免优化asan效果 -Wall : 显示所有告警信息

https://www.coder.work/article/3236742

5、无法检测文件描述符是否关闭

代码见:D:\Github\Storage\c++\standard_library\file\已打开文件可删除\fopen_example.c D:\Github\Storage\c++\standard_library\file\已打开文件可删除\open_example.c

根据简介可知,那是否可以检测内存是否被释放呢? 答案是可检测,代码见:D:\Github\Storage\c++\内存泄露工具\address_sanitizer\malloc_no_free.cpp

AddressSanitizer (ASan) 是一种内存错误检测工具,它主要用于检测内存相关的错误,例如缓冲区溢出、使用已释放的内存等。ASan 并不是一个完整的静态分析工具,它无法检测所有类型的错误,包括文件描述符未关闭的问题。

文件描述符未关闭的问题是一种资源泄漏问题,它会导致程序占用过多的系统资源,最终可能导致程序崩溃或者系统崩溃。ASan 并不会直接检测文件描述符未关闭的问题,因为这个问题不是一个内存相关的错误。

不过,ASan 可以通过检测内存泄漏来间接地发现文件描述符未关闭的问题。当程序使用 malloc 或 new 等函数分配内存时,ASan 会跟踪这些内存块,并在程序结束时检查是否有未释放的内存块。如果有未释放的内存块,ASan 就会报告内存泄漏的错误,这可能是由于文件描述符未关闭导致的。

因此,虽然 ASan 不能直接检测文件描述符未关闭的问题,但它可以通过检测内存泄漏来间接地发现这个问题。开发人员可以结合其他工具和技术,例如静态分析工具和代码审查,来更全面地检测和修复文件描述符未关闭的问题。

6、可以使用什么工具能检测文件描述符未关闭

可以使用一些静态分析工具来检测文件描述符未关闭的问题,例如:

Coverity:Coverity 是一种商业静态分析工具,它可以检测 C/C++ 代码中的各种错误,包括文件描述符未关闭的问题。

Clang Static Analyzer:Clang Static Analyzer 是一个开源的静态分析工具,它可以检测 C/C++ 代码中的各种错误,包括文件描述符未关闭的问题。

PVS-Studio:PVS-Studio 是一种商业静态分析工具,它可以检测 C/C++ 代码中的各种错误,包括文件描述符未关闭的问题。

这些工具都可以在编译时对代码进行静态分析,检测出潜在的文件描述符未关闭的问题,并给出相应的警告或错误信息。开发人员可以根据这些信息及时修复代码中的问题,从而提高程序的稳定性和安全性。

测试发现clang这个工具并不能,也只能检测出内存泄露,代码见:D:\Github\Storage\c++\内存泄露工具\address_sanitizer\clang_example.c 还得是cppcheck工具好用,小巧才1.4MB左右,并且执行简单。代码见:D:\Github\Storage\c++\内存泄露工具\address_sanitizer\cppcheck_example.c

结论:暂时没有工具可以检测open函数打开的文件描述符未关闭问题。 动态和静态都不行。 自己猜测的原因:首先它仅仅是一个数字,在程序关闭后会自动回收。如果在程序中打开同一个文件多次,文件描述符数字会自动增加,也应该没有办法判断后面会不会关闭。总的来说,个人感觉静态工具应该能检测出来的,但是搜索了大量资料也没有一个明确的工具可以对齐进行检测。 https://blog.csdn.net/weixin_38331755/article/details/124545159

7、对可执行文件进行asan检测时,只需要其中依赖的任何一个库启用了asan即可。(不用所有的库和可执行文件都编译时启用asan)

例如A exec依赖 a、b、c库。其中b依赖c库。 只要其中任何一个启用了asan即可实现,检测所有的库和可执行文件中的内存问题。

判断一个文件是否启用asan检测:ldd xxx | grep asan

8、asan报告分析

D:\Users\Administrator\Desktop\testmemleak.cpp

问题原因:
char *y = new char[100];
delete y;

列出出错的地方:delete y;
==26613==ERROR: AddressSanitizer: alloc-dealloc-mismatch (operator new [] vs operator delete) on 0x60b000003c10

列出内存分配的地方:char *y = new char[100];
0x60b000003c10 is located 0 bytes inside of 100-byte region [0x60b000003c10,0x60b000003c74)
allocated by thread T0 (event_base) here:

总结:
SUMMARY: AddressSanitizer: alloc-dealloc-mismatch (/lib/x86_64-linux-gnu/libasan.so.5+0xec128) in operator delete(void*, unsigned long)
==26613==HINT: if you don't care about these errors you may set ASAN_OPTIONS=alloc_dealloc_mismatch=0

错误之间以这个隔开:
===========

同一个地方出现两种类型错误:
SUMMARY: AddressSanitizer: new-delete-type-mismatch (/lib/x86_64-linux-gnu/libasan.so.5+0xec128) in operator delete(void*, unsigned long)
==26613==HINT: if you don't care about these errors you may set ASAN_OPTIONS=new_delete_type_mismatch=0

8-1、gcc5和gcc8版本差异带来不同的结果

gcc5版本AddressSanitizer在每次运行时只报告一个内存错误。这是因为在检测到一个错误后,程序的状态可能已经被破坏,导致后续的内存操作变得不可预测。因此,ASan 可能无法继续检测到其他错误。 但是gcc8版本可以一次性出所有内存地址错误。 gcc5版本内存泄露错误是可以一次性出,前提是不存在内存地址错误。

仔细发现会有AddressSanitizer和LeakSanitizer两种不同的检测,AddressSanitizer是致命错误,地址访问错误。

gcc5和gcc8链接的libasan.so版本不同,

高版本新增参数,如-fsanitize-recover=address:

[root@ubuntu0006:~] #/usr/bin/g++-5 k.cpp -fsanitize=address -fsanitize-recover=address,all -fno-omit-frame-pointer -fsanitize=leak
cc1plus: error: -fsanitize-recover=address is not supported
[root@ubuntu0006:~] #/usr/bin/g++ k.cpp -fsanitize=address -fsanitize-recover=address,all -fno-omit-frame-pointer -fsanitize=leak
[root@ubuntu0006:~] #./a.out
./a.out: error while loading shared libraries: libasan.so.5: cannot open shared object file: No such file or directory

8-2、不同的gcc版本默认加载不同版本的libasan.so文件

当前测试环境默认安装的g++是g++-5,后面将新版本的g++-8已编译的安装移到当前环境,即未在当前环境编译安装,只做解压操作后使用,因此g++-8需要找libasan.so.5版本,而当前环境默认路径则是/usr/lib/x86_64-linux-gnu/libasan.so.2,因此无法正常使用,但是通过-L指定路径编译后却变成了libasan.so.2???

[root@ubuntu0006:~] #g++ k.cpp  -lasan
[root@ubuntu0006:~] #ldd a.out
        linux-vdso.so.1 =>  (0x00007ffdd82c6000)
        libasan.so.5 => not found
        libstdc++.so.6 => /usr/lib/x86_64-linux-gnu/libstdc++.so.6 (0x00007f12d33fb000)
        libm.so.6 => /lib/x86_64-linux-gnu/libm.so.6 (0x00007f12d30f2000)
        libgcc_s.so.1 => /lib/x86_64-linux-gnu/libgcc_s.so.1 (0x00007f12d2edc000)
        libc.so.6 => /lib/x86_64-linux-gnu/libc.so.6 (0x00007f12d2b12000)
        /lib64/ld-linux-x86-64.so.2 (0x00007f12d377f000)
[root@ubuntu0006:~] #./a.out
./a.out: error while loading shared libraries: libasan.so.5: cannot open shared object file: No such file or directory
[root@ubuntu0006:~] #g++ k.cpp -L/usr/lib/gcc/x86_64-linux-gnu/5/ -lasan
[root@ubuntu0006:~] #ldd a.out
        linux-vdso.so.1 =>  (0x00007ffc5fdda000)
        libasan.so.2 => /usr/lib/x86_64-linux-gnu/libasan.so.2 (0x00007f4436650000)
        libstdc++.so.6 => /usr/lib/x86_64-linux-gnu/libstdc++.so.6 (0x00007f44362cc000)
        libm.so.6 => /lib/x86_64-linux-gnu/libm.so.6 (0x00007f4435fc3000)
        libgcc_s.so.1 => /lib/x86_64-linux-gnu/libgcc_s.so.1 (0x00007f4435dad000)
        libc.so.6 => /lib/x86_64-linux-gnu/libc.so.6 (0x00007f44359e3000)
        libpthread.so.0 => /lib/x86_64-linux-gnu/libpthread.so.0 (0x00007f44357c6000)
        libdl.so.2 => /lib/x86_64-linux-gnu/libdl.so.2 (0x00007f44355c2000)
        /lib64/ld-linux-x86-64.so.2 (0x00007f44375bd000)
[root@ubuntu0006:~] #./a.out
on_device_info_change is null
priv is null

-static-libasan:静态加载

动态加载:
[root@ubuntu0006:~] #g++ k.cpp -g -O0 -fsanitize=address -lasan -std=c++11 -I /usr/include/ -pthread -lm
[root@ubuntu0006:~] #./a.out
./a.out: error while loading shared libraries: libasan.so.5: cannot open shared object file: No such file or directory
[root@ubuntu0006:~] #!ldd
ldd a.out
        linux-vdso.so.1 =>  (0x00007fff2b9ee000)
        libasan.so.5 => not found
        libstdc++.so.6 => /usr/lib/x86_64-linux-gnu/libstdc++.so.6 (0x00007fc0e5112000)
        libm.so.6 => /lib/x86_64-linux-gnu/libm.so.6 (0x00007fc0e4e09000)
        libgcc_s.so.1 => /lib/x86_64-linux-gnu/libgcc_s.so.1 (0x00007fc0e4bf3000)
        libpthread.so.0 => /lib/x86_64-linux-gnu/libpthread.so.0 (0x00007fc0e49d6000)
        libc.so.6 => /lib/x86_64-linux-gnu/libc.so.6 (0x00007fc0e460c000)
        /lib64/ld-linux-x86-64.so.2 (0x00007fc0e5496000)

静态加载asan,但是ldd命令则无法看到asan加载(ldd只能看见动态加载):
[root@ubuntu0006:~] #g++ k.cpp -g -O0 -fsanitize=address -static-libasan -std=c++11 -I /usr/include/ -pthread -lm
[root@ubuntu0006:~] #./a.out
hejian 0x602000000010
=================================================================
==19780==ERROR: AddressSanitizer: alloc-dealloc-mismatch (operator new [] vs operator delete) on 0x60b000000040
    #0 0x4cf990 in operator delete(void*) ../../.././libsanitizer/asan/asan_new_delete.cc:135
    #1 0x508d78 in main /root/k.cpp:19
    #2 0x7fd9a20c783f in __libc_start_main (/lib/x86_64-linux-gnu/libc.so.6+0x2083f)
    #3 0x405e68 in _start (/root/a.out+0x405e68)

0x60b000000040 is located 0 bytes inside of 100-byte region [0x60b000000040,0x60b0000000a4)
allocated by thread T0 here:
    #0 0x4cede0 in operator new[](unsigned long) ../../.././libsanitizer/asan/asan_new_delete.cc:93
    #1 0x508d68 in main /root/k.cpp:18
    #2 0x7fd9a20c783f in __libc_start_main (/lib/x86_64-linux-gnu/libc.so.6+0x2083f)

SUMMARY: AddressSanitizer: alloc-dealloc-mismatch ../../.././libsanitizer/asan/asan_new_delete.cc:135 in operator delete(void*)
==19780==HINT: if you don't care about these errors you may set ASAN_OPTIONS=alloc_dealloc_mismatch=0
==19780==ABORTING
[root@ubuntu0006:~] #ldd a.out
        linux-vdso.so.1 =>  (0x00007ffd957ec000)
        libstdc++.so.6 => /usr/lib/x86_64-linux-gnu/libstdc++.so.6 (0x00007fd7307be000)
        libm.so.6 => /lib/x86_64-linux-gnu/libm.so.6 (0x00007fd7304b5000)
        librt.so.1 => /lib/x86_64-linux-gnu/librt.so.1 (0x00007fd7302ad000)
        libdl.so.2 => /lib/x86_64-linux-gnu/libdl.so.2 (0x00007fd7300a9000)
        libpthread.so.0 => /lib/x86_64-linux-gnu/libpthread.so.0 (0x00007fd72fe8c000)
        libgcc_s.so.1 => /lib/x86_64-linux-gnu/libgcc_s.so.1 (0x00007fd72fc76000)
        libc.so.6 => /lib/x86_64-linux-gnu/libc.so.6 (0x00007fd72f8ac000)
        /lib64/ld-linux-x86-64.so.2 (0x00007fd730b42000)

这样写未加载asan:
[root@ubuntu0006:~] #g++ k.cpp -g -O0 -static-libasan -std=c++11 -I /usr/include/ -pthread -lm
[root@ubuntu0006:~] #./a.out
hejian 0x7a7c20

未静态加载无法正常运行,说明静态加载则是找当前系统的默认路径:
[root@ubuntu0006:~] #g++ k.cpp -fsanitize=address
[root@ubuntu0006:~] #./a.out
./a.out: error while loading shared libraries: libasan.so.5: cannot open shared object file: No such file or directory

使用系统的默认g++-5:
[root@ubuntu0006:~] #/usr/bin/g++-5 k.cpp -fsanitize=address
[root@ubuntu0006:~] #./a.out
hejian 0x60200000eff0
0x60200000efd0
0x60400000dfd0
=================================================================
==433==ERROR: AddressSanitizer: alloc-dealloc-mismatch (operator new [] vs operator delete) on 0x60b00000af90
    #0 0x7f32169e6b2a in operator delete(void*) (/usr/lib/x86_64-linux-gnu/libasan.so.2+0x99b2a)
    #1 0x400c5a in main (/root/a.out+0x400c5a)
    #2 0x7f321621f83f in __libc_start_main (/lib/x86_64-linux-gnu/libc.so.6+0x2083f)
    #3 0x400a88 in _start (/root/a.out+0x400a88)

0x60b00000af90 is located 0 bytes inside of 100-byte region [0x60b00000af90,0x60b00000aff4)
allocated by thread T0 here:
    #0 0x7f32169e66b2 in operator new[](unsigned long) (/usr/lib/x86_64-linux-gnu/libasan.so.2+0x996b2)
    #1 0x400c4a in main (/root/a.out+0x400c4a)
    #2 0x7f321621f83f in __libc_start_main (/lib/x86_64-linux-gnu/libc.so.6+0x2083f)

SUMMARY: AddressSanitizer: alloc-dealloc-mismatch ??:0 operator delete(void*)
==433==HINT: if you don't care about these warnings you may set ASAN_OPTIONS=alloc_dealloc_mismatch=0
==433==ABORTING

结果同上:
[root@ubuntu0006:~] #/usr/bin/g++-5 k.cpp -lasan

8-3、低版本gcc强制指定高版本libasan.so

[root@ubuntu0006:~] #/usr/bin/g++-5 k.cpp -L/usr/local/gcc-8.1.0/lib64/ -Wl,-rpath,/usr/local/gcc-8.1.0/lib64/ -fsanitize=address  -fno-omit-frame-pointer -fsanitize=leak -g
/usr/lib/gcc/x86_64-linux-gnu/5/libasan_preinit.o:(.preinit_array+0x0):对‘__asan_init_v4’未定义的引用
/tmp/ccITnHqD.o:在函数‘_GLOBAL__sub_I_00099_0_main’中:
/root/k.cpp:32:对‘__asan_init_v4’未定义的引用
collect2: error: ld returned 1 exit status

9、-fsanitize=address和-lasan区别

问题背景:发现使用-lasan编译的session程序在项目中启动无反应,直接运行报错manually preload it with LD_PRELOAD,但是另外一种虚拟机资源则是正常的,很奇怪,都是启动同一个session程序,为何有这两种截然不同的现象???

原因居然是两种虚拟机资源在启动时加载了不同的环境变量:

setenv("ASAN_OPTIONS", "halt_on_error=0:detect_leaks=1:log_path=/opt/sangfor/vdiclient/log/asan_tray.log", 1 /*overwrite*/);
setenv("LD_PRELOAD", "/lib/x86_64-linux-gnu/libasan.so.5", 1 /*overwrite*/);
execlp(EXE_NAME, EXE_NAME, "-i", info->mSessId.data(), "-s", std::to_string(info->mSessSeq).data(), NULL););

另外一个虚拟机资源启动时未添加setenv("ASAN_OPTIONS".....);,已省略setenv("LD_PRELOAD".....);无关,并且在Terminal中执行export ASAN_OPTIONS="";或者在/etc/profile也是无效的。

这个原因是:ASAN_OPTIONS或者LD_PRELOAD添加后,会让sessiong程序优先加载asan库,从而不会存在manually preload it with LD_PRELOAD问题。但是由于session程序其设置了setuid权限位。linux下对设置了setuid权限的可执行文件,忽略配置的LD_PRELOAD。因此对于session程序来说LD_PRELOAD配置无效。

通过发现使用-fsanitize=address代替-lasan参数后,即使execlp启动程序时没有加载ASAN_OPTIONS或者LD_PRELOAD选项,也能优先加载asan库,这就是编译器会自动处理所需的库链接。 因此建议在使用的时候-fsanitize=address -lasan结合使用最好。

在大多数情况下,只需使用 -fsanitize=address 即可,编译器会自动处理所需的库链接。 -fsanitize=address 是编译器选项,用于启用 AddressSanitizer 功能,插入检测内存错误的代码。 -lasan 是链接器选项,用于链接 AddressSanitizer 的库,通常在使用 -fsanitize=address 时不需要显式指定。

在使用 AddressSanitizer (ASan) 时,出现错误 ASan runtime does not come first in initial library list; you should either link runtime to your application or manually preload it with LD_PRELOAD 通常是因为 ASan 的运行时库没有在链接时被正确放置在库列表的最前面。 使用 -fsanitize=address 选项来编译和链接你的程序,而不是手动使用 -lasan。-fsanitize=address 会自动处理 ASan 运行时库的链接顺序。

如果你确实需要手动链接 ASan 库(例如,使用 -lasan),你可以在运行程序时使用 LD_PRELOAD 来确保 ASan 运行时库在其他库之前加载。 LD_PRELOAD=/path/to/libasan.so ./your_program

在某些情况下,链接器可能会忽略未直接引用的库。你可以尝试使用 -Wl,--no-as-needed 选项来强制链接器链接所有库: g++ -fsanitize=address -Wl,--no-as-needed your_file.cpp -o your_program

10、在编译时,使用 -Wl,-rpath 选项来指定运行时库的搜索路径

不需要使用export LD_LIBRARY_PATH=/usr/local/gcc-8.1.0/lib64/:$LD_LIBRARY_PATH

[root@ubuntu0006:~] #/usr/bin/g++ -g -O0 -lasan -lpthread l.cpp -lusb-1.0 -Wl,-rpath,/usr/local/gcc-8.1.0/lib64/
[root@ubuntu0006:~] #ldd a.out
        linux-vdso.so.1 =>  (0x00007fff18238000)
        libasan.so.5 => /usr/local/gcc-8.1.0/lib64/libasan.so.5 (0x00007f03cd05c000)
        libpthread.so.0 => /lib/x86_64-linux-gnu/libpthread.so.0 (0x00007f03cce3f000)
        libusb-1.0.so.0 => /usr/local/lib/libusb-1.0.so.0 (0x00007f03ccc23000)
        libstdc++.so.6 => /usr/local/gcc-8.1.0/lib64/libstdc++.so.6 (0x00007f03cc89f000)
        libm.so.6 => /lib/x86_64-linux-gnu/libm.so.6 (0x00007f03cc596000)
        libgcc_s.so.1 => /usr/local/gcc-8.1.0/lib64/libgcc_s.so.1 (0x00007f03cc37e000)
        libc.so.6 => /lib/x86_64-linux-gnu/libc.so.6 (0x00007f03cbfb4000)
        libdl.so.2 => /lib/x86_64-linux-gnu/libdl.so.2 (0x00007f03cbdb0000)
        librt.so.1 => /lib/x86_64-linux-gnu/librt.so.1 (0x00007f03cbba8000)
        /lib64/ld-linux-x86-64.so.2 (0x00007f03ce021000)
        libudev.so.1 => /lib/x86_64-linux-gnu/libudev.so.1 (0x00007f03ce208000)
[root@ubuntu0006:~] #/usr/bin/g++ -g -O0 -lasan -lpthread l.cpp -lusb-1.0
[root@ubuntu0006:~] #ldd a.out
        linux-vdso.so.1 =>  (0x00007ffc09db0000)
        libasan.so.5 => not found
        libpthread.so.0 => /lib/x86_64-linux-gnu/libpthread.so.0 (0x00007fc442799000)
        libusb-1.0.so.0 => /usr/local/lib/libusb-1.0.so.0 (0x00007fc44257d000)
        libstdc++.so.6 => /usr/lib/x86_64-linux-gnu/libstdc++.so.6 (0x00007fc4421f9000)
        libm.so.6 => /lib/x86_64-linux-gnu/libm.so.6 (0x00007fc441ef0000)
        libgcc_s.so.1 => /lib/x86_64-linux-gnu/libgcc_s.so.1 (0x00007fc441cda000)
        libc.so.6 => /lib/x86_64-linux-gnu/libc.so.6 (0x00007fc441910000)
        /lib64/ld-linux-x86-64.so.2 (0x00007fc4429b6000)
        libudev.so.1 => /lib/x86_64-linux-gnu/libudev.so.1 (0x00007fc442b9e000)
        librt.so.1 => /lib/x86_64-linux-gnu/librt.so.1 (0x00007fc441708000)

11、manually preload it with LD_PRELOAD.

完整的错误信息:==23929==ASan runtime does not come first in initial library list; you should either link runtime to your application or manually preload it with LD_PRELOAD.

g++-8版本编译,由于-Wl,-rpath,/usr/local/gcc-8.1.0/lib64/是程序运行时加载,因此都会最先加载:
[root@ubuntu0006:~] #/usr/bin/g++ -g -O0 -lpthread l.cpp -lasan -lusb-1.0 -Wl,-rpath,/usr/local/gcc-8.1.0/lib64/
[root@ubuntu0006:~] #./a.out
==12272==ASan runtime does not come first in initial library list; you should either link runtime to your application or manually preload it with LD_PRELOAD.

虽然程序中没有加载-lpthread,但是存在-Wl,-rpath,选项时则会生效(nonono,说明这个参数使用-lpthread,因此往前加-lm就没啥问题):
[root@ubuntu0006:~] #/usr/bin/g++ -g -O0 -lm -lasan -lpthread l.cpp -lusb-1.0 -Wl,-rpath,/usr/local/gcc-8.1.0/lib64/
[root@ubuntu0006:~] #./a.out

=================================================================
==20594==ERROR: LeakSanitizer: detected memory leaks

Direct leak of 4096 byte(s) in 1 object(s) allocated from:
    #0 0x7f47fc0a37a0 in __interceptor_malloc ../../.././libsanitizer/asan/asan_malloc_linux.cc:86
    #1 0x40084b in main /root/l.cpp:17
    #2 0x7f47faf3383f in __libc_start_main (/lib/x86_64-linux-gnu/libc.so.6+0x2083f)

Direct leak of 4 byte(s) in 1 object(s) allocated from:
    #0 0x7f47fc0a51d0 in operator new(unsigned long) ../../.././libsanitizer/asan/asan_new_delete.cc:90
    #1 0x400859 in main /root/l.cpp:19
    #2 0x7f47faf3383f in __libc_start_main (/lib/x86_64-linux-gnu/libc.so.6+0x2083f)

SUMMARY: AddressSanitizer: 4100 byte(s) leaked in 2 allocation(s).
[root@ubuntu0006:~] #/usr/bin/g++ -g -O0 -lm -lpthread -lasan l.cpp -lusb-1.0 -Wl,-rpath,/usr/local/gcc-8.1.0/lib64/
[root@ubuntu0006:~] #./a.out
==5781==ASan runtime does not come first in initial library list; you should either link runtime to your application or manually preload it with LD_PRELOAD.

g++-5忽略-lpthrea和/usr/local/gcc-8.1.0/lib64/:
[root@ubuntu0006:~] #/usr/bin/g++-5 -g -O0 -lpthread l.cpp -lasan -Wl,-rpath,/usr/local/gcc-8.1.0/lib64/
[root@ubuntu0006:~] #ldd a.out
        linux-vdso.so.1 =>  (0x00007ffc7fde9000)
        libasan.so.2 => /usr/lib/x86_64-linux-gnu/libasan.so.2 (0x00007fe1aee47000)
        libstdc++.so.6 => /usr/local/gcc-8.1.0/lib64/libstdc++.so.6 (0x00007fe1aeac3000)
        libc.so.6 => /lib/x86_64-linux-gnu/libc.so.6 (0x00007fe1ae6f9000)
        libpthread.so.0 => /lib/x86_64-linux-gnu/libpthread.so.0 (0x00007fe1ae4dc000)
        libdl.so.2 => /lib/x86_64-linux-gnu/libdl.so.2 (0x00007fe1ae2d8000)
        libm.so.6 => /lib/x86_64-linux-gnu/libm.so.6 (0x00007fe1adfcf000)
        libgcc_s.so.1 => /usr/local/gcc-8.1.0/lib64/libgcc_s.so.1 (0x00007fe1addb7000)
        /lib64/ld-linux-x86-64.so.2 (0x00007fe1afdb4000)
[root@ubuntu0006:~] #./a.out

=================================================================
==13507==ERROR: LeakSanitizer: detected memory leaks

Direct leak of 4096 byte(s) in 1 object(s) allocated from:
    #0 0x7fe450b62602 in malloc (/usr/lib/x86_64-linux-gnu/libasan.so.2+0x98602)
    #1 0x40084a in main /root/l.cpp:7
    #2 0x7fe45039c83f in __libc_start_main (/lib/x86_64-linux-gnu/libc.so.6+0x2083f)

Direct leak of 4 byte(s) in 1 object(s) allocated from:
    #0 0x7fe450b63532 in operator new(unsigned long) (/usr/lib/x86_64-linux-gnu/libasan.so.2+0x99532)
    #1 0x400858 in main /root/l.cpp:9
    #2 0x7fe45039c83f in __libc_start_main (/lib/x86_64-linux-gnu/libc.so.6+0x2083f)

SUMMARY: AddressSanitizer: 4100 byte(s) leaked in 2 allocation(s).

g++-5忽略-lpthrea和Wl,-rpath,/lib/x86_64-linux-gnu/:
[root@ubuntu0006:~] #/usr/bin/g++-5 -g -O0 -lpthread l.cpp -lasan -Wl,-rpath,/lib/x86_64-linux-gnu/
[root@ubuntu0006:~] #./a.out

=================================================================
==14685==ERROR: LeakSanitizer: detected memory leaks

Direct leak of 4096 byte(s) in 1 object(s) allocated from:
    #0 0x7f3995db0602 in malloc (/usr/lib/x86_64-linux-gnu/libasan.so.2+0x98602)
    #1 0x40083a in main /root/l.cpp:7
    #2 0x7f39955ea83f in __libc_start_main (/lib/x86_64-linux-gnu/libc.so.6+0x2083f)

Direct leak of 4 byte(s) in 1 object(s) allocated from:
    #0 0x7f3995db1532 in operator new(unsigned long) (/usr/lib/x86_64-linux-gnu/libasan.so.2+0x99532)
    #1 0x400848 in main /root/l.cpp:9
    #2 0x7f39955ea83f in __libc_start_main (/lib/x86_64-linux-gnu/libc.so.6+0x2083f)

SUMMARY: AddressSanitizer: 4100 byte(s) leaked in 2 allocation(s).

结果同上:
[root@ubuntu0006:~] #/usr/bin/g++-5 -g -O0 l.cpp -lm -lpthread -lasan -lusb-1.0 -Wl,-rpath,/lib/x86_64-linux-gnu/

程序中需要加载-lusb-1.0,因此-lasan靠后了报错:
[root@ubuntu0006:~] #/usr/bin/g++-5 -g -O0 -lm l.cpp -lusb-1.0 -lasan
[root@ubuntu0006:~] #./a.out
==23929==ASan runtime does not come first in initial library list; you should either link runtime to your application or manually preload it with LD_PRELOAD.

总之-lasan往最前面写准没错!

This website is released under the MIT License.