在 Fuchsia 上使用 libFuzzer 讓 FIDL 伺服器故障
Fuchsia 提供實驗性支援,能以 FIDL 單元測試樣式編寫 FIDL 模糊工具。
快速入門指南
//examples/fuzzers/fidl
中定義了模糊處理工具範例。如果您不熟悉模糊程序,請參閱總覽。如要對 Fuchsia 上的 libFuzzer 模糊 FIDL 伺服器,您需要使用 GN 目標來產生模糊目標並編寫一些程式碼,以提供要模糊處理的伺服器執行個體。
- 將
fuzzers = {protocol = "fully.qualified.fidl.ProtocolName"}
加入fidl()
GN 目標。 - 根據您使用的語言而定:
- 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
以外的狀態,那麼模糊情形將會清理並停止。
- C++ 層級 (簡易模式):使用
- 定義
fidl_protocol_fuzzer()
GN 目標。指定:fidl = //path/to:fidl_gn_target
(上述fidl()
目標)。protocol = "fully.qualified.fidl.ProtocolName"
。deps = [... :your_library ...]
(上述範例,定義fuzzer_...
符號)。- 其他
fuzzer
GN 目標所需的其他項目會破壞伺服器。
- 在新的或現有
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_connect
的 async_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 億次的執行作業