常德網(wǎng)站建設(shè)案例教程seo培訓(xùn)一對(duì)一
目錄
1、C++程序中的內(nèi)存問(wèn)題
2、AddressSanitizer是什么?
3、AddressSanitizer內(nèi)存檢測(cè)原理簡(jiǎn)述
3.1、內(nèi)存映射
3.2、插樁
4、為什么選擇AddressSanitizer?
4.1、Valgrind介紹
4.2、AddressSanitizer在速度和內(nèi)存方面為什么明顯優(yōu)于Valgrind
4.3、在很多實(shí)際項(xiàng)目中我們需要使用AddressSanitizer
5、無(wú)法使用Valgrind的具體項(xiàng)目實(shí)例
5.1、使用Valgrind檢測(cè)導(dǎo)致CPU占滿,無(wú)法進(jìn)行檢測(cè)
5.2、使用Valgrind檢測(cè)導(dǎo)致程序運(yùn)行過(guò)慢,無(wú)法進(jìn)行檢測(cè)
6、AddressSanitizer與其他內(nèi)存工具的比較
7、如何使用AddressSanitizer?
7.1、升級(jí)gcc版本
7.2、如何配置使用AddressSanitizer進(jìn)行內(nèi)存檢測(cè)
7.3、使用AddressSanitizer進(jìn)行內(nèi)存檢測(cè)的實(shí)例
7.4、使用AddressSanitizer的注意事項(xiàng)
8、Windows平臺(tái)高版本的Visual Studio也支持AddressSanitizer工具
VC++常用功能開(kāi)發(fā)匯總(專欄文章列表,歡迎訂閱,持續(xù)更新...)https://blog.csdn.net/chenlycly/article/details/124272585C++軟件異常排查從入門到精通系列教程(專欄文章列表,歡迎訂閱,持續(xù)更新...)
https://blog.csdn.net/chenlycly/article/details/125529931C++軟件分析工具從入門到精通案例集錦(專欄文章正在更新中...)
https://blog.csdn.net/chenlycly/article/details/131405795C/C++基礎(chǔ)與進(jìn)階(專欄文章,持續(xù)更新中...)
https://blog.csdn.net/chenlycly/category_11931267.html? ? ? ?C++程序中大部分問(wèn)題都是內(nèi)存問(wèn)題,有些可以快速定位,有些則很難排查,通過(guò)日志打印及代碼走讀很難定位,并且有些難纏的問(wèn)題只在客戶的環(huán)境中才會(huì)出現(xiàn)(在公司內(nèi)部測(cè)試環(huán)境中無(wú)法復(fù)現(xiàn)),處理起來(lái)非常頭疼,把人搞的精疲力竭后可能還查不出來(lái)。今天給大家介紹來(lái)自Google的強(qiáng)大C/C++內(nèi)存檢測(cè)工具AddressSanitizer,它可以很好地解決實(shí)際應(yīng)用環(huán)境中很多無(wú)法快速定位的內(nèi)存問(wèn)題。
1、C++程序中的內(nèi)存問(wèn)題
? ? ? ?在C++程序中,大部分程序運(yùn)行異常都是內(nèi)存問(wèn)題引起的,內(nèi)存問(wèn)題也是最讓C++程序員頭疼的事情。常見(jiàn)的內(nèi)存異常有空指針、野指針、線程棧溢出、內(nèi)存越界(棧內(nèi)存越界、堆內(nèi)存越界和全局內(nèi)存越界)、堆內(nèi)存被破壞、內(nèi)存泄漏、虛擬內(nèi)存不足等,如下所示:
其中某些場(chǎng)景下的內(nèi)存越界問(wèn)題以及堆內(nèi)存被破壞問(wèn)題最為難查,特別是堆內(nèi)存被破壞問(wèn)題。一般堆內(nèi)存被破壞會(huì)表現(xiàn)為,程序到處胡亂崩潰,一會(huì)崩在這里,一會(huì)崩在那里!因?yàn)槎褍?nèi)存被破壞,一會(huì)崩潰在new的地方,一會(huì)崩潰在delete的地方!
? ? ? ? 關(guān)于C++軟件異常及內(nèi)存錯(cuò)誤的詳細(xì)說(shuō)明,我就不在此贅述了,感興趣的,可以去看看我之前寫(xiě)的文章:
C++軟件異常分析概述https://blog.csdn.net/chenlycly/article/details/123991269引發(fā)C++程序內(nèi)存錯(cuò)誤的常見(jiàn)原因分析與總結(jié)
https://blog.csdn.net/chenlycly/article/details/128599525
2、AddressSanitizer是什么?
? ? ? ?AddressSanitizer(簡(jiǎn)稱ASan)是google提供的一款面向C/C++語(yǔ)言的內(nèi)存錯(cuò)誤問(wèn)題檢查工具,它可以檢測(cè)出堆溢出(Heap buffer overflow)、棧溢出(Stack buffer overflow)、全局變量越界(Global buffer overflow)、已釋放內(nèi)存使用(Use after free )、初始化順序(Initialization order bugs)、內(nèi)存泄漏(Use after free )等多個(gè)內(nèi)存問(wèn)題。
AddressSanitizer項(xiàng)目地址:AddressSanitizer · google/sanitizers Wiki · GitHub
參考文檔頁(yè)面:AddressSanitizerAlgorithm · google/sanitizers Wiki · GitHub
? ? ? ?AddressSanitizer相對(duì)于Valgrind要快很多,只拖慢程序兩倍左右。它包括一個(gè)編譯器instrumentation插樁模塊和一個(gè)提供malloc/free替代項(xiàng)的運(yùn)行時(shí)庫(kù)。從gcc 4.8開(kāi)始,AddressSanitizer成為gcc的一部分,使用時(shí)非常方便,只需要在編譯時(shí)指定編譯選項(xiàng)就可以了。gcc 4.8自帶的AddressSanitizer還不完善,有明顯的缺陷(比如當(dāng)監(jiān)測(cè)到任何一個(gè)error,它就會(huì)強(qiáng)制退出主程序,導(dǎo)致程序無(wú)法繼續(xù)運(yùn)行,再比如沒(méi)有符號(hào)信息),最好使用gcc 4.9及以上版本。
AddressSanitizer原先只支持Linux,現(xiàn)在也可以在Windows上使用了。微軟在Visual Studio 2019的16.9版本中引入了強(qiáng)大的內(nèi)存分析工具AddressSanitizer。
3、AddressSanitizer內(nèi)存檢測(cè)原理簡(jiǎn)述
? ? ? ? AddressSanitizer主要由兩部分組成:一個(gè)是靜態(tài)插樁(Instrumentation)模塊,將內(nèi)存訪問(wèn)判斷的邏輯直接插入在了二進(jìn)制中,保證了檢測(cè)邏輯的執(zhí)行速度;另一部分則是運(yùn)行時(shí)庫(kù)(Run-time library),提供部分功能的開(kāi)啟、報(bào)錯(cuò)函數(shù)和 malloc/free/memcpy 等函數(shù)的ASan檢測(cè)版本。
? ? ? ?instrument靜態(tài)插樁模塊,對(duì)棧上對(duì)象、全局對(duì)象、動(dòng)態(tài)分配的對(duì)象分配redzone,以及針對(duì)這些內(nèi)存做訪問(wèn)檢測(cè)。
? ? ? ?runtime 運(yùn)行時(shí)庫(kù)提供了一些運(yùn)行時(shí)的復(fù)雜的功能(比如poison/unpoison shadow memory),替換 malloc/free/memcpy/memset等實(shí)現(xiàn),提供報(bào)錯(cuò)函數(shù),針對(duì)每一次內(nèi)存讀寫(xiě),編譯器都會(huì)插入判斷邏輯,判斷地址是否被投毒(poisoned)。
該算法的思路是,如果要防住Buffer Overflow漏洞,只需要在每塊內(nèi)存區(qū)域右端(或兩端,能防overflow和underflow)加一塊區(qū)域(RedZone),使RedZone的區(qū)域的影子內(nèi)存(Shadow Memory)設(shè)置為不可寫(xiě)即可。
3.1、內(nèi)存映射
? ? ? ?AddressSanitizer保護(hù)的主要原理是對(duì)程序中的虛擬內(nèi)存提供粗粒度的影子內(nèi)存(每8個(gè)字節(jié)的內(nèi)存對(duì)應(yīng)一個(gè)字節(jié)的影子內(nèi)存),為了減少overhead,采用了直接內(nèi)存映射策略,所采用的具體策略如下:Shadow = (Mem >> 3) + offset。每8個(gè)字節(jié)的內(nèi)存對(duì)應(yīng)一個(gè)字節(jié)的影子內(nèi)存:
?影子內(nèi)存中每個(gè)字節(jié)存取一個(gè)數(shù)字k,如果k=0,則表示該影子內(nèi)存對(duì)應(yīng)的8個(gè)字節(jié)的內(nèi)存都能訪問(wèn),如果0<k<7,表示前k個(gè)字節(jié)可以訪問(wèn),如果k為負(fù)數(shù),不同的數(shù)字表示不同的錯(cuò)誤(e.g. Stack buffer overflow, Heap buffer overflow)。
3.2、插樁
? ? ? ?為了防止buffer overflow,需要將原來(lái)分配的內(nèi)存兩邊分配額外的內(nèi)存Redzone,并將這兩邊的內(nèi)存加鎖,設(shè)為不能訪問(wèn)狀態(tài),這樣可以有效的防止buffer overflow(但不能杜絕buffer overflow)。插樁的簡(jiǎn)化示意圖如下:
?? ? ? ? 以下是在棧中插樁的一個(gè)例子:
1)未插樁的代碼:
void foo()?
{char a[8];// ...return;
}
2)插樁后的代碼:
char redzone1[32]; // 32-byte aligned
char a[8]; ? ? ? ? // 32-byte aligned
char redzone2[24];
char redzone3[32]; // 32-byte aligned
int*shadow_base = MemToShadow(redzone1);
shadow_base[e] = oxffffffff;// poison redzone1
shadow_base[1] = oxffffffe0;// poison redzone2,unpoison 'a'
shadow_base[2] = oxffffffff;// poison redzone3
// ...
return;
? ? ? ?在動(dòng)態(tài)運(yùn)行庫(kù)中將malloc/free函數(shù)進(jìn)行了替換。在malloc函數(shù)中額外的分配了Redzone區(qū)域的內(nèi)存,將與Redzone區(qū)域?qū)?yīng)的影子內(nèi)存加鎖,主要的內(nèi)存區(qū)域?qū)?yīng)的影子內(nèi)存不加鎖。free函數(shù)將所有分配的內(nèi)存區(qū)域加鎖,并放到了隔離區(qū)域的隊(duì)列中(保證在一定的時(shí)間內(nèi)不會(huì)再被malloc函數(shù)分配),可檢測(cè)Use after free類的問(wèn)題。
4、為什么選擇AddressSanitizer?
? ? ? ?Linux平臺(tái)上常用的內(nèi)存分析工具主要有Valgrind和AddressSanitizer,這兩個(gè)工具在使用方式上有一定的區(qū)別。Valgrind不需要重新編譯代碼,可以直接附加到程序上對(duì)內(nèi)存進(jìn)行監(jiān)測(cè);AddressSanitizer則需要重新編譯代碼。所以,很多時(shí)候大家為了圖方便,會(huì)優(yōu)先使用Valgrind。
? ? ? ?但Valgrind會(huì)占用大量?jī)?nèi)存并明顯拖慢程序運(yùn)行的速度,這使得在部分場(chǎng)景下無(wú)法正常使用Valgrind。而AddressSanitizer在運(yùn)行速度和效率上要比Valgrind好很多,所以在Valgrind無(wú)法完成檢測(cè)時(shí)可以選擇AddressSanitizer。
4.1、Valgrind介紹
? ? ? ?Valgrind是一套Linux下開(kāi)放源代碼(GPL V2)的仿真調(diào)試工具的集合,是運(yùn)行在Linux 上的多用途代碼分析和內(nèi)存調(diào)試常用工具。Valgrind由內(nèi)核(core)以及基于內(nèi)核的其他調(diào)試工具組成。內(nèi)核類似于一個(gè)框架(framework),它模擬了一個(gè)CPU環(huán)境,并提供服務(wù)給其他工具;而其他工具則類似于插件 (plug-in),利用內(nèi)核提供的服務(wù)完成各種特定的內(nèi)存調(diào)試任務(wù)。
? ? ? ?Valgrind是基于仿真的方式對(duì)程序進(jìn)行調(diào)試,它先于應(yīng)用程序獲取實(shí)際處理器的控制權(quán),并在實(shí)際處理器的基礎(chǔ)上仿真一個(gè)虛擬處理器,并使應(yīng)用程序運(yùn)行于這個(gè)虛擬處理器之上,從而對(duì)應(yīng)用程序的運(yùn)行進(jìn)行監(jiān)視。
? ? ? ?應(yīng)用程序并不知道該處理器是虛擬的還是實(shí)際的,已經(jīng)編譯成二進(jìn)制代碼的應(yīng)用程序并不用重新進(jìn)行編譯,Valgrind 直接解釋二進(jìn)制代碼使得應(yīng)用程序基于它運(yùn)行,從而能夠檢查內(nèi)存操作時(shí)可能出現(xiàn)的錯(cuò)誤。所以,在Valgrind下運(yùn)行的程序運(yùn)行速度要慢的多,而且使用的內(nèi)存比目標(biāo)程序要多的多,這也是Valgrind的一大劣勢(shì),這也導(dǎo)致部分場(chǎng)合下沒(méi)法使用Valgrind去分析。
4.2、AddressSanitizer在速度和內(nèi)存方面為什么明顯優(yōu)于Valgrind
? ? ? ?Valgrind采用的是二進(jìn)制完全映射的影子內(nèi)存技術(shù),會(huì)占用更多內(nèi)存才能去有效地監(jiān)測(cè)內(nèi)存變化。并且開(kāi)啟Valgrind監(jiān)測(cè)之后,會(huì)嚴(yán)重降速,比如使用memcheck工具去監(jiān)測(cè)內(nèi)存,基本上是10到30倍的降速,明顯的降速會(huì)導(dǎo)致我們的軟件在業(yè)務(wù)上出現(xiàn)不可用的情況。關(guān)于降速,Valgrind官網(wǎng)上有著詳細(xì)的說(shuō)明:
The main one is that programs run significantly more slowly under Valgrind. Depending on which tool you use, the slowdown factor can range from 5-100. Memcheck runs programs about 10-30x slower than normal.
? ? ? ?而Google提供的內(nèi)存檢測(cè)工具AddressSanitizer在內(nèi)存占用和運(yùn)行速度方面有著卓越的表現(xiàn),相比于Valgrind,AddressSanitizer的優(yōu)勢(shì)相當(dāng)明顯。AddressSanitizer采用了一種取巧的影子內(nèi)存玩法,將虛擬地址空間的1/8分配給它的影子內(nèi)存,并使用一個(gè)帶有比例和偏移量的直接映射將一個(gè)應(yīng)用程序地址轉(zhuǎn)換為它相應(yīng)的影子地址,確保了少量?jī)?nèi)存就能完成一個(gè)程序的監(jiān)測(cè)。并且AddressSanitizer降速也比較少。AddressSanitizer在內(nèi)存占用和降速方面,通過(guò)USENIX高等計(jì)算機(jī)系統(tǒng)協(xié)會(huì)某篇論文中的一段描述可以佐證:
We present AddressSanitizer, a new tool that combines performance and coverage. AddressSanitizer finds out-of-bounds accesses (for heap, stack, and global objects) and uses of freed heap memory at the relatively low cost of 73% slowdown,1.5x-4x memory overhead,making it a good choice for testing a wide range of C/C++ applications.
4.3、在很多實(shí)際項(xiàng)目中我們需要使用AddressSanitizer
? ? ? ?Valgrind采用的是二進(jìn)制完全映射的影子內(nèi)存技術(shù),會(huì)占用更多內(nèi)存才能去有效地監(jiān)測(cè)內(nèi)存變化,還會(huì)明顯地拖慢程序的運(yùn)行速度,可能會(huì)導(dǎo)致程序在收到請(qǐng)求后不能及時(shí)的響應(yīng),沒(méi)法模擬出真實(shí)運(yùn)行時(shí)的場(chǎng)景,可能就不一定能復(fù)現(xiàn)問(wèn)題,甚至還會(huì)因?yàn)檫\(yùn)行速度過(guò)慢導(dǎo)致程序根本無(wú)法正常的運(yùn)轉(zhuǎn)。所以我們有時(shí)需要使用占用內(nèi)存少、運(yùn)行速度更快的AddressSanitizer。
5、無(wú)法使用Valgrind的具體項(xiàng)目實(shí)例
?? ? ? ?在實(shí)際項(xiàng)目中我們遇到過(guò)不少無(wú)法使用Valgrind的場(chǎng)景。如果沒(méi)有內(nèi)存檢測(cè)工具,排查起來(lái)效率非常低,僅僅通過(guò)打印日志和走讀代碼很難定位問(wèn)題。如果有內(nèi)存檢測(cè)工具,可能很快就能定位出來(lái)。所以后來(lái)轉(zhuǎn)向使用AddressSanitizer,很多Valgrind無(wú)法工作的場(chǎng)景,AddressSanitizer都可以勝任。與Valgrind相比,AddressSanitizer的運(yùn)行速度是真的快,同時(shí)內(nèi)存錯(cuò)誤的檢測(cè)能力也非常強(qiáng)。
5.1、使用Valgrind檢測(cè)導(dǎo)致CPU占滿,無(wú)法進(jìn)行檢測(cè)
? ? ? ?某客戶現(xiàn)場(chǎng)我們的程序出現(xiàn)了內(nèi)存異常問(wèn)題,最先使用Valgrind進(jìn)行檢測(cè),發(fā)現(xiàn)使用Valgrind檢測(cè)時(shí)機(jī)器的CPU一直是100%,直接導(dǎo)致程序業(yè)務(wù)無(wú)法正常工作,由于業(yè)務(wù)無(wú)法運(yùn)轉(zhuǎn),導(dǎo)致我們沒(méi)有辦法讓程序跑到存在問(wèn)題的流程,所以檢測(cè)也就無(wú)法實(shí)施了。
5.2、使用Valgrind檢測(cè)導(dǎo)致程序運(yùn)行過(guò)慢,無(wú)法進(jìn)行檢測(cè)
? ? ? ?某客戶現(xiàn)場(chǎng)出現(xiàn)了多線程死鎖,使用gdb附加到目標(biāo)程序上調(diào)試運(yùn)行,發(fā)現(xiàn)某個(gè)公用模塊每次都會(huì)檢測(cè)到“它管理的堆的魔數(shù)被破壞”,于是強(qiáng)制退出了。由于該公用模塊中內(nèi)存管理器使用的地方比較多,包括上層業(yè)務(wù)代碼和底層庫(kù),通過(guò)走讀代碼去分析哪些地方分配了堆內(nèi)存很難實(shí)施。于是我們使用Valgrind分析,但因?yàn)樗俣冗^(guò)慢程序沒(méi)法運(yùn)轉(zhuǎn)起來(lái),內(nèi)存檢測(cè)任務(wù)無(wú)法實(shí)施。
6、AddressSanitizer與其他內(nèi)存工具的比較
? ? ? ?AddressSanitizer與其他內(nèi)存檢測(cè)工具的比較如下所示:
Items \ Tools | AddressSanitizer | Valgrind/Memcheck | Dr. Memory | Mudflap | Guard Page | gperftools |
technology | CTI | DBI | DBI | CTI | Library | Library |
ARCH | x86,ARM,PPC,... | x86,ARM,PPC | x86 | all(?) | all(?) | all(?) |
OS | Linux, Mac, Windows, FreeBSD, Android | Linux, Mac | Windows, Linux | Linux, Mac(?) | All (1) | Linux, Windows |
Slowdown | 2x | 20x | 10x | 2x-40x | ? | ? |
Detects: | ||||||
Heap OOB | yes | yes | yes | yes | some | some |
Stack OOB | yes | no | no | some | no | no |
Global OOB | yes | no | no | ? | no | no |
UAF | yes | yes | yes | yes | yes | yes |
UAR | yes?(see?UseAfterReturn) | no | no | no | no | no |
UMR | no (see?MemorySanitizer) | yes | yes | ? | no | no |
Leaks | yes?(see?LeakSanitizer) | yes | yes | ? | no | yes |
上表中的相關(guān)名詞說(shuō)明如下:
DBI: dynamic binary instrumentation?
CTI: compile-time instrumentation?
UMR: uninitialized memory reads?
UAF: use-after-free (aka dangling pointer)?
UAR: use-after-return?
OOB: out-of-bounds?
x86: includes 32- and 64-bit.?
Guard Page: a family of memory error detectors (Electric fence?or?DUMA?on Linux, Page Heap on Windows, Guard Malloc in Mac)?gperftools: various performance tools/error detectors bundled with TCMalloc.?Heap checker?(leak detector) is only available on Linux.?Debug allocator?provides both guard pages and canary values for more precise detection of OOB writes, so it's better than guard page-only detectors.
7、如何使用AddressSanitizer?
?? ? ? ? 從gcc 4.8開(kāi)始,gcc才集成AddressSanitizer工具,所以要使用AddressSanitizer必須將gcc升級(jí)到4.8或以上版本。然后使用高版本gcc對(duì)代碼進(jìn)行重新編譯,在編譯時(shí)指定編譯選項(xiàng)就可以了。
7.1、升級(jí)gcc版本
? ? ? ?可以到ftp://gcc.gnu.org/pub/gcc上下載高版本的gcc,然后到執(zhí)行源碼樹(shù)中的contrib/download_prerequisites文件,它會(huì)下載和設(shè)置GCC編譯依賴的組件。然后在GCC源碼樹(shù)同級(jí)的目錄建立一個(gè)編譯目錄,比如叫build_dir,然后在該編譯目錄中執(zhí)行如下命令進(jìn)行編譯和安裝:
../src_dir/configure
make
make install
7.2、如何配置使用AddressSanitizer進(jìn)行內(nèi)存檢測(cè)
? ? ? ? AddressSanitizer是內(nèi)置在gcc中的,主要設(shè)置編譯參數(shù)去設(shè)定是否啟用AddressSanitizer的內(nèi)存檢測(cè)。
1)如果沒(méi)使用makefile,直接gcc命令去編譯,則在命令中添加-fsanitize=address選項(xiàng),如下:
gcc? -fsanitize=address? -fno-omit-frame-pointer? -O1? -g? use-after-free.c? -o? use-after-free
其中:
1)用-fsanitize=address選項(xiàng)編譯和鏈接你的程序。
2)用-fno-omit-frame-pointer編譯,以得到更容易理解stack trace。
3)可選擇-O1或者更高的優(yōu)化級(jí)別編譯
2)如果使用makefile,則在編譯選項(xiàng)CFLAGS和鏈接選項(xiàng)LDFLAGS中都要添加-fsanitize=address選項(xiàng),如下:
#都要追加-fsanitize=address開(kāi)關(guān)
CFLAGS+=-fsanitize=address?
LDFLAGS+=-fsanitize=address
7.3、使用AddressSanitizer進(jìn)行內(nèi)存檢測(cè)的實(shí)例
? ? ? ? ?比如下面的代碼中,分配array數(shù)組并釋放,然后返回它的一個(gè)元素,返回了一個(gè)已經(jīng)釋放了的內(nèi)存地址:
int main (int argc, char** argv)
{int* array = new int[100];delete []array;return array[1];
}
上述代碼放置在use-after-free.c中,直接使用gcc編譯該文件即可,命令如下:
gcc? -fsanitize=address? -fno-omit-frame-pointer? -O1? -g? use-after-free.c? -o? use-after-free
然后,運(yùn)行use-after-fee,AddressSanitizer檢測(cè)了錯(cuò)誤,就會(huì)打印出下面的信息:
==3189==ERROR: AddressSanitizer: heap-use-after-free on address 0x61400000fe44?
at pc 0x0000004008f1 bp 0x7ffc9b6e2630 sp 0x7ffc9b6e2620
READ of size 4 at 0x61400000fe44 thread T0#0 0x4008f0 in main /home/ron/dev/as/use_after_free.cpp:9#1 0x7f3763aa882f in __libc_start_main (/lib/x86_64-linux-gnu/libc.so.6+0x2082f)#2 0x4007b8 in _start (/home/ron/dev/as/build/use_after_free+0x4007b8)0x61400000fe44 is located 4 bytes inside of 400-byte region [0x61400000fe40,0x61400000ffd0)
freed by thread T0 here:#0 0x7f3763ef1caa in operator delete[](void*) (/usr/lib/x86_64-linux-gnu/libasan.so.2+0x99caa)#1 0x4008b5 in main /home/ron/dev/as/use_after_free.cpp:8#2 0x7f3763aa882f in __libc_start_main (/lib/x86_64-linux-gnu/libc.so.6+0x2082f)previously allocated by thread T0 here:#0 0x7f3763ef16b2 in operator new[](unsigned long) (/usr/lib/x86_64-linux-gnu/libasan.so.2+0x996b2)#1 0x40089e in main /home/ron/dev/as/use_after_free.cpp:7#2 0x7f3763aa882f in __libc_start_main (/lib/x86_64-linux-gnu/libc.so.6+0x2082f)SUMMARY: AddressSanitizer: heap-use-after-free /home/ron/dev/as/use_after_free.cpp:9 main
如上圖,打印出的信息主要分三部分:
1)ERROR部分:指出錯(cuò)誤類型是heap-use-after-free;
2)READ部分:指出線程名thread T0,操作為READ,發(fā)生的位置是use-after-free.c:9(行號(hào))。
該heapk塊之前已經(jīng)在use-after-free.c:8(行號(hào))被釋放了;
該heap塊是在use-fater-free.c:7(行號(hào))分配的。
3)SUMMARY部分:前面輸出的概要說(shuō)明。
7.4、使用AddressSanitizer的注意事項(xiàng)
? ? ? ?使用AddressSanitizer過(guò)程中可能會(huì)遇到一些問(wèn)題,此處給大家講幾個(gè)注意事項(xiàng)。
1) 如果存在第三方內(nèi)存管理器,可能需要取消對(duì)第三方管理器的依賴
? ? ? ?如果存在第三方內(nèi)存管理器(比如tcmalloc),需要去掉第三方內(nèi)存管理器的編譯選項(xiàng)或連接選項(xiàng),因?yàn)閮?nèi)存管理器分配的內(nèi)存自身會(huì)預(yù)留一些管理空間,越界不多只寫(xiě)到這部分空間時(shí),AddressSanitizer越界檢測(cè)是不會(huì)認(rèn)為它是異常的(因?yàn)樗鼈內(nèi)匀皇怯脩舴峙涞姆秶畠?nèi)的,第三方內(nèi)存管理器,與應(yīng)用程序?qū)τ贏San沒(méi)有差異的)。同時(shí)內(nèi)存管理器通常會(huì)延遲釋放內(nèi)存,這也會(huì)影響檢測(cè)的及時(shí)性。此外,如果鏈接時(shí)提示ASan中的符號(hào)找不到時(shí)給程序顯示添加對(duì)libasan庫(kù)的連接(默認(rèn)在/usr/local/lib目錄,找不時(shí)使用find命令找下)。
2)內(nèi)存不足問(wèn)題
? ? ? ?內(nèi)存檢測(cè)工具會(huì)增加程序的內(nèi)存消耗,32位程序地址空間只有4G,用戶態(tài)的通常只有3G,如果程序跑起來(lái)之后提示無(wú)法分配內(nèi)存,可以通過(guò)設(shè)置如下兩個(gè)選項(xiàng)緩解一下:
export ASAN_OPTIONS=quarantine_size_mb=256:start_deactivated=1。
其中:
quarantine_size_mb設(shè)置小點(diǎn),犧牲使用已經(jīng)釋放了的內(nèi)存問(wèn)題的檢測(cè)能力。
start_deactivated設(shè)置為1,啟動(dòng)時(shí)不會(huì)加載asan的全部功能,用于節(jié)省內(nèi)存。
上面的選項(xiàng)只是緩解,根本的解決之道還是要開(kāi)發(fā)64位版本的程序。
3)錯(cuò)誤忽略
? ? ? ? 有些錯(cuò)誤我們改動(dòng)不了或直接認(rèn)為絕對(duì)安全,也可以在函數(shù)上面添加屬性,進(jìn)行錯(cuò)誤忽略。比如:
#if defined(__clang__) || defined (__GNUC__)
# define ATTRIBUTE_NO_SANITIZE_ADDRESS __attribute__((no_sanitize_address))
#else
# define ATTRIBUTE_NO_SANITIZE_ADDRESS
#endif
...
ATTRIBUTE_NO_SANITIZE_ADDRESS
void ThisFunctionWillNotBeInstrumented() {...}
8、Windows平臺(tái)高版本的Visual Studio也支持AddressSanitizer工具
? ? ? ?AddressSanitizer工具原先只支持Linux,現(xiàn)在也可以在Windows上使用了。微軟在Visual Studio 2019的16.9版本們引入了AddressSanitizer,在安裝Visual Studio 2019的16.9版本及以后的版本時(shí),會(huì)默認(rèn)安裝AddressSanitizer工具:(默認(rèn)勾選“C++ AddressSanitizer”)
? ? ? ?對(duì)于如何在VS中如何使用AddressSanitizer內(nèi)存分析工具,可以看一下微軟官方文章的詳細(xì)說(shuō)明:
在Visual Studio中使用AddressSanitizerhttps://docs.microsoft.com/zh-cn/cpp/sanitizers/asan?view=msvc-170此處我就不詳細(xì)展開(kāi)了,大家需要使用的話,可以去詳細(xì)研究一下。