调试工具:Google ASAN

一、asan介绍

  Google ASan工具ASAN,全称 AddressSanitizer,也即地址消毒技术。可以用来检测内存问题,例如缓冲区溢出或对悬空指针的非法访问等。
  ASan主要是进行编译器级别的HOOK与插桩,目前主流Clang,GCC,MSVC都支持,再结合运行时对影子内存的诊断输出,相当于双管齐下,整体效果不错;官方说是2倍左右性能开销,1/8的内存到2倍的开销。

  • 内存操作进行插桩: 对new,malloc,delete,free,memcpy,其它内存访问等操作进行编译时替换与代码插入,是编译器完成的;
  • 内存映射与诊断:按照一定的算法对原始内存进行一分影子内存的拷贝生成,目前不是1:1的拷贝,而是巧妙的按1/8大小进行处理,并进行一定的下毒与标记,减少内存的浪费。正常访问内存前,先对影子内存进行检查访问,如果发现数据不对,就进行诊断报错处理。

ASan的诊断功能:

释放后访问(野指针)
栈内存溢出
堆内存溢出
全局对象溢出访问
函数返回后访问
超出作用区访问
初始化顺序问题
内存泄露

ASan的弊端:

对内存溢出检查:依赖正常内存左右两端设定毒药区域大小;比如128字节,虽然这个值可以调,但越界超出这个值后,依然无法检查的到。
释放后访问检查:目前是对该内存进行隔离,并对影子内存标记为0xFD,但这个隔离不可能永久;一但被重新复用后,也可能造成严重内存问题,有类像内存池复用崩溃问题;
性能问题:由于插桩引入了很多汇编指令(Andorid平台还会有动态库),性能与内存上对比其它产品虽然还可以,但也只能在内部环境或Debug环境部署,无法直接应用到线上;

二、asan原理

  • 1、运行时库:libasan.so.x(libasan.so.x)会接管malloc和free等内存操作函数。malloc执行完后,已分配内存的前后(称为“红区”)会被标记为“中毒”状态,而释放的内存则会被隔离起来(暂时不会分配出去)且也会被标记为“中毒”状态。
  • 2、编译器插桩模块:
    加了ASAN相关的编译选项后,代码中的每一次内存访问操作都会被编译器修改为如下方式:

编译前:

*address = ...;    // or   ... = *address;

编译后:

if (IsPoisoned(address)) { // 判断内存是否中毒
  ReportError(address, kAccessSize, kIsWrite);
}
*address = ...;  // or: ... = *address;

该方式的关键点就在于读写内存前会判断地址是否处于“中毒”状态,还有如何把IsPoisoned实现的非常快,把ReportError实现的非常紧凑,从而避免插入的代码过多。

ASan对缓冲区溢出防护的的基本步骤如下:

  1. 通过在被保护的栈、全局变量、堆周围建立标记为中毒状态(Poisnoned)的red-zones;red-zones区会写一个特殊值,该值称为“影子值”。比如fa\fd等
  2. 将缓冲区和red-zone通过每8字节对应1字节的映射的方式建立影子内存区,影子内存区的获取函数为MemToShadow。
  3. 如果出现对red-zone的读、写或执行的访问,则ASan可以ShadowIsPoisoned检测出来并报错。

关于影子值,针对任何8字节对齐的主应用区内存,总共有9种不同的影子内存值:

全部8字节都未“中毒”(可访问的),影子值是00。
全部8字节都“中毒”(不可访问的),影子值是负数。
前k个字节未“中毒”,后8-k字节“中毒”,影子值是k。这一功能的达成是由malloc函数总是返回8字节对齐的内存块来保证的,唯一能出现该情况的场景就在申请内存区域的尾部。例如,我们申请13个字节,即malloc(13),这样我们会得到一个完整的未“中毒”的00和前5个字节未“中毒”、后3个字节“中毒”的03。即00 03

三、常用的 ASAN 部署

1. 编译选项(Gcc编译选项)

常用的选项
ASAN_CFLAGS += -fno-stack-protector -fno-omit-frame-pointer -fno-var-tracking -g1

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

-fno-stack-protector:去使能栈溢出保护

  1. 堆栈溢出为代表的缓冲区溢出 已成为最为普遍的安全漏洞,由此引发的安全问题比比皆是。我们知道攻击者利用堆栈溢出漏洞时,通常会破坏当前的函数栈。在gcc中,通过编译选项可以添加 函数栈的保护机制,通过重新对局部变量进行布局来实现,达到监测函数栈是否非破坏的目的。
  2. gcc中有3个与堆栈保护相关的编译选项(默认不开启这些编译选项的话,gcc 也会对局部变量重新布局)
    -fstack-protector:启用堆栈保护,不过只为局部变量中含有char数组的函数插入保护代码。编译器会对局部变量的组织方式进行重新布局,数组往高地址存放,局部变量往低地址存放
    -fstack-protector-all:启用堆栈保护,为所有函数插入保护代码。编译器会对局部变量的组织方式进行重新布局,数组往高地址存放,局部变量往低地址存放
    -fno-stack-protector :禁用堆栈保护。编译器不会对局部变量的组织方式进行重新布局

