RFC-0117 - 元件模糊架構

RFC-0117:元件模糊測試架構
狀態已接受
區域
  • 測試
說明

Fuchsia 原生跨程序模糊測試架構。

問題
Gerrit 變更
作者
審查人員
提交日期 (年-月-日)2021-05-24
審查日期 (年-月-日)2021-07-28

摘要

引導式模糊測試是減少錯誤和提高平台信心的有效方法,但目前沒有可用於跨多個程序邊界進行模糊測試的模糊測試架構,如同在 Fuchsia 元件拓樸中所見。本文件針對這類架構提出設計建議,讓架構可在各個程序和測試領域之間共用涵蓋率測試輸入內容,讓元件可在最常見的設定中進行模糊測試。

提振精神

程式測試可用於顯示錯誤的存在,但絕不能用於顯示錯誤的缺少!

--Edsger W. Dijkstra

引導式模糊測試是指在回饋迴圈中,使用產生的資料測試軟體:

  1. 模糊測試器會產生一些測試輸入資料,並用於測試目標軟體。
  2. 如果測試結果導致失敗,模糊測試器會記錄輸入內容並結束。
  3. 目標軟體會產生意見回饋,並由模糊測試工具收集。
  4. 模糊測試器會根據這些回饋產生其他測試輸入內容,然後重複執行。

在找出與專案需求無關 (因此通常未經測試) 的軟體錯誤時,引導式模糊測試非常實用。透過自動化測試涵蓋率,開發人員也能更有信心地評估系統中與安全性正確性和/或穩定性相關的重要部分。

您可以使用下列分類法描述引導式模糊測試架構:
模糊測試分類

  • 引擎:目標無關的意見回饋循環。
    • 字典管理:維護一組模糊測試輸入內容 (字典)。記錄新的輸入內容,並修改現有內容 (例如合併)。
      • 種子語料庫是一組手工製作的初始輸入內容。
      • 「即時語料庫」是持續更新的產生輸入內容組合。
    • 變異器:一組變異策略和確定性假亂數來源,用於從語料庫建立新輸入內容。
    • 意見回饋分析:根據意見回饋處理輸入內容。
    • 管理介面:與使用者互動,協調工作流程:
      • 使用特定輸入內容測試目標,也就是執行單一模糊測試執行程序
      • 對目標進行模糊測試,也就是執行 (可能無限次) 模糊測試器執行序列。
      • 分析或操控特定語料庫。
      • 回應偵測到的錯誤,以及/或是處理導致錯誤的構件。
  • 目標:要進行模糊處理的特定目標領域。
    • 輸入處理:將單一執行作業的模糊測試輸入內容對應至測試中的程式碼,例如透過特定函式、I/O 管道等。
    • 收集意見回饋:觀察輸入內容所導致的行為。可能會收集硬體或軟體追蹤記錄、程式碼涵蓋率資料、時間等。
    • 錯誤偵測:判斷輸入內容是否導致錯誤。收集並記錄測試構件,例如輸入內容、記錄、回溯追蹤等。

其中幾個方面可能需要作業系統和/或其工具鍊的特定支援,例如意見回饋收集和錯誤偵測。目前在 Fuchsia 上,最全面支援的模糊測試架構是 libFuzzer,透過預先建構的 clang 工具鍊提供,做為編譯器執行階段。我們已為用於收集程式碼涵蓋率意見回饋的 sanitizer_common 執行階段,以及用於偵測例外狀況的 libFuzzer 本身新增支援功能。除了一組 GN 範本主機工具外,這些工具還可讓開發人員快速為 Fuchsia 上的程式庫開發模糊測試工具。

不過,與 Linux 不同,在 Fuchsia 上,軟體的基本可執行單元是元件,而非程式庫。使用現有的引導式模糊測試架構對元件進行模糊測試相當麻煩,因為這些架構的回饋精細度可能太窄 (例如在單一程序中使用 libFuzzer),也可能太廣 (例如在 qemu 例項上使用 TriforceAFL)。

在 Fuchsia 中,用於模糊測試元件的理想架構應具備下列功能:

  • 與現有的持續模糊測試基礎架構整合,例如 ClusterFuzz
  • 模組化方法,可利用其他模糊處理架構的平台無關部分,例如突變策略。
  • 高效能跨程序碼涵蓋率機制。
  • 整合現有的 Fuchsia 工作流程,例如 ffx
  • 密封環境,可隔離受測元件和/或為其依附元件提供模擬元件。
  • 目標元件的未修改來源。
  • 一種強大靈活的分析執行作業和偵測錯誤的方法。
  • 開發人員故事,類似於 Fuchsia 中的其他測試樣式。

