AddressSanitizer (ASan) 是一項基於編譯器的儀器測試功能,可在運行時檢測 C/C++ 代碼中的多種內存錯誤。在 Android 中,已經測試了對下列內存錯誤類型的檢查功能:
Android 可通過 ASan 執行全面的構建儀器測試,還可以通過 asanwrapper 執行應用級的 ASan 儀器測試。關於這兩種儀器測試技巧的說明均可在 source.android.com 中找到。
AddressSanitizer 基於以下兩個高級概念。第一個概念是針對與內存有關的所有函數調用(包括 alloca、malloc 和 free 等)執行儀器測試並輸出用於跟蹤內存分配、釋放和使用情況統計的信息。通過此儀器測試,ASan 可檢測無效的內存使用錯誤,包括重複釋放、範圍後使用、返回後使用和釋放後使用等錯誤。ASan 還可以檢測在定義的內存區域邊界外發生的讀寫操作。為完成此檢測,它填充所有分配的內存緩衝區和變量。如果對此填充區域進行讀或寫,ASan 將捕獲此操作,並輸出有助於診斷內存違例的信息。在 ASan 術語中,此填充被稱為中毒內存。
下面是包含堆疊分配變量的中毒內存填充布局示例:
▲ ASANified 堆疊變量示例,此變量包含一個由 8 個元素組成的 int8_t 數組、一個 uint32_t 數組和一個由 16 個元素組成的 int8_t 數組。右側顯示使用 ASAN 編譯後的內存布局,其中每個變量之間插入填充。對於每個堆棧變量,變量前後有 32 個填充字節。如果一個變量的對象大小不是 32 個字節,則插入 32 - n 個額外的填充字節,其中 n 是對象大小。
ASan 使用影子內存跟蹤哪些字節為正常內存,哪些字節為中毒內存。字節可以標記為完全正常(在影子內存中標記為 0)、完全中毒(設置對應影子字節的高位)或前面 k 個字節未中毒(影子字節值為 k)。如果影子內存顯示某個字節中毒,則 ASan 會使程序崩潰,並輸出有用的調試信息,包括調用堆棧、影子內存映射、內存違例類型、讀取或寫入的內容、導致違例的計算機以及內存內容。
AddressSanitizer: heap-buffer-overflow on address 0xe6146cf3 at pc 0xe86eeb3c bp 0xffe67348 sp 0xffe66f14WRITE of size 39 at 0xe6146cf3 thread T0 #0 0xe86eeb3b (/system/lib/libclang_rt.asan-arm-android.so+0x64b3b) #1 0xaddc5d27 (/data/simple_test_fuzzer+0x4d27) #2 0xaddd08b9 (/data/simple_test_fuzzer+0xf8b9) #3 0xaddd0a97 (/data/simple_test_fuzzer+0xfa97) #4 0xaddd0fbb (/data/simple_test_fuzzer+0xffbb) #5 0xaddd109f (/data/simple_test_fuzzer+0x1009f) #6 0xaddcbfb9 (/data/simple_test_fuzzer+0xafb9) #7 0xaddc9ceb (/data/simple_test_fuzzer+0x8ceb) #8 0xe8655635 (/system/lib/libc.so+0x7a635)0xe6146cf3 is located 0 bytes to the right of 35-byte region [0xe6146cd0,0xe6146cf3)allocated by thread T0 here: #0 0xe87159df (/system/lib/libclang_rt.asan-arm-android.so+0x8b9df) #1 0xaddc5ca7 (/data/simple_test_fuzzer+0x4ca7) #2 0xaddd08b9 (/data/simple_test_fuzzer+0xf8b9)SUMMARY: AddressSanitizer: heap-buffer-overflow (/system/lib/libclang_rt.asan-arm-android.so+0x64b3b) Shadow bytes around the buggy address: 0x1cc28d40: fa fa 00 00 00 00 07 fa fa fa fd fd fd fd fd fd 0x1cc28d50: fa fa 00 00 00 00 07 fa fa fa fd fd fd fd fd fd 0x1cc28d60: fa fa 00 00 00 00 00 02 fa fa fd fd fd fd fd fd 0x1cc28d70: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa 0x1cc28d80: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa=>0x1cc28d90: fa fa fa fa fa fa fa fa fa fa 00 00 00 00[03]fa 0x1cc28da0: fa fa 00 00 00 00 07 fa fa fa 00 00 00 00 03 fa 0x1cc28db0: fa fa fd fd fd fd fd fa fa fa fd fd fd fd fd fa 0x1cc28dc0: fa fa 00 00 00 00 00 02 fa fa fd fd fd fd fd fd 0x1cc28dd0: fa fa 00 00 00 00 00 02 fa fa fd fd fd fd fd fd 0x1cc28de0: fa fa 00 00 00 00 00 02 fa fa fd fd fd fd fd fdShadow byte legend (one shadow byte represents 8 application bytes): Addressable: 00 Partially addressable: 01 02 03 04 05 06 07 Heap left 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
有關報告各個部分的含義以及如何提高其易讀性的更多信息,可查看 LLVM 網站:
https://clang.llvm.org/docs/AddressSanitizer.html
和 Github:
https://github.com/google/sanitizers/wiki/AddressSanitizer
有時,錯誤發現過程可能無法確定問題所在,當錯誤需要特殊設置或更高級的技巧(例如堆填充或利用爭用條件)才能發現時,更是如此。其中許多錯誤並不能即時發現,可能需要檢查數千條指令才能找到內存違例的真正原因所在。ASan 可針對所有與內存有關的函數執行儀器測試並為必須觸發 ASan 相關回調才可訪問的區域填充數據,可在發生內存違例時立即捕獲違例,而不是等待崩潰導致數據損壞。這對於錯誤發現和根源診斷極為有用。此外,ASAN 還是一個非常有用的模糊測試工具,一直用於 Android 上的各種模糊測試工作。