• AddressSanitizer(ASan)


    ASAN功能介绍

    AddressSanitizer(ASan)工具是用于用户空间程序的内存错误检测工具,无法直接用于内核空间。

    ASAN早先是LLVM中的特性,后被加入gcc4.8,成为 gcc 的一部分,但不支持符号信息,无法显示出问题的函数和行数。从 4.9 开始,gcc 支持 AddressSanitizer 的所有功能。因此gcc 4.8以上版本使用ASAN时不需要安装第三方库,通过在编译时指定编译CFLAGS即可打开开关。

    ASan利用编译器插桩和运行时库来检测内存错误,例如缓冲区溢出、使用未初始化的内存等。但是,内核和用户空间程序的执行环境和内存管理方式不同,因此ASan无法直接应用于内核空间。

    对于内核空间的内存错误检测,可以考虑使用其他专门针对内核开发的工具,如KASAN(Kernel AddressSanitizer)。KASAN是Linux内核中的一个内存错误检测工具,可以用于检测内核空间的内存问题。后续我们介绍学习。

    优点:

    1. 检测能力强大:ASan可以检测到许多内存相关的错误,如缓冲区溢出、使用未初始化的内存、使用已释放的内存等。它通过在运行时对程序进行插桩来捕获并报告这些错误,帮助开发人员及早发现和修复问题。
    2. 低侵入性:ASan工具通过编译器插桩和运行时库来实现内存错误检测,对代码的更改较少,对现有代码的侵入性较小。这使得它相对容易集成到现有项目中,而无需进行大规模的代码修改。
    3. 容易定位问题:当ASan检测到内存错误时,它会提供详细的错误报告,包括错误类型、触发位置和调用栈信息。这有助于开发人员快速定位和修复问题,减少调试时间。
    4. 平台支持广泛:ASan工具在多个平台和编译器中都有支持,包括Linux、macOS和Windows等。这使得开发人员可以在不同的环境中使用相同的工具,提高了可移植性和开发效率。

    缺点:

    1. 内存开销较大:ASan会在运行时为每个分配的内存块添加额外的元数据,并在每次内存访问时进行检查。这会导致内存使用量增加,有时可能会显著影响程序的性能和内存占用。
    2. 编译时间延长:由于ASan需要对程序进行插桩,编译时间可能会延长。特别是对于大型项目或包含大量代码的程序,这可能会导致较长的编译时间。
    3. 不支持所有错误类型:尽管ASan可以检测到许多内存相关的错误,但它并不能覆盖所有可能的问题。某些类型的错误,如数据竞争和内存泄漏,ASan无法直接检测到。
    4. 可能引入假阳性:由于ASan是在运行时进行检测的,它可能会产生一些误报(假阳性),即报告了实际上并非错误的情况。这可能会增加调试和排除问题的复杂性。

    综上所述,ASan作为一种内存错误检测工具,具有很多优点,但也存在一些缺点。在使用ASan之前,需要权衡其带来的利弊,根据具体情况选择适合的工具和方法来确保代码的质量和可靠性。

    ASAN介绍及使用

    which aarch64-none-linux-gnu-gcc
    /usr/local/gcc-arm-9.2-2019.12-x86_64-aarch64-none-linux-gnu/bin/aarch64-none-linux-gnu-gcc
    
    ./aarch64-none-linux-gnu/lib64/libasan.so
    ./aarch64-none-linux-gnu/libc/usr/lib64/libasan.so
    
    • 1
    • 2
    • 3
    • 4
    • 5

    gcc编译选项

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

    #-fno-stack-protector:去使能栈溢出保护
    #-fno-omit-frame-pointer:去使能栈溢出保护

    test.c测试程序:

    //命名test.c
    #include 
    #include 
    
    char* getMemory()
    {
    	char *p = (char *)malloc(30);
    	return p;
    }
    
    int main()
    {
    	char *p = getMemory();
    	p = NULL;
    
    	return 0;
    }
    //这段代码存在内存泄漏的问题。在函数getMemory中,它使用malloc函数动态分配了内存,并返回指向该内存的指针。
    //然而,在main函数中,获取到指向动态分配内存的指针后,立即将指针赋值为NULL,导致之前分配的内存地址丢失,
    //无法再被释放。
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20

    ASAN_OPTIONS设置:

    ASAN_OPTIONS是Address-Sanitizier的运行选项环境变量。

    #halt_on_error=0:检测内存错误后继续运行
    #detect_leaks=1:使能内存泄露检测
    #malloc_context_size=15:内存错误发生时,显示的调用栈层数为15
    #log_path=/home/asan.log:内存检查问题日志存放文件路径

    #env |grep ASAN_OPTIONS
    设置环境变量,

    # export ASAN_OPTIONS=halt_on_error=0:use_sigaltstack=0:detect_leaks=1:malloc_context_size=15:log_path=/home/asan.log
    # env |grep ASAN_OPTIONS
    # gcc test.c -fsanitize=address -fsanitize-recover=address -fno-stack-protector -fno-omit-frame-pointer
    
    • 1
    • 2
    • 3

    运行a.out会生成acan检出的log, /home/asan.log.60920,如下:

    
    =================================================================
    ==114141==ERROR: LeakSanitizer: detected memory leaks
    
    Direct leak of 30 byte(s) in 1 object(s) allocated from:
        #0 0x7fb16294bb40 in __interceptor_malloc (/usr/lib/x86_64-linux-gnu/libasan.so.4+0xdeb40)
        #1 0x55ca068137fb in getMemory (/home/wuqianlong/a.out+0x7fb)
        #2 0x55ca06813817 in main (/home/wuqianlong/a.out+0x817)
        #3 0x7fb16249dc86 in __libc_start_main (/lib/x86_64-linux-gnu/libc.so.6+0x21c86)
    
    SUMMARY: AddressSanitizer: 30 byte(s) leaked in 1 allocation(s).
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11

    动态库的asan日志定位

    现在创建两个文件,test.c和test1.c, test.c的内容如上述内容。

    //test1.c
    #include 
    #include 
    
    extern char* getMemory();
    
    int main()
    {
    	char *p = getMemory();
    	p = NULL;
    
    	return 0;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13

    将test.c编译成共享库libtest.so

    gcc -shared -o libtest.so test.c  -fsanitize=address -fsanitize-recover=address -fno-stack-protector -fPIC
    
    • 1

    链接共享库,编译test1.c成a.out文件

    gcc -o a.out test1.c /home/wuqianlong/libtest.so -fsanitize=address -fsanitize-recover=address -fno-stack-protector
    
    • 1

    运行a.out文件生成asan log如下:

    
    =================================================================
    ==89713==ERROR: LeakSanitizer: detected memory leaks
    
    Direct leak of 30 byte(s) in 1 object(s) allocated from:
        #0 0x7f0efbcefb40 in __interceptor_malloc (/usr/lib/x86_64-linux-gnu/libasan.so.4+0xdeb40)
        #1 0x7f0efba0f6eb in getMemory (/home/wuqianlong/libtest.so+0x6eb)
        #2 0x5570669f283b in main (/home/wuqianlong/a.out+0x83b)
        #3 0x7f0efb63fc86 in __libc_start_main (/lib/x86_64-linux-gnu/libc.so.6+0x21c86)
    
    SUMMARY: AddressSanitizer: 30 byte(s) leaked in 1 allocation(s).
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11

    案例

    1.堆访问越界(heap-buffer-overflow)

    #include 
    #include 
    int main(int argc, char **argv)
    {
        int *array = malloc(sizeof (int) * 100);
        array[0] = 0;
        int res = array[1 + 100]; //array访问越界
        free(array);
    
        pause();//程序等待,不退出
        return 0;
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13

    编译命令:

    gcc heapOOB.c -o heapOOB -g -fsanitize=address -fsanitize=leak
    
    
    • 1
    • 2

    执行情况:

    ==3653==ERROR: AddressSanitizer: heap-buffer-overflow on address 0x61400000ffd4 at pc 0x000000400871 bp 0x7ffe50cde9c0 sp 0x7ffe50cde9b0
    READ of size 4 at 0x61400000ffd4 thread T0
    #0 0x400870 in main /home/jetpack/work/4G/test/asan/heapOOB.c:7
    #1 0x7f30b337a83f in __libc_start_main (/lib/x86_64-linux-gnu/libc.so.6+0x2083f)
    #2 0x400708 in _start (/home/jetpack/work/4G/test/asan/heapOOB+0x400708)
    0x61400000ffd4 is located 4 bytes to the right of 400-byte region [0x61400000fe40,0x61400000ffd0)
    allocated by thread T0 here:
    #0 0x7f30b37bc602 in malloc (/usr/lib/x86_64-linux-gnu/libasan.so.2+0x98602)
    #1 0x4007ee in main /home/jetpack/work/4G/test/asan/heapOOB.c:5
    #2 0x7f30b337a83f in __libc_start_main (/lib/x86_64-linux-gnu/libc.so.6+0x2083f)
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11

    2.栈访问越界(stack-buffer-overflow)

    #include 
    #include 
    int main(int argc, char **argv) 
    {
        int stack_array[100];
        stack_array[100] = 0;//栈访问越界
    
        pause();
        return 0; 
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11

    编译命令:

    gcc stackOOB.c -o stackOOB -g -fsanitize=address -fsanitize=leak
    
    • 1

    执行结果:

    ==3952==ERROR: AddressSanitizer: stack-buffer-overflow on address 0x7fffdb72e830 at pc 0x0000004008ef bp 0x7fffdb72e660 sp 0x7fffdb72e650
    WRITE of size 4 at 0x7fffdb72e830 thread T0
    #0 0x4008ee in main /home/jetpack/work/4G/test/asan/stackOOB.c:6
    #1 0x7f0c47a8e83f in __libc_start_main (/lib/x86_64-linux-gnu/libc.so.6+0x2083f)
    #2 0x400748 in _start (/home/jetpack/work/4G/test/asan/stackOOB+0x400748)
    Address 0x7fffdb72e830 is located in stack of thread T0 at offset 432 in frame
    #0 0x400825 in main /home/jetpack/work/4G/test/asan/stackOOB.c:4
    This frame has 1 object(s):
    [32, 432) 'stack_array' <== Memory access at offset 432 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)
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12

    3.使用已经释放的内存(UseAfterFree)

    #include 
    #include 
    int main(int argc, char **argv) 
    {
        int *array = malloc(sizeof (int) * 100);
        array[0] = 0;
        free(array);
        int res = array[0]; //使用已经释放的内存
    
        pause();
        return 0;
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13

    编译命令:

    gcc heapUAF.c -o heapUAF -g -fsanitize=address -fsanitize=leak
    
    • 1

    执行结果:

    ==4385==ERROR: AddressSanitizer: heap-use-after-free on address 0x61400000fe40 at pc 0x000000400877 bp 0x7ffdc9019c20 sp 0x7ffdc9019c10
    READ of size 4 at 0x61400000fe40 thread T0
    #0 0x400876 in main /home/jetpack/work/4G/test/asan/heapUAF.c:8
    #1 0x7f93f8d5683f in __libc_start_main (/lib/x86_64-linux-gnu/libc.so.6+0x2083f)
    #2 0x400708 in _start (/home/jetpack/work/4G/test/asan/heapUAF+0x400708)
    0x61400000fe40 is located 0 bytes inside of 400-byte region [0x61400000fe40,0x61400000ffd0)
    freed by thread T0 here:
    #0 0x7f93f91982ca in __interceptor_free (/usr/lib/x86_64-linux-gnu/libasan.so.2+0x982ca)
    #1 0x40083f in main /home/jetpack/work/4G/test/asan/heapUAF.c:7
    #2 0x7f93f8d5683f in __libc_start_main (/lib/x86_64-linux-gnu/libc.so.6+0x2083f)
    previously allocated by thread T0 here:
    #0 0x7f93f9198602 in malloc (/usr/lib/x86_64-linux-gnu/libasan.so.2+0x98602)
    #1 0x4007ee in main /home/jetpack/work/4G/test/asan/heapUAF.c:5
    #2 0x7f93f8d5683f in __libc_start_main (/lib/x86_64-linux-gnu/libc.so.6+0x2083f)
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15

    4.内存泄漏 (Memory Leak)

    #include 
    #include 
    #include 
    
    int main(int argc, char **argv) 
    {
        int *array = malloc(sizeof (int) * 100);
        memset(array, 0, 100 * 4);
    
        return 0;
    }
    //内存泄漏:在 main 函数中通过调用 malloc 分配了内存空间给 array,但在函数结束时并未调用 free 来释放这块内存。这将导致在每次程序运行时都会分配一块新的内存空间,而不会释放之前分配的内存,最终可能导致内存泄漏。
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12

    编译命令:

    gcc heapLeak.c -o heapLeak -g -fsanitize=address -fsanitize=leak
    
    • 1

    执行结果:

    ==3120==ERROR: LeakSanitizer: detected memory leaks
    
    Direct leak of 400 byte(s) in 1 object(s) allocated from:
    #0 0x7f412d5b7602 in malloc (/usr/lib/x86_64-linux-gnu/libasan.so.2+0x98602)
    #1 0x40073e in main /home/jetpack/work/4G/test/asan/heapLeak.c:7
    #2 0x7f412d17583f in __libc_start_main (/lib/x86_64-linux-gnu/libc.so.6+0x2083f)
    
    SUMMARY: AddressSanitizer: 400 byte(s) leaked in 1 allocation(s).
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8

    5.函数返回的局部变量访问

    int *ptr;
    void usr_func(void)
    {    
        //申请栈内存
        int local[100] = {0};
        ptr = local;
    }    
         
    int main(void)
    {    
        //使用函数栈返回的内存
        *ptr = 0;
        return 0;                                                                                                                                                                                                                                
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15

    6.全局变量访问越界

    int global_array[100] = {0};                                                                                                                                                                                                                 
     
    int main(int argc, char **argv) 
    {
        //全局变量global_array访问越界 
        global_array[101];
        return 0;
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9

    感谢一起学习,有疑问评论区讨论。

  • 相关阅读:
    定期删除elasticsearch数据
    【计算机基础】优雅的PPT就应该这样设计
    网络安全(黑客)—-2024自学手册
    Vue-计算属性、监视属性、深度监视、计算属性与监视属性对比
    【MySQL】-增删查改
    题目 1057: 二级C语言-分段函数
    【Python】记录从3.9升级到3.11踩的坑
    小目标检测文章阅读
    【Delphi 开箱即用 5】利用封装好的WebSocket库与服务器通信
    浏览器渲染机制
  • 原文地址:https://blog.csdn.net/qq_36781842/article/details/134501305