設計

這項設計旨在:

  • 符合 Fuchsia 的慣用法。
  • 重複使用現有的實作項目。

大致來說,這項設計會運用測試執行程式架構,並新增下列項目:

  • 用於驅動模糊處理的 fuzzer_engine
  • ffx 外掛程式和模糊測試管理工具,可與模糊測試程式互動及管理模糊測試程式。
  • fuzzer_engine 連結至模糊處理器的 fuzz_test_runner


元件模糊測試架構設計

本文件的這一節大致按照控制流程排序;也就是說,它從人類或機器人想要執行模糊測試工作開始,並朝著模糊測試的目標領域前進。讀者應注意,部分章節會提及後續章節中詳細說明的概念。

ffx fuzz 主機工具

使用者 (包括人類和機器人) 會透過 ffx 外掛程式與架構互動。這項外掛程式可透過以下方式與 fuzz_manager 服務通訊:

ffx fuzz 的子指令與 fx fuzz 的子指令相同,例如:

  • analyze:回報指定語料庫和/或字典的涵蓋率資訊。
  • check:檢查一或多個模糊測試器的狀態。
  • coverage:產生測試的涵蓋率報表。
  • list:列出目前版本中可用的模糊測試工具。
  • repro:重播測試單元,重現模糊測試結果。
  • start:啟動特定模糊測試工具。
  • stop:停止特定的模糊測試工具。
  • update:更新模糊測試語料庫的 BUILD.gn 檔案。

模糊管理員

執行元件提供兩項重要功能:

  • 您可以輕鬆建立複雜且密封的測試領域,並透過可自訂的測試執行器驅動這些領域。
  • 這項功能可用於收集記錄和回溯追蹤等重要診斷資訊。

此外,單一模糊測試執行作業可以自然地以元件測試架構的術語表示:程式碼會使用指定的測試輸入內容進行測試,並根據是否發生錯誤,判斷程式碼是否通過測試。

不過,雜訊測試與其他形式的測試有所不同,而當您比較持續雜訊測試持續測試時,這項差異就會更加明顯:

  • 測試輸入內容無法事先得知。
    • 測試輸入內容是模糊測試的結果。
    • 持續模糊測試基礎架構 (例如 ClusterFuzz) 會有許多模糊測試器例,並會在模糊測試進行期間交互影響測試輸入內容。
  • 模糊測試執行作業是開放式的。模糊測試永遠不會「通過」,只會失敗或提早停止。
    • 因此,您必須提供按需狀態,其中包含其他測試通常不會提供的詳細資料,例如執行速度、收集的總意見回饋、耗用記憶體等。
    • 您必須持續向監控模糊測試執行作業的人員或模糊測試基礎結構機器人提供這項狀態。
  • 模糊測試結果比單純的通過/失敗更豐富。
    • 發生錯誤時,輸出內容必須包含觸發輸入內容,以及任何相關記錄和回溯資訊。
    • 在提早終止時,輸出內容可能會包含累積的意見回饋,以及日後模糊測試的建議參數 (例如字典)。
  • 模糊處理的領域可用於模糊處理基礎架構選擇依序執行的幾種不同工作流程,例如「模糊處理一段時間。如果發現錯誤,請清除錯誤,否則合併並壓縮語料庫」將每個步驟表示為測試套件,會導致從一個步驟擷取狀態,然後在下一個步驟還原的大量工作。

其中部分問題可透過擴充測試執行元件架構來解決,例如提供結構化輸出內容。不過,如果對所有模糊處理需求採用這種方法,就會為其他不需要的測試新增大量功能。因此,設計中新增了 fuzz_manager,其功能如下:

接著,請按照下列方式修改測試執行工具架構:

  1. 新增 fuzz_test_runner。這個執行元件會在現有的 elf_test_runner 上建構,以啟動 fuzzer_engine 並將 fuzzer 網址傳遞給它。
  2. 修改 test_manager 以將 fuchsia.fuzzer.manager.Harness 能力重新導向至 fuzz_test_runner。這項能力「不會」路由至測試,且不會影響非模糊處理器的密封性。
  3. fuzz_test_runner 會為 fuchsia.fuzzer.Controller 通訊協定建立管道組合。它會將一端安裝為 fuzzer_engine 中的啟動程序句柄,並使用 fuchsia.fuzzer.manager.Harness 將另一端傳遞至 fuzz_manager

