模糊 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/hlcpp/fuzzing/include/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 設為 run-until-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}

建議盡量讓模糊測試目標保持單執行緒。也就是說,請使用 ServerProviderDispatcherMode::kFromCaller使用 C++ 中的 或 async_dispatcher_t*, 以 C 表示:fuzzer_connect這是較佳做法,因為這樣可提高 fuzzer 發現的錯誤能持續重現的可能性。

使用 AFL 對 FIDL 主機工具進行模糊測試

此外,過去的實驗成果已使用 afl-fuzz 對 FIDL 編譯器進行模糊測試。

建構 afl-fuzz

下載並建構,然後用 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 億次。