-fno-omit-frame-pointer:表示将堆栈帧指针存储在寄存器中,帧指针是用来指示当前函数的栈帧(stack frame)的指针,在调试时可以帮助跟踪函数调用的堆栈信息。在 AArch32 架构中,堆栈帧指针存储在寄存器 R11(A32代码)或寄存器 R7(T32代码)中;在AArch64架构中,堆栈帧指针存储在寄存器 X29 中。作为帧指针使用的寄存器不能用作通用寄存器,但如果使用 -fomit-frame-pointer 选项编译,则可用作通用寄存器。

-fno-var-tracking:默认选项为-fvar-tracking,会导致运行非常慢,是GCC编译器的一个选项,它用于控制是否启用变量跟踪分配的切换(待补充)

-g1:表示最小调试信息,通常debug版本用-g即-g2

2. Ld链接选项

ASAN_LDFLAGS += -fsanitize=address -g1
如果使用gcc链接,此处可忽略。

3. ASAN运行选项

export ASAN_OPTIONS=halt_on_error=0:use_sigaltstack=0:symbolize=1:detect_leaks=1:malloc_context_size=15:log_path=./asan.log:suppressions=$SUPP_FILE

3.1 ASAN_OPTIONS设置

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=0:use_sigaltstack=0:detect_leaks=1:malloc_context_size=15:log_path=./asan.log:suppressions=$SUPP_FILE

3.2 其他选项

ASAN_OPTIONS=${ASAN_OPTIONS}:verbosity=0:handle_segv=1:allow_user_segv_handler=1:detect_stack_use_after_return=1:fast_unwind_on_fatal=1:fast_unwind_on_check=1:fast_unwind_on_malloc=1:quarantine_size=4194304
  • detect_stack_use_after_return=1:检查访问指向已被释放的栈空间

  • handle_segv=1:处理段错误;也可以添加handle_sigill=1处理SIGILL信号

  • quarantine_size=4194304:内存cache可缓存free内存大小4M

3.3 LSAN_OPTIONS设置

export LSAN_OPTIONS=exitcode=0:use_unaligned=4
  • LSAN_OPTIONS是LeakSanitizier运行选项的环境变量,而LeakSanitizier是ASAN的内存泄漏检测模块,常用运行选项有:
  • exitcode=0:设置内存泄露退出码为0,默认情况内存泄露退出码0x16
  • use_unaligned=4:4字节对齐

四、常见的 ASAN 识别技巧

1. 程序结束堆内存未释放 leaked

  • 用长度去判断
    SUMMARY: AddressSanitizer: 100 byte(s) leaked in 1 allocation(s).

  • 用文件地址去判断
    #1 0x4011ac in main /home/ofcx/ju/main.c:15

  • 用 asan log 形式判断
    堆内存未释放一般不会指出具体位置,log 不会有具体的位置非法信息,整个log 以SUMMARY 那行收尾
    在这里插入图片描述

2. 悬空指针非法访问 heap-use-after-free

  • 四要素:访问 悬空指针的位置和大小 、内存被释放位置、内存的分配 位置的堆栈信息以及线程信息

  • 提供了错误访问的内存地址对应的shadow 内存的详细,其中 fa表示堆区内存的red zone,fd表示已经释放的堆区内存

  • 判断方法:

    • 影子区看状态
      =>0x0c167fff8000: fa fa fa fa fa fa fa fa[fd]fd fd fd fd fd fd fd

    • fd 表示已经释放的堆区内存

    • SUMMARY log 看返错
      SUMMARY: AddressSanitizer:heap-use-after-free

    • 内存区域看大小
      0x60b000000040 is located 0 bytes inside of 100-byteregion
      [0x60b000000040,0x60b0000000a4)

3. 检测堆溢出 heap-buffer-overflow

  • 怎么判断(SUMMARY, fa):
    • SUMMARY log 返错 heap-buffer-overflow
    • 快照 fa 堆红灯区
  • 需要哪些信息
    • 申请堆栈
      allocated by thread T0 here:
      #0 0x7f623521abb8 in __interceptor_malloc (/lib64/libasan.so.5+0xefbb8)
      #1 0x4011bc in main (/home/ofcx/ju/main+0x4011bc)
      #2 0x7f6234b69492 in __libc_start_main (/lib64/libc.so.6+0x23492

    • 堆大小
      0x60200000001c is located 0 bytes to the right of 12-byte
      region
      [0x602000000010,0x60200000001c)

    • 快照
      在这里插入图片描述

4. 检测栈溢出 stack-buffer-overflow

  • 如何判断 从 summary 可以直接读出(SUMMARY, f3)

    • SUMMARY : AddressSanitizer: stack-buffer-overflow
    • 快照也可以反映,通过 f1、f3。f3 踩上界
      在这里插入图片描述
  • 要素
    栈大小、踩内存地址
    [32, 432) ‘stack_array’ <== Memory access at offset 436 overflows
    this variable

5. 检测全局缓冲区溢出

  • 如何判断 出(SUMMARY, f9)
    • SUMMARY 总结
      SUMMARY: AddressSanitizer: global-buffer-overflow

    • 快照 f9
      在这里插入图片描述

参考:

内存检测工具——ASan(AddressSanitizer)的介绍和使用 - 知乎
(zhihu.com)

Google ASan内存诊断工具简单讨论与分析 - 知乎
(zhihu.com)

ASAN 问题总结 - 优快云

asan原理详解 - 优快云

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值