Fuzzer 引擎

fuzzer_engine 是模糊化領域的元件。就模糊測試分類方式而言,這項工具:

  • 實作 fuchsia.fuzzer.Controller 通訊協定,提供管理介面
  • 建立並使用儲存空間能力,管理每個語料庫
  • 變異字庫中的輸入內容,以建立新的測試輸入內容。(例如與 libMutagen 相依的連結)。
  • Uses Adapter 能力,可傳送新的待處理的輸入內容
  • Exposesfuchsia.fuzzer.ProcessProxy 能力,可用於在模糊處理的領域中檢測遠端程序,以提供收集到的意見回饋回報錯誤
  • 分析意見回饋

如果模糊測試視為一系列含有不同輸入內容的測試,一種方法是讓模糊測試引擎為每個輸入內容例項化一個全新的測試領域,也就是讓測試執行程式依序執行每個模糊測試執行。這種做法的主要問題是回饋分析和突變迴圈的效能。模糊測試器的品質與吞吐量直接相關,因此主迴圈必須非常快速:"變異、處理輸入內容、收集意見回饋,以及分析意見回饋"的額外負擔應為微秒等級。

因此,模糊測試引擎會以類似於用於測試複雜拓樸結構的測試驅動程式庫程式方式,納入測試領域。由 eventpairs 協調的共用 VMOs 可用來將測試輸入內容傳送至模糊處理目標轉接器,並從檢測到的遠端程序傳回意見,且延遲時間盡可能低。

模糊測試引擎是由 fuzz_test_runner 啟動。這個執行元件與現有的 elf_test_runner 非常相似,但有一個重大的差異:它會為 fuchsia.fuzzer.Controller 通訊協定建立管道組合。並將此組合的其中一端安裝為 fuzzer_engine 中的啟動程序句柄。它會使用 test_manager 轉送至 fuchsia.fuzz.manager.Harnessfuchsia.fuzz.manager.Harness 能力,將其他內容傳遞至 fuzz_manager。這樣一來,test_manager 就只會為 fuzz_test_runner 和啟動的模糊測試器提供 Harness 能力,而非所有測試。

目標轉接程式

fuzzer 分類法中,模糊測試目標轉接器會執行「輸入處理」角色。使用上述共用 VMO 和事件對,將由模糊測試引擎產生的測試輸入內容,對應至目標領域的檢測設備遠端程序的特定互動,並進行模糊測試。

這些特定互動是由模糊測試作者提供,通常是「編寫模糊測試」的貢獻。

模糊測試程式作者可以提供自訂的模糊測試目標轉接程式,也可以使用提供的其中一個腳手架。

可能的轉接程式架構範例包括:

  • llvm_fuzzer_adapter:作者應實作 LLVM 的模糊目標函式

    • 針對 C/C++,作者應實作以下項目:
    extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size);
    
    • 如果是 Rust,作者應使用 #[fuzz] proc_macro 屬性實作方法。
    • 在 Go 中,作者應實作以下項目:
    func Fuzz(s []byte);
    
  • realm_builder_adapter:除了 LLVM 模糊測試目標函式外,作者還會實作修改提供 RealmBuilder 的方法。轉接器會為此函式提供預設建構工具,並使用結果建構要模糊處理的元件領域。作者可以新增其他路徑、功能和模擬資料等,藉此修改此檔案:

    pub trait FuzzedRealmBuilder {
      fn extend(builder : &mut RealmBuilder);
    }
    
  • libfuzzer_adapter:與 llvm_fuzzer_adapter 的預期相同,但其元件資訊清單會省略模糊測試引擎,公開 Controller 能力本身,並直接連結至 libFuzzer。這個截然不同的元件拓撲可在這個架構中,使用 libFuzzer 進行傳統的程式庫模糊測試。

  • honggfuzz-persistent-adapter:預期模糊測試程式作者會實作以下項目:

    extern HF_ITER(uint8_t** buf, size_t* len);
    

    honggfuzz 本身目前不支援,但為其編寫的模糊測試目標函式仍可與此架構整合。

