由于虛擬機(jī)的存在,Android應(yīng)用開(kāi)發(fā)者們通常不用考慮內(nèi)存訪(fǎng)問(wèn)相關(guān)的錯(cuò)誤。而一旦我們深入到Native世界中,原本面容和善的內(nèi)存便開(kāi)始兇惡起來(lái)。這時(shí),由于程序員寫(xiě)法不規(guī)范、邏輯疏漏而導(dǎo)致的內(nèi)存錯(cuò)誤會(huì)統(tǒng)統(tǒng)跳到我們面前,對(duì)我們嘲諷一番。
這些錯(cuò)誤既影響了程序的穩(wěn)定性,也影響了程序的安全性,因?yàn)楹枚鄲阂獯a就通過(guò)內(nèi)存錯(cuò)誤來(lái)完成入侵。不過(guò)麻煩的是,Native世界中的內(nèi)存錯(cuò)誤很難排查,因?yàn)楹芏鄷r(shí)候?qū)е聠?wèn)題的地方和發(fā)生問(wèn)題的地方相隔甚遠(yuǎn)。為了更好地解決這些問(wèn)題,各路大神紛紛祭出自己手中的神器,相互PK,相互補(bǔ)充。
ASAN(Address Sanitizer)和HWASAN(Hardware-assisted Address Sanitizer)就是這些工具中的佼佼者。
在ASAN出來(lái)之前,市面上的內(nèi)存調(diào)試工具要么慢,要么只能檢測(cè)部分內(nèi)存錯(cuò)誤,要么這兩個(gè)缺點(diǎn)都有。總之,不夠優(yōu)秀。
HWASAN則是ASAN的升級(jí)版,它利用了64位機(jī)器上忽略高位地址的特性,將這些被忽略的高位地址重新利用起來(lái),從而大大降低了工具對(duì)于CPU和內(nèi)存帶來(lái)的額外負(fù)載。
1. ASAN
ASAN工具包含兩大塊:
插樁模塊(Instrumentation module)
一個(gè)運(yùn)行時(shí)庫(kù)(Runtime library)
插樁模塊主要會(huì)做兩件事:
對(duì)所有的memory access都去檢查該內(nèi)存所對(duì)應(yīng)的shadow memory的狀態(tài)。這是靜態(tài)插樁,因此需要重新編譯。
為所有棧上對(duì)象和全局對(duì)象創(chuàng)建前后的保護(hù)區(qū)(Poisoned redzone),為檢測(cè)溢出做準(zhǔn)備。
運(yùn)行時(shí)庫(kù)也同樣會(huì)做兩件事:
替換默認(rèn)路徑的malloc/free等函數(shù)。為所有堆對(duì)象創(chuàng)建前后的保護(hù)區(qū),將free掉的堆區(qū)域隔離(quarantine)一段時(shí)間,避免它立即被分配給其他人使用。
對(duì)錯(cuò)誤情況進(jìn)行輸出,包括堆棧信息。
1.1 Shadow Memory
如果想要了解ASAN的實(shí)現(xiàn)原理,那么shadow memory將是第一個(gè)需要了解的概念。
Shadow memory有一些元數(shù)據(jù)的思維在里面。它雖然也是內(nèi)存中的一塊區(qū)域,但是其中的數(shù)據(jù)僅僅反應(yīng)其他正常內(nèi)存的狀態(tài)信息。所以可以理解為正常內(nèi)存的元數(shù)據(jù),而正常內(nèi)存中存儲(chǔ)的才是程序真正需要的數(shù)據(jù)。
Malloc函數(shù)返回的地址通常是8字節(jié)對(duì)齊的,因此任意一個(gè)由(對(duì)齊的)8字節(jié)所組成的內(nèi)存區(qū)域必然落在以下9種狀態(tài)之中:最前面的k(0≤k≤8)字節(jié)是可尋址的,而剩下的8-k字節(jié)是不可尋址的。這9種狀態(tài)便可以用shadow memory中的一個(gè)字節(jié)來(lái)進(jìn)行編碼。
實(shí)際上,一個(gè)byte可以編碼的狀態(tài)總共有256(2^8)種,因此用在這里綽綽有余。
Shadow memory和normal memory的映射關(guān)系如上圖所示。一個(gè)byte的shadow memory反映8個(gè)byte normal memory的狀態(tài)。那如何根據(jù)normal memory的地址找到它對(duì)應(yīng)的shadow memory呢?
對(duì)于64位機(jī)器上的Android而言,二者的轉(zhuǎn)換公式如下:
Shadow memory address = (Normal memory address 》》 3) + 0x100000000
右移三位的目的是為了完成 81的映射,而加一個(gè)offset是為了和Normal memory區(qū)分開(kāi)來(lái)。最終內(nèi)存空間種會(huì)存在如下的映射關(guān)系:
Bad代表的是shadow memory的shadow memory,因此其中數(shù)據(jù)沒(méi)有意義,該內(nèi)存區(qū)域不可使用。
上文中提到,8字節(jié)組成的memory region共有9中狀態(tài):
1~7個(gè)字節(jié)可尋址(共七種),shadow memory的值為1~7。
8個(gè)字節(jié)都可尋址,shadow memory的值為0。
0個(gè)字節(jié)可尋址,shadow memory的值為負(fù)數(shù)。
為什么0個(gè)字節(jié)可尋址的情況shadow memory不為0,而是負(fù)數(shù)呢?是因?yàn)?個(gè)字節(jié)可尋址其實(shí)可以繼續(xù)分為多種情況,譬如:
這塊區(qū)域是heap redzones
這塊區(qū)域是stack redzones
這塊區(qū)域是global redzones
這塊區(qū)域是freed memory
對(duì)所有0個(gè)字節(jié)可尋址的normal memory region的訪(fǎng)問(wèn)都是非法的,ASAN將會(huì)報(bào)錯(cuò)。而根據(jù)其shadow memory的值便可以具體判斷是哪一種錯(cuò)。
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 (實(shí)際上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 檢測(cè)算法
ShadowAddr = (Addr 》》 3) + Offset;k = *ShadowAddr;if (k != 0 && ((Addr & 7) + AccessSize 》 k)) ReportAndCrash(Addr);
在每次內(nèi)存訪(fǎng)問(wèn)時(shí),都會(huì)執(zhí)行如上的偽代碼,以判斷此次內(nèi)存訪(fǎng)問(wèn)是否合規(guī)。
首先根據(jù)normal memory的地址找到對(duì)應(yīng)shadow memory的地址,然后取出其中存取的byte值:k。
k!=0,說(shuō)明Normal memory region中的8個(gè)字節(jié)并不是都可以被尋址的。
Addr & 7,將得知此次內(nèi)存訪(fǎng)問(wèn)是從memory region的第幾個(gè)byte開(kāi)始的。
AccessSize是此次內(nèi)存訪(fǎng)問(wèn)需要訪(fǎng)問(wèn)的字節(jié)長(zhǎng)度。
(Addr&7)+AccessSize 》 k,則說(shuō)明此次內(nèi)存訪(fǎng)問(wèn)將會(huì)訪(fǎng)問(wèn)到不可尋址的字節(jié)。(具體可分為k大于0和小于0兩種情況來(lái)分析)
當(dāng)此次內(nèi)存訪(fǎng)問(wèn)可能會(huì)訪(fǎng)問(wèn)到不可尋址的字節(jié)時(shí),ASAN會(huì)報(bào)錯(cuò)并結(jié)合shadow memory中具體的值明確錯(cuò)誤類(lèi)型。
1.3 典型錯(cuò)誤
1.3.1 Use-After-Free
想要檢測(cè)UseAfterFree的錯(cuò)誤,需要有兩點(diǎn)保證:
已經(jīng)free掉的內(nèi)存區(qū)域需要被標(biāo)記成特殊的狀態(tài)。在ASAN的實(shí)現(xiàn)里,free掉的normal memory對(duì)應(yīng)的shadow memory值為0xfd(猜測(cè)有freed的意思)。
已經(jīng)free掉的內(nèi)存區(qū)域需要放入隔離區(qū)一段時(shí)間,防止發(fā)生錯(cuò)誤時(shí)該區(qū)域已經(jīng)通過(guò)malloc重新分配給其他人使用。一旦分配給其他人使用,則可能漏掉UseAfterFree的錯(cuò)誤。
測(cè)試代碼:
// RUN: clang -O -g -fsanitize=address %t && 。/a.outint main(int argc, char **argv) { int *array = new int[100]; delete [] array; return array[argc]; // BOOM}
ASAN輸出的錯(cuò)誤信息:
===================================================================6254== ERROR: AddressSanitizer: heap-use-after-free on address 0x603e0001fc64 at pc 0x417f6a bp 0x7fff626b3250 sp 0x7fff626b3248READ of size 4 at 0x603e0001fc64 thread T0 #0 0x417f69 in main example_UseAfterFree.cc:5 #1 0x7fae62b5076c (/lib/x86_64-linux-gnu/libc.so.6+0x2176c) #2 0x417e54 (a.out+0x417e54)0x603e0001fc64 is located 4 bytes inside of 400-byte region [0x603e0001fc60,0x603e0001fdf0)freed by thread T0 here: #0 0x40d4d2 in operator delete[](void*) /home/kcc/llvm/projects/compiler-rt/lib/asan/asan_new_delete.cc:61 #1 0x417f2e in main example_UseAfterFree.cc:4previously allocated by thread T0 here: #0 0x40d312 in operator new[](unsigned long) /home/kcc/llvm/projects/compiler-rt/lib/asan/asan_new_delete.cc:46 #1 0x417f1e in main example_UseAfterFree.cc:3Shadow bytes around the buggy address: 0x1c07c0003f30: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa 0x1c07c0003f40: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa 0x1c07c0003f50: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa 0x1c07c0003f60: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa 0x1c07c0003f70: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa=》0x1c07c0003f80: fa fa fa fa fa fa fa fa fa fa fa fa[fd]fd fd fd 0x1c07c0003f90: fd fd fd fd fd fd fd fd fd fd fd fd fd fd fd fd 0x1c07c0003fa0: fd fd fd fd fd fd fd fd fd fd fd fd fd fd fd fd 0x1c07c0003fb0: fd fd fd fd fd fd fd fd fd fd fd fd fd fd fa fa 0x1c07c0003fc0: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa 0x1c07c0003fd0: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa
可以看到,=》指向的那行有一個(gè)byte數(shù)值用中括號(hào)給圈出來(lái)了:[fd]。它表示的是此次出錯(cuò)的內(nèi)存地址對(duì)應(yīng)的shadow memory的值。而其之前的fa表示Heap left redzone,它是之前該區(qū)域有效時(shí)的遺留產(chǎn)物。連續(xù)的fd總共有50個(gè),每一個(gè)shadow memory的byte和8個(gè)normal memory byte對(duì)應(yīng),所以可以知道此次free的內(nèi)存總共是50×8=400bytes。這一點(diǎn)在上面的log中也得到了驗(yàn)證,截取出來(lái)展示如下:
0x603e0001fc64 is located 4 bytes inside of 400-byte region [0x603e0001fc60,0x603e0001fdf0)
此外,ASAN的log中不僅有出錯(cuò)時(shí)的堆棧信息,還有該內(nèi)存區(qū)域之前free時(shí)的堆棧信息。因此我們可以清楚地知道該區(qū)域是如何被釋放的,從而快速定位問(wèn)題,解決問(wèn)題。
1.3.2 Heap-Buffer-Overflow
想要檢測(cè)HeapBufferOverflow的問(wèn)題,只需要保證一點(diǎn):
正常的Heap前后需要插入一定長(zhǎng)度的安全區(qū),而且此安全區(qū)對(duì)應(yīng)的shadow memory需要被標(biāo)記為特殊的狀態(tài)。在ASAN的實(shí)現(xiàn)里,安全區(qū)被標(biāo)記為0xfa。
測(cè)試代碼:
ASAN輸出的錯(cuò)誤信息:
1405==ERROR: AddressSanitizer: heap-buffer-overflow on address 0x0060bef84165 at pc 0x0058714bfb24 bp 0x007fdff09590 sp 0x007fdff09588WRITE of size 1 at 0x0060bef84165 thread T0 #0 0x58714bfb20 (/system/bin/bootanimation+0x8b20) #1 0x7b434cd994 (/apex/com.android.runtime/lib64/bionic/libc.so+0x7e994)
0x0060bef84165 is located 1 bytes to the right of 100-byte region [0x0060bef84100,0x0060bef84164)allocated by thread T0 here: #0 0x7b4250a1a4 (/system/lib64/libclang_rt.asan-aarch64-android.so+0xc31a4) #1 0x58714bfac8 (/system/bin/bootanimation+0x8ac8) #2 0x7b434cd994 (/apex/com.android.runtime/lib64/bionic/libc.so+0x7e994) #3 0x58714bb04c (/system/bin/bootanimation+0x404c) #4 0x7b45361b04 (/system/bin/bootanimation+0x54b04)
SUMMARY: AddressSanitizer: heap-buffer-overflow (/system/bin/bootanimation+0x8b20) Shadow bytes around the buggy address: 0x001c17df07d0: fa fa fa fa fa fa fa fa fd fd fd fd fd fd fd fd 0x001c17df07e0: fd fd fd fd fd fa fa fa fa fa fa fa fa fa fa fa 0x001c17df07f0: fd fd fd fd fd fd fd fd fd fd fd fd fd fa fa fa 0x001c17df0800: fa fa fa fa fa fa fa fa fd fd fd fd fd fd fd fd 0x001c17df0810: fd fd fd fd fd fa fa fa fa fa fa fa fa fa fa fa=》0x001c17df0820: 00 00 00 00 00 00 00 00 00 00 00 00[04]fa fa fa 0x001c17df0830: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa 0x001c17df0840: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa 0x001c17df0850: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa 0x001c17df0860: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa 0x001c17df0870: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa
可以看到最終出錯(cuò)的shadow memory值為0x4,表示該shadow memroy對(duì)應(yīng)的normal memory中只有前4個(gè)bytes是可尋址的。0x4的shadow memory前還有12個(gè)0x0,表示其前面的12個(gè)memory region(每個(gè)region有8個(gè)byte)都是完全可尋址的。因此所有可尋址的大小=12×8+4=100,正是代碼中malloc的size。之所以此次訪(fǎng)問(wèn)會(huì)出錯(cuò),是因?yàn)榈刂?x60bef84165意圖訪(fǎng)問(wèn)最后一個(gè)region的第五個(gè)byte,而該region只有前四個(gè)byte可尋址。由于0x4后面是0xfa,因此此次錯(cuò)誤屬于HeapBufferOverflow。
1.4 缺陷
自從2011年誕生以來(lái),ASAN已經(jīng)成功地參與了眾多大型項(xiàng)目,譬如Chrome和Android。雖然它的表現(xiàn)很突出,但仍然有些地方不盡如人意,重點(diǎn)表現(xiàn)在以下幾點(diǎn):
ASAN的運(yùn)行是需要消耗memory和CPU資源的,此外它也會(huì)增加代碼大小。它的性能相比于之前的工具確實(shí)有了質(zhì)的提升,但仍然無(wú)法適用于某些壓力測(cè)試場(chǎng)景,尤其是需要全局打開(kāi)的時(shí)候。這一點(diǎn)在Android上尤為明顯,每當(dāng)我們想要全局打開(kāi)ASAN調(diào)試某些奇葩問(wèn)題時(shí),系統(tǒng)總會(huì)因?yàn)樨?fù)載過(guò)重而跑不起來(lái)。
ASAN對(duì)于UseAfterFree的檢測(cè)依賴(lài)于隔離區(qū),而隔離時(shí)間是非永久的。也就意味著已經(jīng)free的區(qū)域過(guò)一段時(shí)間后又會(huì)重新被分配給其他人。當(dāng)它被重新分配給其他人后,原先的持有者再次訪(fǎng)問(wèn)此塊區(qū)域?qū)⒉粫?huì)報(bào)錯(cuò)。因?yàn)檫@一塊區(qū)域的shadow memory不再是0xfd。所以這算是ASAN漏檢的一種情況。
ASAN對(duì)于overflow的檢測(cè)依賴(lài)于安全區(qū),而安全區(qū)總歸是有大小的。它可能是64bytes,128bytes或者其他什么值,但不管怎么樣終歸是有限的。如果某次踩踏跨過(guò)了安全區(qū),踩踏到另一片可尋址的內(nèi)存區(qū)域,ASAN同樣不會(huì)報(bào)錯(cuò)。這是ASAN的另一種漏檢。
2.HWASAN
HWASAN是ASAN工具的“升級(jí)版”,它基本上解決了上面所說(shuō)的ASAN的3個(gè)問(wèn)題。但是它需要64位硬件的支持,也就是說(shuō)在32位的機(jī)器上該工具無(wú)法運(yùn)行。
AArch64是64位的架構(gòu),指的是寄存器的寬度是64位,但并不表示內(nèi)存的尋址范圍是2^64。真實(shí)的尋址范圍和處理器內(nèi)部的總線(xiàn)寬度有關(guān),實(shí)際上ARMv8尋址只用到了低48位。也就是說(shuō),一個(gè)64bit的指針值,其中真正用于尋址的只有低48位。那么剩下的高16位干什么用呢?答案是隨意發(fā)揮。AArch64擁有地址標(biāo)記(Address tagging, or top-byte-ignore)的特性,它表示允許軟件使用64bit指針值的高8位開(kāi)發(fā)特定功能。
HWASAN用這8bit來(lái)存儲(chǔ)一塊內(nèi)存區(qū)域的標(biāo)簽(tag)。接下來(lái)我們以堆內(nèi)存示例,展示這8bit到底如何起作用。
堆內(nèi)存通過(guò)malloc分配出來(lái),HWASAN在它返回地址時(shí)會(huì)更改該有效地址的高8位。更改的值是一個(gè)隨機(jī)生成的單字節(jié)值,譬如0xaf。此外,該分配出來(lái)的內(nèi)存對(duì)應(yīng)的shadow memory值也設(shè)為0xaf。需要注意的是,HWASAN中normal memory和shadow memory的映射關(guān)系是161,而ASAN中二者的映射關(guān)系是81。
以下分別討論UseAfterFree和HeapOverFlow的情況。
2.1 Use-After-Free
當(dāng)一個(gè)堆內(nèi)存被分配出來(lái)時(shí),返回給用戶(hù)空間的地址便已經(jīng)帶上了標(biāo)簽(存儲(chǔ)于地址的高8位)。之后通過(guò)該地址進(jìn)行內(nèi)存訪(fǎng)問(wèn),將先檢測(cè)地址中的標(biāo)簽值和訪(fǎng)問(wèn)地址對(duì)應(yīng)的shadow memory的值是否相等。如果相等則驗(yàn)證通過(guò),可以進(jìn)行正常的內(nèi)存訪(fǎng)問(wèn)。
當(dāng)該內(nèi)存被free時(shí),HWASAN會(huì)為該塊區(qū)域分配一個(gè)新的隨機(jī)值,存儲(chǔ)于其對(duì)應(yīng)的shadow memory中。如果此后再有新的訪(fǎng)問(wèn),則地址中的標(biāo)簽值必然不等于shadow memory中存儲(chǔ)的新的隨機(jī)值,因此會(huì)有錯(cuò)誤產(chǎn)生。通過(guò)如下圖示可以很好地明白這一點(diǎn)(圖中只用了4bit記錄標(biāo)記值,但不影響理解,8bit標(biāo)記值的檢測(cè)和它一致)。
2.2 Heap-Over-Flow
想要檢測(cè)HeapOverFlow,有一個(gè)前提需要滿(mǎn)足:相鄰的memory區(qū)域需要有不同的shadow memory值,否則將無(wú)法分辨兩個(gè)不同的memory區(qū)域。為每個(gè)memory區(qū)域隨機(jī)分配將有概率讓兩個(gè)相鄰區(qū)域具有同樣的shadow memory值,雖然概率比較小,但總歸是個(gè)缺陷。因此工具中會(huì)有其他邏輯保證這個(gè)前提。
下圖展示了HeapOverFlow的檢測(cè)過(guò)程。指針p的標(biāo)簽和訪(fǎng)問(wèn)的地址p[32]所對(duì)應(yīng)的shadow memory值不一致,因此報(bào)錯(cuò)(圖中只用了4bit記錄標(biāo)記值,但不影響理解,8bit標(biāo)記值的檢測(cè)和它一致)。
2.3 錯(cuò)誤信息示例
Abort message: ‘==12528==ERROR: HWAddressSanitizer: tag-mismatch on address 0x003d557e2c20 at pc 0x00748b4a6918READ of size 4 at 0x003d557e2c20 tags: d1/9b (ptr/mem) in thread T0 #0 0x748b4a6914 (/system/lib64/libutils.so+0x11914) #1 0x748a521bdc (/apex/com.android.runtime/lib64/bionic/libc.so+0x121bdc) #2 0x748a51ad7c (/apex/com.android.runtime/lib64/bionic/libc.so+0x11ad7c) #3 0x748a47f830 (/apex/com.android.runtime/lib64/bionic/libc.so+0x7f830)
[0x003d557e2c20,0x003d557e2c80) is a small unallocated heap chunk; size: 96 offset: 0Thread: T0 0x006b00002000 stack: [0x007fcd371000,0x007fcdb71000) sz: 8388608 tls: [0x000000000000,0x000000000000)HWAddressSanitizer can not describe address in more detail.Memory tags around the buggy address (one tag corresponds to 16 bytes): e1 e1 e1 e1 83 83 83 83 83 00 a3 a3 a3 a3 a3 a3 b7 b7 b7 b7 b7 00 01 01 01 01 01 00 95 95 95 95 95 00 ec ec ec ec ec 00 c8 c8 c8 c8 c8 00 21 21 21 21 21 00 cb cb cb cb cb 00 b8 b8 b8 b8 b8 00 14 14 14 14 14 14 b9 b9 b9 b9 b9 b9 89 89 89 89 89 89 95 95 95 95 95 95 47 47 47 47 47 00 fe fe fe fe fe 00 c5 c5 c5 c5 c5 00 8e 8e 8e 8e 8e 8e 5c 5c 5c 5c 5c 5c af af af af af af b0 b0 b0 b0=》 b0 b0 [9b] 9b 9b 9b 9b 9b 1f 1f 1f 1f 1f 1f 69 69 《= 69 69 69 a0 7a 7a 7a 7a 7a ff eb eb eb eb eb eb 16 16 16 16 16 16 81 81 81 81 81 81 7f 7f 7f 7f 7f 7f 57 57 57 57 57 57 e0 e0 e0 e0 e0 e0 94 94 94 94 94 00 35 35 35 35 35 35 98 98 98 98 98 00 7d 7d 7d 7d 7d 7d 6e 6e 6e 6e 6e 6e 59 59 59 59 59 59 8e 8e 8e 8e 8e 8e 6d 6d 6d 6d 6d 6d 69 69 69 69 69 69 d5 d5 d5 d5 d5 d5 63 63 63 63 63 63
0x9b總共有6個(gè),因此該memory區(qū)域的總長(zhǎng)為6×16=96,與上述提示一致。
[0x003d557e2c20,0x003d557e2c80) is a small unallocated heap chunk; size: 96
2.4 優(yōu)缺點(diǎn)
和ASAN相比,HWASAN具有如下缺點(diǎn):
可移植性較差,只適用于64位機(jī)器。
需要對(duì)Linux Kernel做一些改動(dòng)以支持工具。
對(duì)于所有錯(cuò)誤的檢測(cè)將有一定概率false negative(漏掉一些真實(shí)的錯(cuò)誤),概率為1/256。原因是tag的生成只能從256(2^8)個(gè)數(shù)中選一個(gè),因此不同地址的tag將有可能相同。
不過(guò)相對(duì)于這些缺點(diǎn),HWASAN所擁有的優(yōu)點(diǎn)更加引人注目:
不再需要安全區(qū)來(lái)檢測(cè)buffer overflow,既極大地降低了工具對(duì)于內(nèi)存的消耗,也不會(huì)出現(xiàn)ASAN中某些overflow檢測(cè)不到的情況。
不再需要隔離區(qū)來(lái)檢測(cè)UseAfterFree,因此不會(huì)出現(xiàn)ASAN中某些UseAfterFree檢測(cè)不到的情況。
2.5 一個(gè)難題
上述的討論其實(shí)回避了一個(gè)問(wèn)題:如果一個(gè)16字節(jié)的memory region中只有前幾個(gè)字節(jié)可尋址(假設(shè)是5),那么其對(duì)應(yīng)的shadow memory值也是5。這時(shí),如果用地址去訪(fǎng)問(wèn)該region的第2個(gè)字節(jié),那么如何判斷訪(fǎng)問(wèn)是否合規(guī)呢?
此時(shí)直接對(duì)比地址的tag和shadow memory的值肯定是不行的,因?yàn)榇藭r(shí)的shadow memory值含義發(fā)生了變化,它不再是一個(gè)類(lèi)似于tag的隨機(jī)值,而是memory region中可訪(fǎng)問(wèn)字節(jié)的數(shù)目。
為了解決這個(gè)難題,HWASAN在這種情況下將memory region的隨機(jī)值保存在最后一個(gè)字節(jié)中。所以即便地址的tag和shadow memory的值不等,但只要和memory region中最后一個(gè)字節(jié)相等,也表明該訪(fǎng)問(wèn)合法。
? ? ? ?責(zé)任編輯:pj
評(píng)論