提振精神
清理程式是用來偵測程式碼中特定錯誤類別的工具。雖然清理工具通常會 (但不一定) 仰賴某種形式的編譯時間檢測功能,以導致執行階段錯誤的方式變更程式碼,但作業原則各不相同。Fuchsia 使用各種消毒液識別及診斷難以發現的危險錯誤。
清理程式可透過建構時間旗標啟用。Sanitizer 版本會持續在 Fuchsia 的持續整合 (CI) 和修訂佇列 (CQ) 上進行執行,並提供 Fuchsia C/C++ 和 Rust 開發人員。
開發人員通常無須採取特殊行動就能使用消毒液,而且只有在偵測到錯誤時才需要注意消毒液。但須遵守特定限制。繼續閱讀下文,瞭解支援哪些消化器和使用方式。
支援的消毒液
Fuchsia 目前支援下列消毒液:
- AddressSanitizer (ASan) 會偵測超出存取範圍的執行個體、在釋放 / 傳回 / 範圍後使用,且會重複釋放。
- LeakSanitizer (LSan) 可偵測記憶體流失情形。LeakSanitizer 的運作方式就像是保守的垃圾收集器,用於檢查是否外洩。凡是無法透過參照根存取的配置 (執行緒堆疊、執行緒註冊、全域變數和執行緒本機變數),都會被視為外洩。
- ThreadSanitizer (TSan) 可偵測資料競爭 (僅限主機)。
- UndefinedBehaviorSanitizer (UBSan) 會偵測依賴未定義的程式行為的特定問題。
Zircon 核心中提供以下消化器行為:
- 實體記憶體管理工具 (PMM) 檢查工具 (
pmm_checker
) 會偵測釋放後使用的錯誤,並去除 DMA。 - Kernel AddressSanitizer (KASan) 與 PMM 合作,將 AddressSanitizer 延伸至核心程式碼。
- Lockdep 是執行階段鎖定驗證工具,可偵測鎖定危害 (例如死結)。
根據預設,系統會新增下列 C/C++ 編譯選項,用於在執行階段偵測或防範錯誤:
-ftrivial-auto-var-init=pattern
(請參閱 RFC) 將自動變數初始化為非零模式,顯示與未初始化記憶體讀取相關的錯誤。- ShadowCallStack 和 SafeStack 可針對堆疊溢位來強化產生的程式碼。
最後,Fuchsia 使用 libFuzzer 和 syzkaller 執行涵蓋範圍導向的模糊測試。模糊工具與消毒器類似,都會嘗試在執行階段公開程式碼中的錯誤,且通常用於組合用途。Fuzzer 與 Sanitizer 不同,會在可能公開錯誤的路徑中強制執行實際工作環境程式碼。
支援的設定
下列設定目前在本機建構作業和 CI/CQ 中都支援 Sanitizer:
bringup.x64
bringup.arm64
core.x64
core.arm64
zbi_tests.x64
zbi_tests.arm64
此外,消毒工具也適用於主機工具。
上述所有測試都是在 qemu 和 Intel NUC 的 CI/CQ 上執行。由於資源和容量問題,其他平台不會使用消化器進行測試,但您可以使用下方的建構工作流程,在這些平台上在本機進行測試。
針對 //vendor
下定義的設定,Gerrit 和 CI 控制台可能會顯示在特定的登入使用者中。請尋找名稱中含有 -asan
的設定。
上述清理工具適用於 C/C++ 程式碼。此外,LSan 也會套用至 Rust 程式碼,用於偵測 Rust 記憶體流失情形。
排解消毒液問題
建構
Fuchsia 平台建構 (樹狀結構內)
fx set product --variant asan-ubsan --variant host_asan-ubsan
您也可以只檢測特定二進位檔:
fx set product --variant asan-ubsan/executable_name
選擇性檢測工作流程非常適合用於在本機硬體上測試,因為硬體完整檢測版本無法滿足裝置需求。
具體而言,如要偵測核心程式碼中停用後的使用錯誤,您必須啟用核心 PMM 檢查工具。
樹狀結構外建構
使用 Fuchsia 工具鍊進行編譯時,只要傳遞 -fsanitize=
標記,即可指出要使用的消化器。請參閱編譯器說明文件。
使用檢測元件建立 Fuchsia 套件時,您必須確保套件包含所有執行階段依附元件,包括做為 Clang 工具鍊的一部分發布,以及透過 sysroot 下為 Fuchsia SDK 發布的檢測 C 程式庫。
測試
請照常在本機工作流程或已啟用清理工具的 CQ 建構工具 (名稱包含 asan
) 進行測試。如果清理程式偵測到問題,系統會將訊息輸出到含有下列其中一個字串的記錄中:
ERROR: AddressSanitizer
ERROR: LeakSanitizer
SUMMARY: UndefinedBehaviorSanitizer
WARNING: ThreadSanitizer
追蹤這些訊息後,您會看到堆疊追蹤,找出問題性質並指出根本原因。您可以在 fx log
中找到這些訊息。
請注意,觸發消毒液的測試可能仍會顯示為通過。Sanitizer 問題不會顯示為測試失敗。
消毒液偵測到的問題通常也是類似的根本原因。您可以搜尋 Fuchsia 錯誤,藉此找出先前工作的相關參考資料,例如您在消化器輸出中看到的部分關鍵字。
已知問題
#[should_panic]
Fuchsia 的 Rust 建構了 panic!
,這可大幅減少二進位檔的大小。一個不幸的副作用是,使用 #[should_panic]
屬性的測試可能會錯誤地偵測記憶體流失情形。這些測試會發出預期的恐慌,然後在不解開的情況下結束,也就是說,不會釋放堆積分配。對 LeakSanitizer 來說,這是無法區分實際記憶體流失的情況。
如果這個問題會影響您的測試,您可以按照這個範例在 Sanitizer 版本中將其停用。
請參閱:問題 88496:Rust 測試 should_panic 觸發資訊外洩工具
最佳做法
確保測試可練習程式碼
衛生器會在執行階段曝露錯誤。除非程式碼執行,例如在測試期間執行,或以 CI/CQ 練習的方式在 Fuchsia 上執行,否則掃毒工具將無法在您的程式碼中公開錯誤。
如要確保程式碼的消毒程式涵蓋範圍,最佳做法是確保使用相同設定下的測試涵蓋範圍。請參閱測試涵蓋範圍指南。
請勿在程式碼中抑制消毒液
某些建構目標可能會抑制消毒器。最常見的情況是,在消毒化支援推出之前的問題,尤其是 Fuchsia 專案不屬於第三方程式碼的問題。
抑制的消毒劑不僅會隱藏舊的錯誤,還會讓您在程式碼導入時發現新錯誤,因此應視為技術債務。理想情況下不應新增抑制功能,而應移除現有抑制功能並修正潛在錯誤。
您可以編輯定義可執行目標的 BUILD.gn
檔案,藉此隱藏消毒液:
executable("please_fix_the_bugs") {
...
# TODO(https://fxbug.dev/42074368): delete the below and fix the memory bug.
deps += [ "//build/config/sanitizers:suppress-asan-stack-use-after-return" ]
# TODO(https://fxbug.dev/42074368): delete the below and fix the memory bug.
deps += [ "//build/config/sanitizers:suppress-asan-container-overflow" ]
# TODO(https://fxbug.dev/42074368): delete the below and fix the memory leak.
deps += [ "//build/config/sanitizers:suppress-lsan.DO-NOT-USE-THIS" ]
}
上例示範如何抑制所有消毒液。不過,您應該盡可能抑製造成故障的消毒液。如要追蹤隱藏情況,請回報錯誤,並在註解中參照 (如上所示)。
另一個停用消毒液的常見做法如下:
executable("too_slow_when_built_with_asan") {
...
exclude_toolchain_tags = [ "asan" ]
}
以上兩個範例都會隱藏整個執行檔的精細程度。 為了更精細的抑制功能,您可以偵測程式碼中是否存在消毒液。如果您想在特定測試案例中抑制消毒液,而非更廣泛的範圍,這個功能就非常實用。舉例來說,如果測試刻意導入記憶體錯誤,並測試消毒器執行階段本身,就可以使用這個特徵。
如果是 C/C++,請參閱:
以 Rust 來說,您可以按照以下模式操作:
#[cfg(test)]
mod tests {
#[test]
// TODO(https://fxbug.dev/42074368): delete the below and fix the leak
#[cfg_attr(feature = "variant_asan", ignore)]
fn test_that_leaks() {
// ...
}
}
測試不穩定
如果測試中的程式碼行為不具有確定性,則衛生器錯誤可能是不穩定。例如,記憶體流失可能只會在特定競爭狀況下發生。如果衛生器錯誤看起來不穩定,請參閱「在 CQ 中測試脆弱」指南。
回報良好錯誤
遇到 Sanitizer 問題時,請回報錯誤,並加入所有可用的疑難排解資訊。
範例:問題 73214:blobfs 中的 ASAN 使用後範圍
錯誤報告內容包含:
- 清理程式提供的錯誤 (在本例中為 ASan)。
- 說明如何建構及測試重現錯誤的操作說明。
- 後續調查的詳細資料,以及需要的特定程式碼指標。
- 參考相關變更的參考資料,例如在這個案例中,為了修正錯誤的根本原因所做的變更。
發展藍圖
進行中的工作:
- 硬體加速 AddressSanitizer (hwasan):大幅降低 Asan 的記憶體負擔,確保在 RAM 受限的裝置上可以執行檢測作業,並致力消除硬體相關程式碼的測試差距。另請參閱 RFC-0143: Userspace Top-Byte-Ignore。
- GWP-ASan:我們目前正著手示範如何使用這個抽樣版本的亞洲偵測版本,藉此偵測欄位中的錯誤。
- 透過 Syscall 進行涵蓋率導向核心模糊化。
未來工作領域:
- ThreadSanitizer (TSan):偵測資料競爭。
- 用於偵測並行錯誤的核心支援。
- 進一步支援 Rust 的清理程式支援,例如在 Rust
unsafe {}
程式碼區塊或 FFI 呼叫中偵測記憶體安全錯誤,或是偵測未定義的行為錯誤。 - MemorySanitizer (MSan):偵測未初始化記憶體的讀取作業。
另請參閱:2021 年藍圖中的消毒液。