如何利用Sanitizer解決Android中的Bug?
文 / Google Android 安全團隊 Dan Austin
LLVM 是構建 Android 所使用的編譯器基礎架構,包含多個用於執行靜態和動態分析的元件。在分析 Android 時,得到廣泛使用的一組元件是擦除器,特別是 AddressSanitizer、UndefinedBehaviorSanitizer 和 SanitizerCoverage。
這些擦除器是包含在 compiler-rt 中的基於編譯器的儀器測試元件,可在開發和測試過程中用於消除錯誤和優化 Android。Android 中當前提供的這些擦除器可以發現和診斷多種記憶體濫用錯誤和未定義的行為,還可以提供程式碼覆蓋指標,確保您的測試套件儘可能面面俱到。
本文將詳細介紹現有 Android 擦除器(AddressSanitizer、UndefinedBehaviorSanitizer 和 SanitizerCoverage)的內部結構,並演示可以如何在 Android 構建系統中使用它們。
Address Sanitizer
AddressSanitizer (ASan) 是一項基於編譯器的儀器測試功能,可在執行時檢測 C/C++ 程式碼中的多種記憶體錯誤。在 Android 中,已經測試了對下列記憶體錯誤型別的檢查功能:
堆、堆疊和全域性變數的越界訪問
釋放後使用
返回後使用(執行時標誌 ASAN_OPTIONS=detect_stack_use_after_return=1)
範圍後使用(clang 標誌 -fsanitize-address-use-after-scope)
重複釋放,無效釋放
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 會使程式崩潰,並輸出有用的除錯資訊,包括呼叫堆疊、影子記憶體對映、記憶體違例型別、讀取或寫入的內容、導致違例的計算機以及記憶體內容。
有關報告各個部分的含義以及如何提高其易讀性的更多資訊,可檢視 LLVM 網站:
https://clang.llvm.org/docs/AddressSanitizer.html
和 Github:
https://github.com/google/sanitizers/wiki/AddressSanitizer
有時,錯誤發現過程可能無法確定問題所在,當錯誤需要特殊設定或更高階的技巧(例如堆填充或利用爭用條件)才能發現時,更是如此。其中許多錯誤並不能即時發現,可能需要檢查數千條指令才能找到記憶體違例的真正原因所在。ASan 可針對所有與記憶體有關的函式執行儀器測試併為必須觸發 ASan 相關回撥才可訪問的區域填充資料,可在發生記憶體違例時立即捕獲違例,而不是等待崩潰導致資料損壞。這對於錯誤發現和根源診斷極為有用。此外,ASAN 還是一個非常有用的模糊測試工具,一直用於 Android 上的各種模糊測試工作。
UBSan
UndefinedBehaviorSanitizer (UBSan) 執行編譯時儀器測試,檢查各種未定義的行為。裝置製造商可通過將 LOCAL_SANITIZE:=default-ub 包含到生成檔案或將 default-ub: true 包含到 blueprint 檔案的 sanitize 塊中,將 UBSan 加入其測試構建中。UBSan 可以檢測多種未定義的行為,而 Android 的構建系統直接支援:
bool
integer-divide-by-zero
return
returns-nonnull-attribute
shift-exponent
unreachable
vla-bound
Android 的構建系統還使用了 UBSan 的整數溢位檢查功能。UBSan 還支援 unsigned-integer-overflow,這不是嚴格意義上的未定義行為,但它包含在擦除器中。在生成檔案中,可以將 LOCAL_SANITIZE 設定為 signed-integer-overflow、unsigned-integer-overflow 或 combination flag integer,啟用 signed-integer-overflow、unsigned-integer-overflow、integer-divide-by-zero、shift-base 和 shift-exponent,以啟用這些行為。在 blueprint 檔案中,可以將 Misc_undefined 設定為所需的標誌,啟用這些行為。這些 UBSan 目標,尤其是 unsigned-integer-overflow,廣泛用於 mediaserver 元件中,以用來消除任何潛在的整數溢位漏洞。
在 Android 中,當出現未定義的行為時,預設的做法是中止程式。但是,從 2016 年 10 月開始,Android 中的 UBSan 將提供一個可選的執行時庫,其報告的錯誤資訊將更加詳細,包括出現的未定義行為型別、檔案和原始碼行資訊。
在 Android.mk 檔案中,可通過以下方式啟用該庫:
而在 Android.bp 檔案中,可通過以下方式啟用該庫:
下面是 UBSan 執行時庫提供的資訊示例:
SanitizerCoverage
Sanitizer 工具內建一個非常簡單的程式碼覆蓋工具。SanitizerCoverage 可實現呼叫級、基本塊級和邊緣級的程式碼覆蓋。此 Sanitizer 可用作獨立的儀器測試工具,也可與其他任何擦除器配合使用,包括 AddressSanitizer 和 UndefinedBehaviorSanitizer 等。要使用這一基於 Guard 的新覆蓋工具,請設定 fsanitize-coverage=trace-pc-guard。編譯器將在每個邊緣插入 __sanitizer_cov_trace_pc_guard(&guard_variable)。每個邊緣均有各自的 uint32_t guard_variable。
此外,還會生成模組建構函式,即 __sanitizer_cov_trace_pc_guard_init(uint32_t* start, uint32_t* stop)。所有 __sanitizer_cov_ 函式均應由使用者提供。您可以按照使用 Guard 跟蹤計算機中的示例操作:
https://clang.llvm.org/docs/SanitizerCoverage.html#tracing-pcs-with-guards
除了跟蹤控制流外,SanitizerCoverage 還可以跟蹤資料流。此功能可通過 fsanitize-coverage=trace-cmp 啟用,並通過 __sanitizer_cov_trace_* 函式檢測所有開關和比較指令來實現。對於整數除法和 GEP 指令,也存在類似的功能,可分別通過 fsanitize-coverage=trace-div 和 fsanitize-coverage=trace-gep 啟用。這是一個實驗性介面,可能危及執行緒安全,可能隨時更改,但在 Android 構建中提供並可執行此介面。
在擦除器覆蓋會話期間,覆蓋資訊記錄在兩個檔案中,即 .sancov 檔案和 sancov.map 檔案。前一個檔案包含程式的所有儀器測試點,而後一個檔案包含在前一個檔案中用一系列索引表示的執行跟蹤。預設情況下,這兩個檔案儲存在當前工作目錄中,系統將為執行過程中執行的每個可執行的共享物件建立一個目錄。
結論
ASan、UBSan 和 SanitizerCoverage 僅僅是 LLVM 擦除器在 Android 中應用的開端。目前,正在向 Android 構建系統中整合更多的 LLVM 擦除器。本文所介紹的擦除器可用作程式碼執行狀況和系統穩定性檢測工具,Android 安全團隊甚至正在用它們來發現和預防安全錯誤!
檢視全文及文中連結,請點選文末“閱讀原文”。
推薦閱讀:
相關文章
- 如何利用工具提高你的Android程式碼質量(Checkstyle、Findbugs、PMD)Android
- iOS 解決MJPhotoBrowser的bugiOS
- 如何利用限流解決遊戲陪玩app開發中的重複提交問題?遊戲APP
- 常見BUG解決
- Sanitizer:給你的DOM消消毒
- 如何利用TRIZ理論解決磁頭焊接工藝中的實際難題
- 奇怪的bug:解決 vue-cli中 proxyTable 配置無效Vue
- bug 11890804 的解決
- android ListView中CheckBox錯位的解決AndroidView
- BUG 解決記錄 一
- 解決 React 中的 input 輸入框在中文輸入法下的 bugReact
- IE6中的常見BUG與相應的解決辦法
- kewastUnPackStats(): oracle 11.2.0.1的bug解決方法ASTOracle
- 無限debugger的解決----(一)
- Android解決The APK file app-debug.apk does not exist on disk.AndroidAPKAPP
- Kali Linux 2017中Scapy執行bug解決Linux
- iview 酸爽debug: subMenu預設選中無效的解決方法View
- 解決了一例awk中substr處理漢字字串的bug字串
- 教你解決電量抽風的大Bug
- Entity Framework的預設值BUG解決方法Framework
- Hive常見的bug與解決辦法。Hive
- elementUI 的 input無法輸入bug解決UI
- Android Studio 除錯出現waiting for debugger的解決辦法Android除錯AI
- Android 中 View.setEnabled(false) 無效的解決方法AndroidViewFalse
- IE CSS Bug系列:IE6中Min-Height的解決辦法CSS
- bug的一生:軟體測試員,你是如何利用專業技術修復bug的?
- 如何快速解決網站中存在的Web漏洞?網站Web
- 線上出現bug解決用例
- 解決GAT首頁亂碼Bug
- Android利用Theme和Style解決APP啟動閃黑屏問題AndroidAPP
- 利用errorstack event解決問題Error
- 記錄一個前端bug的解決過程前端
- MacOS 不顯示 SD 卡的 bug 解決方法Mac
- 在使用SwipebackLayout出現的bug以及解決的方法
- Keil中 "STARTUP.A51" SET (SMALL) DEBUG EP問題解決方法
- 解決arcgis for android中feature不顯示的問題Android
- 解決VSCode中Debug和執行路徑不一致的VSCode
- 如何在直播中解決花屏問題?