消毒殺菌劑

提振精神

清理程式是用來偵測程式碼中特定錯誤類別的工具。雖然清理工具通常會 (但不一定) 仰賴某種形式的編譯時間檢測功能,以導致執行階段錯誤的方式變更程式碼,但作業原則各不相同。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) 將自動變數初始化為非零模式,顯示與未初始化記憶體讀取相關的錯誤。
  • ShadowCallStackSafeStack 可針對堆疊溢位來強化產生的程式碼。

最後,Fuchsia 使用 libFuzzersyzkaller 執行涵蓋範圍導向的模糊測試。模糊工具與消毒器類似,都會嘗試在執行階段公開程式碼中的錯誤,且通常用於組合用途。Fuzzer 與 Sanitizer 不同,會在可能公開錯誤的路徑中強制執行實際工作環境程式碼。

支援的設定

下列設定目前在本機建構作業和 CI/CQ 中都支援 Sanitizer:

  • bringup.x64
  • bringup.arm64
  • core.x64
  • core.arm64
  • zbi_tests.x64
  • zbi_tests.arm64

此外,消毒工具也適用於主機工具。

上述所有測試都是在 qemuIntel 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)。
  • 說明如何建構及測試重現錯誤的操作說明。
  • 後續調查的詳細資料,以及需要的特定程式碼指標。
  • 參考相關變更的參考資料,例如在這個案例中,為了修正錯誤的根本原因所做的變更。

發展藍圖

進行中的工作:

未來工作領域:

  • ThreadSanitizer (TSan):偵測資料競爭。
  • 用於偵測並行錯誤的核心支援。
  • 進一步支援 Rust 的清理程式支援,例如在 Rust unsafe {} 程式碼區塊或 FFI 呼叫中偵測記憶體安全錯誤,或是偵測未定義的行為錯誤。
  • MemorySanitizer (MSan):偵測未初始化記憶體的讀取作業。

另請參閱:2021 年藍圖中的消毒液