AddressSanitizer(Asan)的使用

1,795 阅读3分钟

开启掘金成长之旅!这是我参与「掘金日新计划 · 12 月更文挑战」的第5天,点击查看活动详情

摘要

作为C/C++程序员,具有操作内存的能力,但是这也造成C/C++程序员易由于内存操作不当,导致产生各种bug,AddressSanitizer作为一种编译器和运行技术,可用于查找各种bug。

AddressSanitizer作用

AddressSanitizer (ASan) 是一种编译器和运行时技术。最初由 Google 引入,它是运行时错误检查和静态分析的一种强大的替代方法。 它提供运行时 bug 查找技术,这些技术可直接使用现有的生成系统和现有的测试资产。
从 Visual Studio 2019 版本 16.9 开始,Microsoft 的 AddressSanitizer 技术可实现与 Visual Studio IDE 的集成。 如果擦除器在运行时发现 bug,该功能可以选择创建故障转储文件。前提是在运行程序之前设置了 ASAN_SAVE_DUMPS=MyFileName.dmp 环境变量。

AddressSanitizer支持检查的bug

在其说明的文档列出了AddressSanitizer可以在运行期检查出的部分bug,可以看出AddressSanitizer的功能还是很强大的。

  1. alloc/dealloc 不匹配和 new/delete 类型不匹配
    • 分配内存和释放内存的函数不匹配,如new分配内存,delete[] 释放内存
    • 构造函数和析构函数不匹配, 子类构造、基类析构
  2. 分配对堆来说太大
  3. calloc 溢出和 alloca 溢出
    函数原型:void* calloc(unsigned int num,unsigned int size);
    功能:在内存的动态存储区中分配num个长度为size的连续空间,函数返回一个指向分配起始地址的指针;如果分配不成功,返回NULL。
    
    函数原型:void * __cdecl  alloca(size_t);
    功能:在调用 alloca的函数返回的时候, 它分配的内存会自动释放。
    也就是说, 用 alloca 分配的内存在栈上。
    alloca不具可移植性, 而且在没有传统堆栈的机器上很难实现。
    
    • 当calloc设定的数量为非法值,如-1
    • 当alloca申请的栈空间的索引值大于其空间时,如
      char *str = (char *)_alloca(len);//len=10;index=33;
      str[index] = '1'; // Boom !
      
  4. 重复释放和释放后使用
    • 重复释放:new申请的内存,被delete两次(首次delete后未将指针置为nullptr)
    • 指针被释放后仍操作指针:就是常说的悬挂指针
  5. 全局变量溢出
    • C程序中,指针越界
  6. 堆缓冲区溢出
    • new[]申请内存,但是使用时,索引值越界
  7. 对齐值对齐无效
    void * _aligned_malloc(size_t size,size_t alignment);
     size:请求的内存分配的大小。
     alignment:对齐值,必须是 2 的整数次幂。
    
    void* ptr = _aligned_malloc(8,5);//触发该错误
    
  8. memcpy 和 strncat 参数重叠(自测VS2022未检查出该问题
  9. 堆栈缓冲区溢出和下溢
    • 溢出:定义数组,而后索引越界
    • 下溢:索引值为-1
  10. return 后使用堆栈和限定作用域后使用
    此检查需要通过额外的编译器选项 /fsanitize-address-use-after-return 
    并通过设置环境变量 ASAN_OPTIONS=detect_stack_use_after_return=1 
    激活的代码生成。
    
    • 操作已经析构的局部变量
  11. 在内存中毒后使用内存

AddressSanitizer的安装

在安装VS2019时可以选择安装该工具,如果VS2019已经安装完成但未安装该功能,可以通过控制面板找到VS2019,然后右键->更改,勾选C++ AddressSanitizer,然后单击安装(或更改)即可。具体的选择界面如下:

asan-installer.png

AddressSanitizer用法

介绍以VS IDE开启AddressSanitizer,步骤如下:

  1. 选中项目->右键->属性,打开属性页
  2. 选中C/C++ -> 常规 ->启用地址擦除器系统 VS2019 asan_open.jpg
  3. 如果安装VS2022或更高版本的VS时,还可以启用模糊支持

VS2022 asan_open.jpg

开启AddressSanitizer后重新编译工程,F5调试执行即可

AddressSanitizer案例

源代码如下,用以分配对堆来讲太大

	char* buffer = (char*)malloc(x * y * x * y); //Boom!

	memcpy(buffer, buffer + 8, 8);

允许时AddressSanitizer给出的反馈如下:

allocation-size-too-big.jpg