实际上,一个byte可以编码的状态总共有256(2^8)种,因此用在这里绰绰有余。
Shadow memory和normal memory的映射关系如上图所示。一个byte的shadow memory反映8个byte normal memory的状态。那如何根据normal memory的地址找到它对应的shadow memory呢?
对于64位机器上的Android而言,二者的转换公式如下:
Shadow memory address = (Normal memory address 》》 3) + 0x100000000
右移三位的目的是为了完成 81的映射,而加一个offset是为了和Normal memory区分开来。最终内存空间种会存在如下的映射关系:
Bad代表的是shadow memory的shadow memory,因此其中数据没有意义,该内存区域不可使用。
上文中提到,8字节组成的memory region共有9中状态:
1~7个字节可寻址(共七种),shadow memory的值为1~7。
8个字节都可寻址,shadow memory的值为0。
0个字节可寻址,shadow memory的值为负数。
为什么0个字节可寻址的情况shadow memory不为0,而是负数呢?是因为0个字节可寻址其实可以继续分为多种情况,譬如:
这块区域是heap redzones
这块区域是stack redzones
这块区域是global redzones
这块区域是freed memory
对所有0个字节可寻址的normal memory region的访问都是非法的,ASAN将会报错。而根据其shadow memory的值便可以具体判断是哪一种错。
Shadow byte legend (one shadow byte represents 8 application bytes): Addressable: 00 Partially addressable: 01 02 03 04 05 06 07 Heap left redzone: fa (实际上Heap right redzone也是fa) Freed Heap region: fd Stack left redzone: f1 Stack mid redzone: f2 Stack right redzone: f3 Stack after return: f5 Stack use after scope: f8 Global redzone: f9 Global init order: f6 Poisoned by user: f7 Container overflow: fc Array cookie: ac Intra object redzone: bb ASan internal: fe Left alloca redzone: ca Right alloca redzone: cb Shadow gap: cc
1.2 检测算法
ShadowAddr = (Addr 》》 3) + Offset;k = *ShadowAddr;if (k != 0 && ((Addr & 7) + AccessSize 》 k)) ReportAndCrash(Addr);
在每次内存访问时,都会执行如上的伪代码,以判断此次内存访问是否合规。
首先根据normal memory的地址找到对应shadow memory的地址,然后取出其中存取的byte值:k。
k!=0,说明Normal memory region中的8个字节并不是都可以被寻址的。