AddressSanitizer使用介紹

1、關於AddressSanitizer

1.1 引言

也許很多人都聽說過類似這樣的一個故事:某公司的服務器每隔3個月就會毫無預兆的崩潰一次,怎麼也查不出原因,爲了避免崩潰可能引發的問題,只得每2個月手動重啓一次服務器。在這類有些靈異的事件背後,以內存泄露爲代表的一系列內存錯誤往往就是那個幕後黑手。


在計算機科學中,內存泄漏指由於疏忽或錯誤造成程序未能釋放已經不再使用的內存的情況。內存泄漏並非指內存在物理上的消失,而是應用程序分配某段內存後,由於設計錯誤,失去了對該段內存的控制,因而造成了內存的浪費。


內存泄漏會導致可用內存的數量減少從而降低計算機的性能。過多的可用內存被分配掉會導致全部或部分設備停止正常工作,或者應用程序崩潰。


其他的內存錯誤還包括緩衝區溢出、越界訪問等,此類錯誤輕則導致程序計算錯誤,重則引起程序崩潰,在內存緊張的嵌入式系統中尤爲致命,Google旗下的開源工具AddressSanitizer可以幫助我們檢測此類錯誤。


AddressSanitizer(ASan)是一個快速的內存錯誤檢測工具。它非常快,只拖慢程序兩倍左右。它包括一個編譯器instrumentation模塊和一個提供malloc()/free()替代項的運行時庫。從gcc 4.8開始,AddressSanitizer成爲gcc的一部分。


詳細瞭解AddressSanitizer信息可以訪問其github項目地址:
https://github.com/google/sanitizers/wiki/AddressSanitizer

 

1.2 AddressSanitizer原理簡介

AddressSanitizer主要包括兩部分:插樁(Instrumentation)和動態運行庫(Run-time library)。插樁主要是針對在llvm編譯器級別對訪問內存的操作(store,load,alloca等),將它們進行處理。動態運行庫主要提供一些運行時的複雜的功能(比如poison/unpoison shadow memory)以及將malloc,free等系統調用函數hook住。該算法的思路是:如果想防住Buffer Overflow漏洞,只需要在每塊內存區域右端(或兩端,能防overflow和underflow)加一塊區域(RedZone),使RedZone的區域的影子內存(Shadow Memory)設置爲不可寫即可。具體的示意圖如下圖所示。

 

 

內存映射

AddressSanitizer保護的主要原理是對程序中的虛擬內存提供粗粒度的影子內存(每8個字節的內存對應一個字節的影子內存),爲了減少overhead,採用了直接內存映射策略,所採用的具體策略如下:Shadow=(Mem >> 3) + offset。每8個字節的內存對應一個字節的影子內存,影子內存中每個字節存取一個數字k,如果k=0,則表示該影子內存對應的8個字節的內存都能訪問,如果0<k<7,表示前k個字節可以訪問,如果k爲負數,不同的數字表示不同的錯誤(e.g. Stack buffer overflow, Heap buffer overflow)。

 

插樁

爲了防止buffer overflow,需要將原來分配的內存兩邊分配額外的內存Redzone,並將這兩邊的內存加鎖,設爲不能訪問狀態,這樣可以有效的防止buffer overflow(但不能杜絕buffer overflow)。以下是在棧中插樁的一個例子。

 

未插樁的代碼:

 

插樁後的代碼:

 

插樁後的代碼:

在動態運行庫中將malloc/free函數進行了替換。在malloc函數中額外的分配了Redzone區域的內存,將與Redzone區域對應的影子內存加鎖,主要的內存區域對應的影子內存不加鎖。
free函數將所有分配的內存區域加鎖,並放到了隔離區域的隊列中(保證在一定的時間內不會再被malloc函數分配),可檢測Use after free類的問題。
詳細瞭解ASan算法原理可以訪問以下地址:
https://github.com/google/sanitizers/wiki/AddressSanitizerAlgorithm

 

2、使用方法

環境:Ubuntu 16.04   gcc 4.8以上
用-fsanitize=address選項編譯和鏈接你的程序;
用-fno-omit-frame-pointer編譯,以在錯誤消息中添加更好的堆棧跟蹤。
增加-O1以獲得更好的性能。
下面以簡單的測試代碼leaktest.c爲例

 

 

在終端中輸入以下命令編譯leaktest.c

 

 

運行leaktest,會打印下面的錯誤信息:

 

 

第一部分(ERROR),指出錯誤類型detected memory leaks;
第二部分給出詳細的錯誤信息:Direct leak of 80 byte(s) in 1 object(s),以及發生錯誤的對象名/源文件位置/行數;
第三部分是以上信息的一個總結(SUMMARY)。
 

3、設置檢測白名單

可以根據以下的需求設置ASan檢測白名單
忽略一個確定正確的函數以提高應用運行速度;
忽略一個特定功能的函數(例如:繞過框架邊界穿過線程的堆棧);
忽略已知的問題。

使用no_sanitize_address屬性(Clang3.3及GCC4.8以上版本支持)定義如下的宏命令:

 

4、AddressSanitizer能檢測的錯誤類型(以官方Wiki爲準)

文章原地址: https://www.bynav.com/cn/resource/bywork/healthy-work/70.html?id=35

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章