請注意,目標轉接器可以且應連結至遠端程式庫,並與檢測目標中的檢測遠端程序一同運作。

檢測設備的遠端程序

為了收集意見回饋並偵測錯誤,您需要使用額外的檢測工具 (例如 SanitizerCoverage),在模糊處理的目標領域內建立所有程序。對於樹狀結構內建構的模糊測試器,您可以透過工具鍊變化版本flagsdeps 傳播至 GN 目標的依附元件,以達到這項目標。必要的旗標 (例如 -fsanitize-coverage=inline-8bit-counters) 也會記錄在文件中,以便允許樹狀結構外編譯。

此外,這些程序也需要 fuchsia.fuzzer.ProcessProxy 用戶端實作。上述的相同工具鍊變化版本可自動新增依附元件,以便將樹狀結構內的模糊測試工具連結至遠端程式庫。

模糊處理分類的角度來看,遠端程式庫提供以下功能:

  • 透過回呼收集意見回饋,例如 __sanitizer_cov_inline_8bit_counters_init
  • 在啟動期間盡早連線至 fuzzer_engineProcessProxy
  • 偵測錯誤的背景執行緒,例如監控例外狀況、記憶體用量等。

樹外雜訊偵測工具可提供自己的用戶端實作項目。將 fuchsia.fuzzer.ProcessProxy FIDL 介面和遠端程式庫實作項目新增至 SDK,就能更輕鬆地編寫樹狀結構外模糊測試器。

最後,所需的編譯時間修改作業,只會是 LLVM IR 上的轉換。所有其他修改作業都只會在連結時間進行。這可讓服務供應商向願意提供元件 LLVM 位元碼的 SDK 消費者提供「模糊測試做為服務」,而無須要求提供原始碼。

元件拓撲

將上述所有內容結合起來,模糊測試元件拓撲包含:

  • core:系統根元件。
  • fuzz_manager:在 fuzzer 和主機工具之間的根領域建立橋接。
  • test_manager:如同測試執行程式架構
  • target_fuzzer:模糊處理的領域進入點。
  • fuzzer_engine:不區分目標的模糊測試驅動程式庫。
  • target_adapter:針對特定目標的元件,其中包含使用者提供的輸入處理程式碼。
  • instrumented_target:正在模糊處理的元件。

adaptertarget 元件可能會有其他子項,例如模擬項目和目標領域的模糊處理。

上述各項元素的互動關係如下所示:
模糊測試框架拓撲

FIDL 介面

此架構會新增兩個 FIDL 程式庫:一個用於與 fuzz_manager 互動,另一個用於與模糊測試器本身互動。

fuchsia.fuzzer.manager

fuchsia.fuzzer.manager 定義的類型包括:

  • LaunchError:可擴充的 enum,列出與尋找及啟動模糊測試器相關的錯誤。

fuchsia.fuzzer.manager 定義的通訊協定包括:

  • fuchsia.fuzzer.manager.Coordinator:由 fuzz_manager 透過 ffx 提供給使用者。包含啟動模糊測試器並連結 fuchsia.fuzzer.Controller 的方法,以及停止模糊測試器的方法。
  • fuchsia.fuzzer.manager.Harness:透過 coretest_manager 的靜態路由,由 fuzz_manager 提供至 fuzz_test_runner。執行程式會使用這個通訊協定,將管道的其中一端傳遞給可用於 fuchsia.fuzzer.Controller 通訊協定的管理員。

fuchsia.fuzzer

fuchsia.fuzzer 定義的類型包括:

  • Options:可擴充的 table,其中包含用於設定執行作業、錯誤偵測等的參數。
  • Feedback:靈活的 union,用於代表目標意見回饋,例如程式碼涵蓋率、追蹤記錄、時間等。
  • Status:可擴充的 table,包含各種模糊測試指標,例如總涵蓋率、速度等。
  • FuzzerError:可擴充的 enum,列出錯誤類別,例如 ClusterFuzz 所辨識的錯誤類別。

