Fuchsia 的工具鍊支援使用 LLVM 的 libFuzzer 執行模糊作業。如要為特定介面建立模糊化工具,您必須實作「模糊目標函式」,這個函式會使用所提供的位元組序列練習介面。位元組序列稱為模糊的「輸入」。libFuzzer 會使用模糊目標函式搜尋導致恐慌或其他錯誤的輸入內容。
Fuzz 程式碼範例
針對下列每個範例,假設您要測試程式碼,如下所示:
C/C++
class Parser {
Parser(const std::string &name, uint32_t flags);
virtual ~Parser();
int Parse(const uint8 *buf, size_t len);
};
Rust
struct ToyStruct {
n: u8,
s: String,
}
fn toy_example(input: ToyStruct) -> Result<u8, &'static str>;
簡單的 Fuzz 目標函式
針對各種語言,模糊目標函式將使用提供的位元組呼叫要進行模糊化的程式碼。如果正在模糊化的介面已記錄了參數的限制,您可以拒絕不符合這些限制條件的輸入。您也可以忽略傳回的錯誤,因為對無效參數執行失敗是正確的行為。
C/C++
如果是 C 和 C++,模糊目標函式必須包含 extern "C" int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size)
簽名並傳回 0:
extern "C" int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size) {
Parser parser("", 0);
if (size < 5) {
return 0;
}
parser.Parse(data, size);
return 0;
}
請將此程式碼放在受到模糊化程式碼的來源檔案附近,與單元測試一樣。舉例來說,上述程式碼可能位於 parser-fuzztest.cc
。
Rust
針對 Rust,建議您使用下一節討論的 Arbitrary
特徵。
您也可以建立「手動」模糊目標函式,用於其他語言的簡單模糊目標函式。這個函式必須參照位元組切片做為單一參數、不傳回任何內容,且具有 #[fuzz]
屬性:
use fuzz::fuzz;
#[fuzz]
fn toy_example_u8(input: &[u8]) {
if input.len() == 0 {
return
}
let n = input[0];
if let Ok(s) = std::str::from_utf8(input[1:]) {
let _ = toy_example(ToyStruct{n, s: s.to_string(),});
}
}
與單元測試一樣,此程式碼可以和其測試的程式碼放在同一個檔案中。舉例來說,上述程式碼可能位於 toy_example/src/lib.rs
。
支援更複雜的類型
每種語言都有一些公用程式,可幫助您建立更複雜的模糊目標函式:
C/C++
LLVM 提供的 FuzzedDataProvider
類別可協助您將提供的 data
的部分對應至更複雜的類型。
例如:
#include <fuzzer/FuzzedDataProvider.h>
extern "C" int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size) {
FuzzedDataProvider provider(data, size);
auto flags = provider.ConsumeIntegral<uint32_t>();
auto name = provider.ConsumeRandomLengthString();
Parser parser(name, flags);
auto buf = provider.ConsumeRemainingBytes<uint8_t>();
parser.Parse(buf.data(), buf.size());
return 0;
}
使用這個程式庫有兩項明顯的優點:
- 第一,這樣比較容易快速寫出模糊字元。
- 其次,其設計能以動態方式分割輸入,讓模糊程序能有效從涵蓋率資料建立新的輸入。
有一個明顯的缺點:
Rust
您可以建立模糊目標函式,以使用 arbitrary
Crate 中的 Arbitrary
特徵擷取一或多個輸入內容。這是我們建議的方法。
如何編寫容易自動轉換任意輸入內容的模糊目標函式:
如有需要,請針對測試程式碼使用的類型實作
Arbitrary
特徵。如果可以,建議透過自動衍生特徵來達到這個目的。或者,您也可以按照 crate 的操作說明「手動」完成這項作業。例如,在您的
src/lib.rs
中:use arbitrary:Arbitrary; #[derive(Arbitrary)] struct ToyStruct { ... }
使用
#[fuzz]
屬性建立函式,將必要參數傳送至要測試的程式碼。例如,在您的
src/lib.rs
中:use fuzz::fuzz; #[fuzz] fn toy_example_arbitrary(input: ToyStruct) { let _ = toy_example(input); }
接下來,您可以使用 GN 和 Ninja 建構新建立的模糊作業。