ASAN入门参考

目录

Asan 是什么?

Asan 功能

缓冲区溢出

悬空指针(引用)

非法释放

内存泄漏

如何使用

实验环境

内存泄露检查

demo1

demo2

堆缓冲区溢出

栈缓冲区溢出

使用悬空指针

使用栈上返回的变量

使用退出作用域的变量/内存

重复释放

输出优化

使用ASAN_OPTIONS参数启动程序

使用asan_symbolize.py脚本

参考 


Asan 是什么?

  1. ASan,即Address Sanitizer,是一个适用于c/c++的动态内存错误检测器,它由一个编译器检测模块(LLVM pass)和一个替换malloc函数的运行时库组成,在性能及检测内存错误方面都优于Valgrind。

Asan 功能

ASan作为编译器内置功能,支持检测各种内存错误:

缓冲区溢出

  1. 堆内存溢出

  2. 栈上内存溢出

  3. 全局区缓存溢出

悬空指针(引用)

  1. 使用释放后的堆上内存

  2. 使用返回的栈上内存

  3. 使用退出作用域的变量

非法释放

  1. 重复释放

  2. 无效释放

内存泄漏

如何使用

  1. 使用ASan时,只需gcc/g++选项加上-fsanitize=address
  2. 如果想要在使用asan的时候获取更好的性能,可以加上O1或者更高的编译优化选项
  3. 想要在错误信息中让栈追溯信息更友好,可以加上-fno-omit-frame-pointer选项
  4. 需要注意的是需要带-g选项,会加入一些调试信息到符号表以供输出使用,不带也可以,如果不带,可能看不到错在哪一行
  5. 需要动态链接库liblsan.so,这个动态链接库会替换掉malloc等系统函数,在自己的malloc中加上统计信息,以达到检测内存泄露的作用。需要注意的点是,我们的环境中可能会有多个gcc或者找不到dso的时候。那么我们可以使用g++ --print-file-name=libasan.so来找到系统的动态链接库(只能是系统的库),这条命令会告诉你so在哪,但在我们的环境中我遇到了一个比较坑的问题。就是so是有,但是so的大小仅为4,里面写着让重新下载so
  6. -fsanitize=,这个有好几种选项

        6.1 asan(内存检测)

        6.2 ubsan(未定义行为检测), 有的时候debug的程序没问题,release的程序会奇奇怪怪的core dump掉,那么你需要做这个检测

        6.3 tsan(线程安全检测),如标题;tsan有一个点需要注意,因为大家代码跑通后一般不会用tsan做检测,再者tsan出来时间不长,一些老的库会有非常多的线程安全问题。再加上检测条件非常严格。所以,大型项目第一用tsan做检查的时候,可能每一行都会有线程安全问题。

        6.4 leak(泄露检测),被6.1包括了

        6.5 如果我们有多个libasan.so,我们需要跟-L/path/to/lib

        6.6 如果提示请加载PRELOAD,那么请export LD_PRELOAD=或者export LD_PRELOAD=/path/to/liblsan.so/libasan.so

实验环境

No LSB modules are available.

Distributor ID: Ubuntu

Description: Ubuntu 20.04.3 LTS

Release: 20.04

Codename: focal

gcc (Ubuntu 9.4.0-1ubuntu1~20.04.1) 9.4.0

Copyright (C) 2019 Free Software Foundation, Inc.

This is free software; see the source for copying conditions. There is NO

warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.

g++ (Ubuntu 9.4.0-1ubuntu1~20.04.1) 9.4.0

Copyright (C) 2019 Free Software Foundation, Inc.

This is free software; see the source for copying conditions. There is NO

warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.

内存泄露检查

demo1

#include 
using namespace std;
int main()
{
    int *p = new int(5);
    std::cout << *p << std::endl;
    return 0;
}

编译:g++ main.cpp -g -llsan -fsanitize=leak

ASAN入门参考_第1张图片

第9行,是个总结,他会告诉你总共泄露了4bytes,这4bytes属于1次开辟的。

4-7行,我们这里只泄露了一次,如果多次的话会有多个4-7行,会告诉你那个线程开辟的空间,哪个线程释放的空间。并且打印出详细的开辟和释放的函数栈,当然我们注意第五行,我们用的malloc已经被替换成lsan下的malloc了。

第2行,错误类型。

demo2

#include 
#include 
#include 
 
char* get_systeminfo()
{
        char *p_system = (char*)malloc(38*sizeof(char));
        strcpy(p_system, "Linux version");
        return p_system;
}
 