fuchsia.fuzzer 定義的通訊協定包括:

  • fuchsia.fuzzer.Controller:由 fuzzer_engine 提供,並透過 fuzz_test_runner 傳遞至 fuzz_manager。由 fuzz_manager 代理給使用者。包含將輸入內容傳送至模糊器或從模糊器傳送至遺留物的各種方法,以及在模糊器上執行工作流程,例如輸入內容最小化、字典合併和一般模糊處理。
  • fuchsia.fuzzer.CorpusReader:由 fuchsia.fuzzer.Controller 提出要求。用於從特定種子或即時語料庫取得輸入內容。
  • fuchsia.fuzzer.CorpusWriter:由 fuchsia.fuzzer.Controller 提出要求。用於在特定種子或即時語料庫中新增輸入內容。
  • fuchsia.fuzzer.Adapter:由開發人員提供的 target_adapter 提供給 fuzzer_engine。包含註冊協調事件組合和用於傳送測試輸入內容的共用 VMO 的方法。
  • fuchsia.fuzzer.ProcessProxy:由 fuzzer_engine 提供給模糊處理領域中每個檢測的程序。包含註冊協調事件組合的方法,以及註冊用於提供意見回饋的共用 VMOs 的方法。

建構公用程式

該架構會為開發人員提供 fuchsia_fuzzer_package GN 範本。這可讓他們:

  • 自動加入 fuzzer_engine。
  • 產生工具可用的中繼資料,例如種子語料庫的位置。
  • 選取非模糊測試工具鍊變數時,請建構整合測試,而非模糊測試工具,如「測試」一節所述。
  • 從相關整合測試中重複使用受測元件的建構規則。

此架構也包含元件資訊清單片段,其中包含模糊測試器所需的常見元素,例如 fuzzer_engine 及其功能、fuzz_test_runner 等等。模糊測試器的元件資訊清單包含以下項目:

  • 預設的模糊處理器區塊。
  • 目標轉接元件的網址。
  • 要模糊處理的元件資訊清單網址。這通常可從相關的整合測試中重複使用。

這些建構公用程式設計得可讓模糊測試開發體驗與整合測試開發體驗相似。比較:
測試和模糊測試器開發程序

實作

實作計畫很簡單:在一系列變更中開發及單元測試個別類別,然後組合由 libFuzzer 衍生的整合測試,如「測試」一節所述。

語言

fuzzer_engineremote_library 是以 C++ 實作,以便處理其特質:

  • fuzzer_engineremote_library 都必須與其他 C ABI 整合,例如 libMutagenSanitizerCoverage 等。
  • 大部分的 remote_library 功能會在「main 之前和 exit 之後」發生,也就是在建構和/或載入 LLVM 模組時、執行 atexit 處理常式時,或發生致命例外狀況時。因此,架構需要明確控制 ELF 可執行檔生命週期的細微細節。

其他部分 (例如 realm_builder_adapter) 則是以 Rust 編寫。

資料傳輸通訊協定

在某些情況下,使用者需要提供或擷取任意數量的資料,包括:

  • 提供特定測試輸入內容,以便執行、清理或縮小。
  • 將 fuzzer 叢集與開發人員主機上的叢集同步,或跨多個 ClusterFuzz 執行個體同步。
  • 擷取觸發錯誤的測試輸入內容。

為盡量減少維護負擔,建議您使用 overnet 傳輸這類資料。不過,任何單一傳輸都可能超出 Zircon 管道中單一 FIDL 訊息的大小。相反地,Controller 通訊協定包含多種方法,可提供 zx_socket 物件,讓模糊測試引擎用於將資料從 VMOs 和/或本機儲存的檔案傳送至或從這些檔案。

系統會使用最少的通訊協定串流資料,讀取或寫入命名的位元組序列。由於傳送的資料可能超過 FIDL 訊息的長度上限,因此這個通訊協定「不是」FDLib。不過,命名的位元組序列在概念上等同於下列 FIDL 結構體:

struct NamedByteSequence {
  uint32 name_length;
  uint32 size;
  bytes:name_length name;
  bytes:size data;
};

堆疊解開

目前,libFuzzer 會使用 LLVM 的 unwinder,該 unwinder 會假設從在觸發信號的執行緒上執行的 POSIX 信號處理常式呼叫。對於 Fuchsia,這需要採用複雜的方法來處理例外狀況,包括修改當機執行緒的堆疊,以及插入回溯追蹤保留的組合跳板,以便「復活」unwinder 中的執行緒。

