Fuchsia 的工具鍊支援使用 LLVM 的 libFuzzer 進行模糊測試。如要為特定介面建立模糊測試器,您需要實作模糊測試目標函式,使用提供的位元組序列來執行介面。這個位元組序列稱為模糊測試器「輸入」。libFuzzer 會使用模糊測試目標函式,搜尋導致恐慌或其他錯誤的輸入內容。
模糊測試的程式碼範例
在下列每個範例中,假設您要測試的程式碼如下:
C/C++
class Parser {
Parser(const std::string &name, uint32_t flags);
virtual ~Parser();
int Parse(const uint8 *buf, size_t len);
};
荒漠油廠
struct ToyStruct {
n: u8,
s: String,
}
fn toy_example(input: ToyStruct) -> Result<u8, &'static str>;
簡單的模糊測試目標函式
對於每種語言,模糊測試目標函式都會使用提供的位元組,呼叫您要模糊測試的程式碼。如果模糊測試的介面有參數的記錄限制,您可以拒絕不符合這些限制的輸入內容。您也可以忽略傳回的錯誤,因為針對無效參數正常失敗是正確的行為。
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,建議使用下一節討論的 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;
}
使用這個程式庫有兩項顯著優點:
- 首先,這項功能可讓您更輕鬆地快速編寫模糊測試器。
- 其次,這項工具的設計目的是動態分割輸入內容,讓模糊測試器能根據涵蓋範圍資料有效率地建立新輸入內容。
這項技術有一項明顯缺點:
荒漠油廠
您可以建立模糊測試目標函式,使用 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 建構新建立的模糊測試器。