提振精神
清除器是偵測程式碼中特定類別錯誤的工具。雖然運作原則各不相同,但清除器通常 (但不一定) 會依賴某種形式的編譯時間檢測,以改變程式碼,在執行階段公開錯誤。Fuchsia 會使用各種清除器,找出並診斷難以發現的危險錯誤。
消毒劑是透過建構時間旗標啟用。系統會持續在 Fuchsia 的持續整合 (CI) 和提交佇列 (CQ) 中執行 Sanitizer 建構作業,並為 Fuchsia C/C++ 和 Rust 開發人員提供服務。
開發人員通常不需要採取任何特殊動作,即可使用清除器,而且只有在清除器偵測到錯誤時,才需要注意。但仍須遵守特定限制。請繼續閱讀,瞭解支援的清除工具和使用方式。
支援的消毒液
Fuchsia 目前支援下列清除器:
- AddressSanitizer (ASan) 可偵測超出界限的存取、釋放 / 傳回 / 範圍後使用,以及重複釋放等情況。
- LeakSanitizer (LSan) 可偵測記憶體流失情形。LeakSanitizer 的運作方式類似於保守的垃圾收集器,會檢查是否有記憶體洩漏。如果無法從參照根目錄 (執行緒堆疊、執行緒暫存器、全域變數和執行緒本機變數) 存取任何配置,系統就會視為記憶體洩漏。
- ThreadSanitizer (TSan) 會偵測資料競爭 (僅限主機)。
- UndefinedBehaviorSanitizer (UBSan) 會偵測依賴未定義程式行為的特定問題。
Zircon 核心提供下列清除器行為:
- 實體記憶體管理員 (PMM) 檢查程式 (
pmm_checker) 會偵測「使用已釋放記憶體」錯誤和未受控的 DMA。 - 核心 AddressSanitizer (KASan) 與 PMM 合作,將 AddressSanitizer 擴充至核心程式碼。
- Lockdep 是執行階段鎖定驗證器,可偵測鎖定危害,例如死結。
系統預設會新增下列 C/C++ 編譯選項,以便在執行階段偵測或避免發生錯誤:
-ftrivial-auto-var-init=pattern(請參閱 RFC) 會將自動變數初始化為非零模式,以公開與從未初始化的記憶體讀取資料相關的錯誤。- ShadowCallStack 和 SafeStack 可強化產生的程式碼,防止堆疊溢位。
最後,Fuchsia 會使用 libFuzzer 和 syzkaller 執行涵蓋範圍導向的模糊測試。模糊測試器與清除器類似,都會嘗試在執行階段公開程式碼中的錯誤,而且通常會一併使用。模糊測試器與清除器不同,模糊測試器會嘗試強制執行正式版程式碼,進入可能公開錯誤的路徑。
支援的設定
目前,在下列設定下,本機建構和 CI/CQ 均支援清除器:
bringup.x64bringup.arm64(注意:如要在本機模擬,請使用bringup.qemu-arm64)core.x64core.arm64(注意:如要在本機模擬,請使用core.qemu-arm64)zbi_tests.x64zbi_tests.arm64
此外,清除器也適用於主辦人工具。
上述所有項目的測試都會在 CI/CQ 上透過 qemu 和 Intel NUC 執行。由於資源和容量問題,我們不會在其他平台上測試清除器,但您可以使用下列建構工作流程,在本機測試這些平台。
在 Gerrit 和 CI 控制台中,系統可能會向特定已登入使用者顯示 //vendor 下定義的設定的其他試用作業。尋找名稱含有 -asan 的設定。
上述清除工具會套用至 C/C++ 程式碼。此外,LSan 也適用於 Rust 程式碼,可偵測 Rust 記憶體洩漏。
排解清除器問題
建構
Fuchsia 平台建構版本 (樹狀結構內)
如要重現 Sanitizer 建構作業,可以使用建構變數啟用 Sanitizer:
fx set product --variant asan-ubsan --variant host_asan-ubsan或者,您也可以選擇只檢測特定二進位檔:
fx set product --variant asan-ubsan/executable_name如果裝置無法容納完整檢測的版本,您可以使用選擇性檢測工作流程在本機測試硬體。
如要偵測核心程式碼中的「使用後釋放」錯誤,您需要啟用核心 PMM 檢查工具。
樹狀結構外建構
使用 Fuchsia 工具鍊編譯時,只要傳遞 -fsanitize= 旗標,即可指出要使用的清除器。請參閱編譯器說明文件。
使用插碼元件建立 Fuchsia 套件時,請務必確保套件包含所有執行階段依附元件,包括做為 Clang 工具鍊一部分發布的清除器執行階段,以及做為 Fuchsia SDK 一部分發布的插碼 C 程式庫 (位於 sysroot 下方)。
測試
如常在本機工作流程或已啟用清除器的 CQ 建構工具上進行測試 (tryjob 名稱中含有 asan)。如果清除器偵測到問題,系統會在記錄檔中列印訊息,其中包含下列其中一個字串:
ERROR: AddressSanitizerERROR: LeakSanitizerSUMMARY: UndefinedBehaviorSanitizerWARNING: ThreadSanitizer
這些訊息之後會顯示堆疊追蹤,指出問題的性質並找出根本原因。這些訊息會顯示在 fx log 中。
請注意,觸發清除器的測試可能仍會顯示為通過。 清除器問題不會導致測試失敗。
清除工具偵測到的問題通常有類似的根本原因。您或許可以搜尋 Fuchsia 錯誤,找出先前工作的參照,方法是搜尋與清除器輸出內容中某些關鍵字相同的錯誤。
已知問題
#[should_panic]
Fuchsia 的 Rust 建構作業會在 panic!中止。這會大幅縮減二進位檔大小。但很遺憾,使用 #[should_panic] 屬性的測試可能會誤判為有記憶體洩漏問題。這些測試會發出預期的恐慌,然後在不解除的情況下結束,這表示測試不會釋放堆積分配。對 LeakSanitizer 而言,這與實際的記憶體流失無法區別。
如果這項問題影響了您的測試,您可以採取下列做法:
按照這個範例切換至
#[fuchsia::test]屬性。這是偏好的屬性,因為#[fuchsia::test]只會停用 LeakSanitizer,但其他清除器仍會保持啟用狀態。如果難以切換至
#[fuchsia::test],請按照這個範例,在清除器建構版本中停用測試。
請參閱:問題 88496:should_panic 觸發 leaksanitizer 的 Rust 測試
最佳做法
確保測試會執行程式碼
清除器會在執行階段公開錯誤。除非您的程式碼會執行 (例如在測試中,或一般在 Fuchsia 上以 CI/CQ 執行的形式),否則清除器無法公開程式碼中的錯誤。
如要確保程式碼的清除器涵蓋範圍,最好的方法是在相同設定下確保測試涵蓋範圍。請參閱測試涵蓋範圍指南。
請勿在程式碼中停用清除工具
特定建構目標可能會停用清除器。這項功能最常用於早於導入清除器支援的相關問題,特別是 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 中不穩定測試的指南。
回報有用的錯誤
遇到清理工具問題時,請提交錯誤報告,並附上所有可用的疑難排解資訊。
範例:Issue 73214: ASAN use-after-scope in blobfs
錯誤報告包含:
- 清除工具提供的錯誤 (本例為 ASan)。
- 如何建構及測試以重現錯誤的操作說明。
- 後續調查的詳細資料,以及視需要提供的特定程式碼指標。
- 相關變更的參照,例如本例中修正錯誤根本原因的變更。
發展藍圖
持續進行的工作:
- 硬體加速 AddressSanitizer (hwasan):大幅減少 asan 的記憶體負擔,讓儀表板可在 RAM 受限的裝置上運作,並縮小硬體相關程式碼的測試差距。另請參閱:RFC-0143:Userspace Top-Byte-Ignore。
- GWP-ASan:目前正在努力展示如何使用這個 ASan 取樣版本,偵測現場的錯誤。
- 透過系統呼叫進行涵蓋範圍導向的核心模糊測試。
未來工作領域:
- ThreadSanitizer (TSan):偵測資料競爭。
- 核心支援偵測並行錯誤。
- 擴充 Rust 的清除器支援功能,例如偵測 Rust
unsafe {}程式碼區塊或 FFI 呼叫中的記憶體安全錯誤,或是偵測未定義的行為錯誤。 - MemorySanitizer (MSan):偵測從未初始化的記憶體讀取資料。
另請參閱:2021 年發展藍圖中的清除工具。