如果 libFuzzer 未處理錯誤,則不需要執行上述操作。相反地,系統會以最方便且有效的方式處理不同類型的錯誤,例如:

  • 例外狀況會由模糊測試引擎處理,該引擎會從模糊測試執行元件接收例外狀況管道,該管道是透過測試工作建立的。
  • 超時也由模糊測試引擎管理。
  • 消毒器回呼和 OOM 由遠端程式庫處理,並通知模糊測試引擎。

成效

模糊測試不會在實際運作系統上執行,因此不會影響任何發布程式碼的效能。雖然加入模糊測試工具鍊變數會對 Fuchsia 建構作業的效能造成輕微影響,但這個架構會重複使用現有的變數,因此不應造成任何新影響。

同樣地,在未經檢測的版本上使用模糊測試器產生單元測試,也能反映目前的方法,且不會在目前方法上增加任何重大的模糊測試成本。

就雜訊產生器本身而言,用來判斷雜訊產生器品質的最重要指標是每單位時間的涵蓋率,可透過測量兩個額外指標來推導:

  1. 在固定時間內執行模糊測試的總覆蓋率。
  2. 在固定時間內執行的總次數。

ClusterFuzz 已在資訊主頁上監控及發布每個模糊測試器的指標。

人體工學

人體工學是這項設計的重要面向,因為其影響力取決於開發人員的採用程度。

這個架構會嘗試以多種方式,盡可能簡化模糊測試。這項功能可讓開發人員:

  • 目標轉接器一節所述,以熟悉且彈性的方式編寫模糊測試器。
  • 使用現有的 GN 模糊測試範本系列建構模糊測試工具。
  • 使用熟悉的工作流程執行模糊測試工具。ffx fuzz 的用法與 fx fuzz 相似。
  • 取得可採取行動的結果。整合 ClusterFuzz 後,系統會自動記錄錯誤,並附上符號化的回溯追蹤和重現操作說明。

回溯相容性

現有的以 libFuzzer 為基礎的模糊測試器會實作模糊測試目標函式。只要提供 libFuzzer 專用的模糊測試目標轉接程式,這些模糊測試器就能在這個架構中運作,而無須修改任何來源。

安全性考量

這個架構不會用於運送產品設定。如果裝置內建模糊測試設定,則裝置間的通訊會使用 overnetffx 提供的現有驗證和安全通訊功能。

模糊測試產生的輸出內容可能會涉及安全性考量,例如測試輸入內容可能會導致可利用的記憶體毀損。這些疑慮必須由模糊測試操作員 (人類或模糊測試基礎架構) 處理,處理方式與任何其他可利用的錯誤報告相同 (例如正確標示、防止未經授權的揭露等)。

隱私權注意事項

在考量隱私權影響時,我們不會對模糊處理器運算子處理模糊處理器輸出內容的方式做出任何假設。這些輸出內容包括符號化記錄、導致錯誤的輸入內容、產生的字典和產生的語料庫。系統會假設記錄檔已沒有使用者資料,因為這是個別的隱私權問題,且受到密切監控。其餘的輸出內容則是直接從測試輸入內容衍生而來。因此,為了讓模糊測試器輸入不含使用者資料,就必須讓模糊測試器輸出不含使用者資料。

有三種方式可將輸入內容新增至模糊處理器的語料庫:

  • 做為種子輸入內容。您應將種子語料庫簽入原始碼存放區。在來源存放區中加入使用者資料時,會套用一般限制。
  • 手動新增至即時語料庫。
    • 這通常會由模糊處理基礎架構 (例如 ClusterFuzz) 執行,因為模糊處理器會與其他例項產生的輸入內容「交互授粉」。在這種情況下,其他例項不會包含使用者資料,新增的輸入內容也不會包含。
    • 操作員也可以透過 ffx 新增輸入內容。使用這種方式新增手動輸入內容時,工具會顯示使用者資料警告。
  • 產生的內容會加入現有語料庫。這些輸入內容是從現有輸入內容變異而來。由於這些輸入內容不含使用者資料,因此產生的內容也是如此。有些輸入內容可能會純粹碰巧與部分使用者資料相符,例如模糊測試器成功產生有效的使用者名稱。不過,在這種情況下,並未明確與使用者資料建立關聯。

即使模糊測試器並非密封的 (且非確定性的!),且使用測試領域公開的來源資料,也不會在字元集合中納入其他資料。架構不會將該資料視為測試輸入內容的一部分,也不會儲存該資料。

