1.4 缺陷

自从2011年诞生以来,ASAN已经成功地参与了众多大型项目,譬如Chrome和Android。虽然它的表现很突出,但仍然有些地方不尽如人意,重点表现在以下几点:

ASAN的运行是需要消耗memory和CPU资源的,此外它也会增加代码大小。它的性能相比于之前的工具确实有了质的提升,但仍然无法适用于某些压力测试场景,尤其是需要全局打开的时候。这一点在Android上尤为明显,每当我们想要全局打开ASAN调试某些奇葩问题时,系统总会因为负载过重而跑不起来。

ASAN对于UseAfterFree的检测依赖于隔离区,而隔离时间是非永久的。也就意味着已经free的区域过一段时间后又会重新被分配给其他人。当它被重新分配给其他人后,原先的持有者再次访问此块区域将不会报错。因为这一块区域的shadow memory不再是0xfd。所以这算是ASAN漏检的一种情况。

ASAN对于overflow的检测依赖于安全区,而安全区总归是有大小的。它可能是64bytes,128bytes或者其他什么值,但不管怎么样终归是有限的。如果某次踩踏跨过了安全区,踩踏到另一片可寻址的内存区域,ASAN同样不会报错。这是ASAN的另一种漏检。

2.HWASAN

HWASAN是ASAN工具的“升级版”,它基本上解决了上面所说的ASAN的3个问题。但是它需要64位硬件的支持,也就是说在32位的机器上该工具无法运行。

AArch64是64位的架构,指的是寄存器的宽度是64位,但并不表示内存的寻址范围是2^64。真实的寻址范围和处理器内部的总线宽度有关,实际上ARMv8寻址只用到了低48位。也就是说,一个64bit的指针值,其中真正用于寻址的只有低48位。那么剩下的高16位干什么用呢?答案是随意发挥。AArch64拥有地址标记(Address tagging, or top-byte-ignore)的特性,它表示允许软件使用64bit指针值的高8位开发特定功能。

HWASAN用这8bit来存储一块内存区域的标签(tag)。接下来我们以堆内存示例,展示这8bit到底如何起作用。

堆内存通过malloc分配出来,HWASAN在它返回地址时会更改该有效地址的高8位。更改的值是一个随机生成的单字节值,譬如0xaf。此外,该分配出来的内存对应的shadow memory值也设为0xaf。需要注意的是,HWASAN中normal memory和shadow memory的映射关系是161,而ASAN中二者的映射关系是81。

以下分别讨论UseAfterFree和HeapOverFlow的情况。

2.1 Use-After-Free

当一个堆内存被分配出来时,返回给用户空间的地址便已经带上了标签(存储于地址的高8位)。之后通过该地址进行内存访问,将先检测地址中的标签值和访问地址对应的shadow memory的值是否相等。如果相等则验证通过,可以进行正常的内存访问。