使用 DataProvider 功能进行测试

除了使用字节流对 API 进行测试的方法之外,fuzz 包提供了 FuzzDataProvider 类,用于更友好地从变异的数据源产生仓颉的标准数据类型,方便对 API 进行测试。

public func api2(dp: FuzzDataProvider): Int32 {
    if(dp.consumeBool() && dp.consumeByte() == b'A' && dp.consumeuint32() == 0xdeadbeef){
        throw Exception("TRAP")
    }
    return 0
}

此案例中,开启 --sanitizer-coverage-trace-compares 可有效提高 fuzz 效率。

DataProvider 模式下,无法直观地判断各个 API 返回值分别是什么,因此提供了 Fuzzer.enableDebugDataProvider() 和 DebugDataProvider,在 startFuzz 前调用 enableDebugDataProvider() 即可令本次 fuzz 每次调用 consumeXXX 时打印日志。

例如,上文代码触发异常后,添加 enableDebugDataProvider 重新编译,效果如下。

import fuzz.fuzz.*

main() {
    let fuzzer = Fuzzer(api2)
    fuzzer.enableDebugDataProvider()
    fuzzer.startFuzz()
    return 0
}

运行结果

./main crash-d7ece8e77ff25769a5d55eb8d3093d4bace78e1b
Running: crash-d7ece8e77ff25769a5d55eb8d3093d4bace78e1b
[DEBUG] consumeBool return true
[DEBUG] consumeByte return 65
[DEBUG] consumeUInt32 return 3735928559
[WARNING]: Detect uncatched exception, maybe caused by bugs, exit now
An exception has occurred:
Exception: TRAP
         at default.api2(fuzz/fuzz::FuzzDataProvider)(/tmp/test.cj:12)
         at _ZN7default4api2EC_ZN9fuzz$fuzz16FuzzDataProviderE_cc_wrapper(/tmp/test.cj:0)
         at libfuzzerCallback(fuzz/fuzz/callback.cj:0)
[INFO]: data is: [191, 65, 239, 190, 173, 222]