模糊 FIDL

在 Fuchsia 上使用 libFuzzer 讓 FIDL 伺服器故障

Fuchsia 提供實驗性支援,能以 FIDL 單元測試樣式編寫 FIDL 模糊工具。

快速入門指南

//examples/fuzzers/fidl 中定義了模糊處理工具範例。如果您不熟悉模糊程序,請參閱總覽。如要對 Fuchsia 上的 libFuzzer 模糊 FIDL 伺服器,您需要使用 GN 目標來產生模糊目標並編寫一些程式碼,以提供要模糊處理的伺服器執行個體。

  1. fuzzers = {protocol = "fully.qualified.fidl.ProtocolName"} 加入 fidl() GN 目標。
  2. 根據您使用的語言而定:
    • C++ 層級 (簡易模式):使用 //sdk/lib/fidl/cpp/fuzzing/server_provider.h 中的 FIDL_FUZZER_DEFINITION() 巨集,定義介面和伺服器實作類別的伺服器供應商。系統會自動定義下述的 C 符號。如需參考範例,請參閱 //examples/fuzzers/fidl
    • C 層級 (硬模式):實作定義下列符號的程式庫:
      • zx_status_t fuzzer_init():將伺服器實作例項化。
      • zx_status_t fuzzer_connect(zx_handle_t, async_dispatcher_t*):將伺服器實作繫結至管道控制代碼。如果您的伺服器與模糊用戶端在同一個執行緒上進行模糊化,請視需要使用調度工具 (請參閱執行緒相關注意事項)。
      • zx_status_t fuzzer_disconnect(zx_handle_t, async_dispatcher_t*):與管道控制代碼解除繫結伺服器實作項目。
      • zx_status_t fuzzer_clean_up():清除伺服器實作。如果其中有任何項目傳回 ZX_OK 以外的狀態,那麼模糊情形將會清理並停止。
  3. 定義 fidl_protocol_fuzzer() GN 目標。指定:
    • fidl = //path/to:fidl_gn_target (上述 fidl() 目標)。
    • protocol = "fully.qualified.fidl.ProtocolName"
    • deps = [... :your_library ...] (上述範例,定義 fuzzer_... 符號)。
    • 其他 fuzzer GN 目標所需的其他項目會破壞伺服器。
  4. 在新的或現有 fuzzer_package GN 目標中,將 fidl_protocol_fuzzer() 目標新增至 fuzzers = [ ... ]

導入作業詳細資料

大部分 FIDL 伺服器的實作模糊目標都是由 fidlgen 產生的 C++ 程式碼,預期少有 C 符號,可為 FIDL 伺服器實作提供 API。產生的程式碼含有繫結至初始執行緒的全域 async::Loop,每次執行模糊目標時,FIDL 連線的用戶端都會重複使用。LibFuzzer 會使用不同的輸入內容重複叫用相同的模糊目標。產生的模糊目標程式碼將會:

  • 叫用 fuzzer_init(),正在初始化要進行模糊處理的伺服器。
  • 將一對 zx::channel 執行個體化。
  • 初始化適當類型的 fidl::InterfacePtr,並繫結至管道端點和迴圈的調度工具。
  • 請叫用 fuzzer_connect(raw_server_channel_handle, loop->dispatcher()),與伺服器建立連線,並允許伺服器選擇使用與用戶端相同的調度工具 (如果其 API 與這類配置相容 (請參閱「執行緒注意事項」)。
  • 透過其 fidl::InterfacePtr 叫用方法。
  • async::Loop 設為執行後直到 idle。
  • 透過 zx::event 與方法的回呼同步。
  • 請叫用 fuzzer_disconnect(raw_server_channel_handle, loop->dispatcher()),允許伺服器清理連線的結尾。
  • 叫用 fuzzer_clean_up() 來拆解伺服器執行個體。

模糊目標輸入至 FIDL 訊息的分配方式

大範圍會使用前兩個位元組,從 FIDL 來源檔案中定義的通訊協定和方法組合中選取。如果 FIDL 檔案包含許多通訊協定,但在模糊器中只啟用一個,則尋找有意義的輸入內容時,需仰賴 LibFuzzer 的涵蓋範圍指引引擎,推斷特定形式的前位元組,會導致模糊功能執行幾乎完全沒有程式碼。

識別通訊協定和方法的組合後,剩餘的位元組會併入以下方式:

  • 每個類型都有各自的特徵,定義所需的位元組數下限。
  • 如果輸入的位元組不足,模糊目標會立即結束。
  • 否則,系統會將超過方法參數最低要求的「slack」位元組平均分配給各個參數,並叫用每種類型的配置特徵,以最多 MinForParam + SlackForParam 個位元組建構適當類型的物件。

就配置特徵詳細資料而言,集合和數字類型會根據要轉換為物件的位元組集合,而有相對自然的解釋。帳號代碼的處理方式與數字類型相同,在伺服器嘗試運動時,處理這類代碼可能會發生錯誤。

執行緒相關注意事項

{#a-note-about-threading}

「強烈」建議您保留單執行緒。也就是說,請在 C++ 中使用 ServerProviderDispatcherMode::kFromCaller,或使用傳遞至 C 中 fuzzer_connectasync_dispatcher_t*。我們建議採用這種做法,因為這會增加模糊功能發現的錯誤持續重現的可能性。

使用 AFL 強化 FIDL 主機工具

此外,過去的實驗已經使用 afl-fuzz 模糊處理 FIDL 編譯器。

建立精力模糊

請下載並建構檔案,然後 export AFL_PATH 做為您下載和用來建構的任何路徑。

修補剖析器,避免納入無效的語法

afl-fuzz 會將當機事件視為有趣,但剖析器目前在遇到無效語法時呼叫 __builtin_trap()。從 parser.h 移除此行 (位於 Parser::Fail() 方法中)。

使用 fl-fuzz 檢測功能建構 fidl 工具

清除任何現有版本,然後使用 afl-fuzz 編譯器包裝函式進行建構。

cd $ZIRCON_DIR
rm -fr build-x86
PATH=$PWD/prebuilt/downloads/clang+llvm-x86_64-linux/bin/:$PATH:$AFL_PATH make \
  build-x86/tools/fidl HOST_TOOLCHAIN_PREFIX=afl-

如果並非在 x86 Linux 等平台上建構,請進行調整。

執行模糊效果器

剖析器提供了一些可做為輸入的範例。隨著 FIDL 採用,我們可以擴大輸入範圍,以納入樹狀結構中宣告的所有不同通訊協定,但現在我們會使用 tools/fidl/examples 中的內容。

$AFL_PATH/afl-fuzz -i tools/fidl/examples -o fidl-fuzz-out build-x86/tools/fidl dump '@@'

成果

從 2017 年 5 月初對來源執行,在速度相當快的機器上,兩天後沒有任何當機或停止運作。超過 3 億次的執行作業