int main()
{
        printf("Linux version is:%s", get_systeminfo());
        return 0;

}

编译运行输出:

需带上ASAN_OPTIONS=detect_leaks=1 环境变量参数启程序
编译:
g++ -fsanitize=address -fno-omit-frame-pointer -o memory_leak_test main.cpp
ASAN_OPTIONS=detect_leaks=1  ./memory_leak_test

输出:
=================================================================
==73465==ERROR: LeakSanitizer: detected memory leaks

Direct leak of 38 byte(s) in 1 object(s) allocated from:
    #0 0x7fc23737d808 in __interceptor_malloc ../../../../src/libsanitizer/asan/asan_malloc_linux.cc:144
    #1 0x55d14d17b21e in get_systeminfo() (/home/wangji/文档/桌面/学习资料/C++/memory_leak_test+0x121e)
    #2 0x55d14d17b24d in main (/home/wangji/文档/桌面/学习资料/C++/memory_leak_test+0x124d)
    #3 0x7fc236d560b2 in __libc_start_main (/lib/x86_64-linux-gnu/libc.so.6+0x240b2)

SUMMARY: AddressSanitizer: 38 byte(s) leaked in 1 allocation(s).

堆缓冲区溢出

#include 
#include 
#include 
 
int main()
{
   char *heap_buf = (char*)malloc(32*sizeof(char));
   memcpy(heap_buf+30, "overflow", 8);//在heap_buf的第30个字节开始,拷贝8个字符
 
   free(heap_buf);
 
   return 0;
}

编译:

gcc -fsanitize=address -fno-omit-frame-pointer -o heap_ovf_test main.c

./heap_ovf_test

ASAN入门参考_第2张图片

可以看到asan报错:==74085==ERROR: AddressSanitizer: heap-buffer-overflow on address 0x603000000030 at xxxx,下面也列出了发生heap-buffer-overflow时的调用链及heap buffer在哪里申请的。

栈缓冲区溢出

#include 
#include 
 
int main()
{
        char stack_buf[4] = {0};
        strcpy(stack_buf, "1234");
 
        return 0;
}

编译:

gcc -fsanitize=address -fno-omit-frame-pointer -o stack_ovf_test main.c

./stack_ovf_test

ASAN入门参考_第3张图片

使用悬空指针

#include 
#include 
#include 
 
int main()
{
        char *p = (char*)malloc(32*sizeof(char));
        free(p);
 
        int a = p[1];
 
        return 0;
}

编译:

gcc -fsanitize=address -fno-omit-frame-pointer -o dang_pointer main.c

./dang_pointer

ASAN入门参考_第4张图片

使用栈上返回的变量

#include 
#include 
#include 
int *ptr;
void get_pointer()
{
        int local[10];
        ptr = &local[0];
        return;
}

int main()
{
        get_pointer();
 
        printf("%d\n", *ptr);
 
        return 0;

}

编译:

启用ASAN_OPTIONS=detect_stack_use_after_return=1标志,才能检测此种内存错误使用的情况

gcc -fsanitize=address -fno-omit-frame-pointer -o use_after_return main.c

ASAN_OPTIONS=detect_stack_use_after_return=1 ./use_after_return

ASAN入门参考_第5张图片

使用退出作用域的变量/内存

#include 
#include 
#include 
 
int main()
{
        int *p;
        {
                int num = 10;
                p = #
        }
        printf("%d/n", *p);
 
        return 0;

}

编译:

gcc -fsanitize=address -fno-omit-frame-pointer -o use-after-scope main.c

./use-after-scope

ASAN入门参考_第6张图片

重复释放

#include 
#include 
 
int main()
{
        char *p = (char*)malloc(32*sizeof(char));
        free(p);
        free(p);
 
        return 0;
}

编译:

gcc -fsanitize=address -fno-omit-frame-pointer -o invalid_free_test main.c

./invalid_free_test

ASAN入门参考_第7张图片

输出优化

使用ASAN_OPTIONS参数启动程序

ASAN_OPTIONS='stack_trace_format="[frame=%n, function=%f, location=%S]"'参数启动程序

ASAN入门参考_第8张图片

使用asan_symbolize.py脚本

输出的调用链中信息更精确,可以对应到代码文件的具体某一行。

参考 

AddressSanitizer · google/sanitizers Wiki · GitHub

你可能感兴趣的:(#,KAsan,ASAN&HWASAN,ASAN)