最糟的情況是,模糊測試工具故意設計為非密封的工具,並使用公開的功能,將測試領域的資料傳送至其他驗證個人識別資訊的服務,例如傳回使用者名稱是否有效。這需要花費大量心力,才能避開模糊處理和測試架構,以便鼓勵密封性。由於外部服務未經過檢測,這項做法不如隨機猜測。

此外,在實際操作中,模糊測試器會完全密封。這些測試不會在含有使用者資料的產品設定中執行,而是只會在開發雜訊產生器和 ClusterFuzz 時在本機上執行。

測試

使用常用方法 (例如 GoogleTest#[cfg(test)] 等) 對模糊測試引擎、目標轉接程式庫和遠端程式庫進行單元測試。此外,整合測試會使用預設的 ELF 測試執行元件,根據適用的 compiler-rt 子集,執行一組含有專門設計範例目標的模糊測試工作流程。

對於使用架構編寫的模糊測試器,架構會採用 GN 模糊測試器範本目前支援的相同方法:在未檢測的版本中建構模糊測試器時,引擎會由測試驅動程式取代,該驅動程式只會執行種子語料庫中的每個輸入內容。這可確保所有模糊測試器都能建構及執行,進而減輕「位元腐敗」的情形。這也能做為回歸測試,特別是當模糊測試作者在修正模糊測試發現的瑕疵時,透過新增輸入內容來維護其種子語料庫時。

說明文件

模糊測試說明文件樹狀結構需要更新,以便提供使用新 GN 範本的具體範例。任何其他計畫中的說明文件變更 (例如程式碼研究室等) 也應反映此架構。

缺點和替代方案

建議做法可能的缺點包括:

  • 效能降低的風險,可透過實作方式來緩解,這類實作方式會模擬高度最佳化模糊測試器中對效能至關重要的部分。
  • 維護負擔,可透過不必維護不便的整合作業 (例如 POSIX 模擬) 來節省。
  • 耦合風險,例如測試執行程式架構可能會以破壞這項設計的方式變更,或是可能因為這項設計而無法變更。如果這在日後成為問題,您可以將更多 test_manager 的功能直接整合至 fuzz_manager,例如讓後者直接建立隔離的測試領域。

這些缺點的影響,不如我們已探討的其他替代方案嚴重:

僅使用 libFuzzer 進行程式庫模糊測試。

我們已在 libFuzzer 中加入足夠的 Fuchsia 支援功能,以便在 Fuchsia 上使用該功能建構模糊測試器。過去幾年,這些工具已成功找出數百個錯誤。

同時,這些程式僅限於以程式庫結構建構的單一程序。由於元件是 Fuchsia 上可執行軟體的單位,且元件會透過 FIDL 廣泛地進行通訊,因此這個方法會讓越來越多的 Fuchsia 程式碼「無法模糊處理」。


傳統 libFuzzer

程序內 FIDL 模糊測試。

Chrome 等專案已嘗試在單一程序中執行用戶端和伺服器執行緒,以解決 RPC 模糊處理問題。這需要修改用戶端和伺服器,以便在新的非標準設定中執行。這可在服務之間重複使用,但對於元件生命週期和/或各語言繫結重新實作,往往會做出不靈活的假設。

更根本來說,要對互動元件的關閉進行模糊測試,難度也越來越高。許多元件都有複雜的拓樸結構。無論是執行或模擬整個關閉函式,都會很快變得難以維持,因為這會導致複雜度、額外負擔和效能問題。

這種做法已在 Fuchsia 上提供,但至少部分受限於這些限制,因此尚未廣泛採用。


程序內 FIDL 模糊測試

單一服務 FIDL 模糊測試。

最初嘗試設計跨程序 FIDL 模糊測試架構時,考慮的是單一用戶端和服務。在這個設計中,libFuzzer 會連結至服務,而用戶端則會維持為簡單的 Proxy。透過保留用戶端和伺服器之間的 FIDL 介面,可讓目標維持在更典型的設定中,讓服務生命週期更具彈性,並減少需要重新實作的程式碼。

不過,這並未解決元件關閉的模糊測試問題,因此相較於程序內 FIDL 模糊測試,提供的效益非常有限。


單一服務 FIDL 模糊測試

LibFuzzer 支援跨程序模糊測試。

一般來說,重複使用程式碼比重新實作程式碼有幾項優點:程式碼通常會更「成熟」,效能更好且錯誤更少,且共用維護成本較低。基於這些原因,我們先前曾嘗試擴充 libFuzzer,而非設計及實作新的模糊測試架構。新的編譯器執行階段 clang_rt.fuzzer-remote.a 會充當上述的遠端程式庫,而 libFuzzer 本身可用於引擎。這兩個編譯器執行階段都會使用一組特定於 OS 的 IPC 傳輸程式庫,將方法呼叫代理至其他程序。

我們與 libFuzzer 維護人員合作,實作了一系列變更,並將兩個執行階段發布供審查。此外,IPC 傳輸程式庫的實作項目也同時為 Linux 和 Fuchsia 開發。維護人員明確要求 Linux 支援功能,以便進行持續測試,因此再次送交審查

  • 在 Linux 上,共用記憶體會以匿名對應檔案的形式建立,也就是透過 memfd_create 建立,而信號則是透過 Unix 網域 Socket 傳遞的簡單訊息。這些 Socket 也用於傳輸共用記憶體檔案描述符,也就是透過 sendmsgrecvmsg
  • 在 Fuchsia 上,共用記憶體是使用 VMOs 實作,信號則是透過 eventpairs 和透過 FIDL 訊息交換,這與本提案中的設計類似。

不幸的是,在延長審查期間,這種做法變得不可行,原因並非技術問題,而是程序問題:隨著時間過去,libFuzzer 維護人員對所需變更的範圍越來越擔心,因為這些變更會讓 libFuzzer 以非原始設計的方式運作。最後,團隊決定無限期延後實施提案的變更。


單一服務 FIDL 模糊測試

AFL

LibFuzzer 絕非唯一的模糊測試框架。有些函式庫 (例如 AFL) 是從一開始就明確設計為跨程序。不過,有幾個原因會導致 AFL 需要比預期更多的投資:

  • AFL 會假設它正在對單一程序進行模糊測試,因此仍會面臨關閉問題。
  • AFL 會大量使用特定的 Linux 和/或 POSIX 功能,用於回饋和錯誤偵測。這些信號包括 POSIX 信號,但更重要的是,系統大量使用 /proc 檔案系統,而 Fuchsia 並沒有類似的系統 (這點沒錯)。
  • AFL 會使用經過修改的 GCC 來檢測程式碼,但這並非 Fuchsia 工具鍊的一部分。

AFLplusplus 是 AFL 的改良分支,由一組安全研究人員和 CTF 競賽者維護。在 FuzzBench 上有出色的效能,且具備模組化 AFL。很抱歉,第一個版本已淘汰,而第二個版本尚未準備就緒 (或至少尚未成熟到足以強制變更上述設計)。不過,有幾個部分與此提案的設計相符,日後有機會整合這些部分,以改善架構的涵蓋率和/或速度。

搭配 QEMU 的 AFL

此外,也有幾個專案將 AFL 與 qemu 結合:

  • afl-unicorn 將 AFL 與 Unicorn 結合,後者是透過相當簡潔的介面公開 qemu 的 CPU 模擬核心的專案。這可透過從 CPU 模擬中收集涵蓋率回饋,在不使用原始碼的情況下模糊處理不透明的二進位檔。以下是幾個不建議採用元件架構的原因:
    • 與 qemu 核心 CPU 模擬的整合作業相當複雜,因此 Unicorn 決定放棄追隨 qemu 的開發,並鎖定在 v2.1.2 (與 qemu 目前的 6.0.0 版相比)。預期較新模擬功能的程式碼可能無法正常運作。
    • 不需要使用不透明的二進位雜湊。事實上,設計只需要將目標程式碼與遠端程式庫進行檢測和連結;LLVM 位元組程式碼就足以達成這項目標。
  • TriforceAFL 會在完整的檢測 qemu 例項上使用 AFL。這項功能會從 qemu 本身收集涵蓋率,再次允許在沒有來源的情況下模糊處理不透明的二進位檔。與 afl-unicorn 相同,此工具不適合用於以下情況:
    • 同樣地,沒有必要使用不透明的二進位數模糊測試。
    • 此外,由於收集到的涵蓋率是整個例項,因此使用 TriforceAFL 進行模糊處理時,系統會產生大量雜訊,尤其是在執行許多元件的情況下。這類測試通常只適用於極度受限的設定,例如開機後立即執行的 USB 驅動程式庫。