撰寫模糊效果

Fuchsia 的工具鍊支援使用 LLVM 的 libFuzzer 進行模糊測試。如要針對特定的 介面,您需要實作模糊目標函式, 提供的位元組序列來練習介面。位元組順序稱為 模糊的「input」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>;

簡單的模糊目標函式

針對每種語言,模糊目標函式會使用所提供的位元組來呼叫所需的程式碼。 進行模糊化處理如果模糊介面記錄了參數的限制,您可以拒絕 不符合這些限制條件的輸入值您也可以忽略因失敗而傳回的錯誤 謹慎地處理無效參數,才是正確行為。

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;
  }

使用這個程式庫有兩項重要優點:

  • 首先,您可以更輕鬆地快速撰寫模糊版本。
  • 第二,這項服務可以動態分割輸入 有效率地建立新的輸入資料。

其中有個明顯的缺點:

  • 由於輸入內容是以動態方式分割,因此要提供 corpus。不過,您還是可以提供 字典

Rust

您可以使用 Arbitrary 特徵建立模糊目標函式,以接收一或多個輸入內容 擷取自 arbitrary Crate。建議您採取這種做法。

如何編寫能自動轉換任意輸入內容的模糊目標函式:

  1. 如有需要,請針對測試程式碼使用的類型實作 Arbitrary 特徵。可能的話 建議的產生方法是自動衍生特徵否則,您可以使用 「手動完成」按照 Crate 的操作說明進行。

    例如,在 src/lib.rs 中:

    use arbitrary:Arbitrary;
    
    #[derive(Arbitrary)]
    struct ToyStruct { ... }
    
  2. 建立含有傳遞必要要求的 #[fuzz] 屬性的函式 參數加到要測試的程式碼

    例如,在 src/lib.rs 中:

    use fuzz::fuzz;
    
    #[fuzz]
    fn toy_example_arbitrary(input: ToyStruct) {
        let _ = toy_example(input);
    }
    

接下來,您可以使用 GN 和 Ninja 建構新建立的模糊工具。