1# 测试框架Fuzzing安全测试指导 2 3- [Fuzzing简介](#section7730298375831) 4- [Fuzzing测试关注的风险接口](#section00067164921) 5- [使用测试框架DTFuzz](#section0009871491) 6 - [配置启动测试框架](#section005187487501) 7 - [单个Fuzz用例初始化](#section372106507189) 8 - [Fuzz用例编写](#section98175917) 9 - [Fuzz用例编译](#section00816581589) 10 - [Fuzz用例执行](#section7510974319) 11- [测试结果与日志](#section016190470) 12 13## Fuzzing简介<a name="section7730298375831"></a> 14 15模糊测试(fuzzing test)是一种软件测试技术,其核心思想是将自动或半自动生成的随机数据输入到一个程序中,并监视程序异常,如崩溃,断言(assertion)失败,以发现可能的程序错误,比如内存泄漏,访问越界等。 16 17Fuzzing测试框架使用了LLVM编译器框架中的[libFuzzer](https://llvm.org/docs/LibFuzzer.html)作为Fuzzing引擎进行构建,libFuzzer是一个基于LLVM编译时路径插桩,可以对被测库API进行路径引导测试的Fuzzing引擎。 18 19使用Fuzzing测试框架,需要完成fuzzer测试用例初始化、fuzzer用例编写、fuzzer用例编译和fuzzer用例执行几步。 20 21 22 23## Fuzzing测试关注的风险接口<a name="section00067164921"></a> 24 25开发者应该了解自身模块的接口数据输入是基于不可信的来源输入,特别是针对 26 27- 解析处理远程发送来的TCP/UDP或者蓝牙等协议数据 28- 通过复杂的文件解码处理等,包括音视频、图片解码、解压缩等 29- IPC跨进程的数据输入处理 30 31通过Fuzzing的覆盖引导能力,可以有效的探测和消减外部输入造成的内存安全问题,也可以极大的增强系统稳定性。 32 33 34 35## 使用测试框架开展Fuzzing<a name="section0009871491"></a> 36 37### 配置启动测试框架<a name="section005187487501"></a> 38 39参考[ 开发者测试组件](https://gitee.com/openharmony/testfwk_developer_test/blob/master/README_zh.md)中的描述完成测试框架安装、设备连接配置,并在linux环境下通过 40 41``` 42./start.sh 43``` 44 45启动测试框架。 46 47 48 49### 单个Fuzz用例初始化<a name="section372106507189"></a> 50 511. Fuzz测试用例生成 52 53 执行gen命令用于fuzzer源文件生成,会自动生成fuzzer源文件、fuzzer配置文件和corpus语料,目录结构如下 54 55 ``` 56 calculator_fuzzer/ 57 ├── corpus # Fuzz语料目录 58 │ ├── init # Fuzz语料 59 ├── BUILD.gn # Fuzz用例编译配置 60 ├── calculator_fuzzer.cpp # Fuzz用例源文件 61 ├── calculator_fuzzer.h # Fuzz用例头文件 62 ├── project.xml # Fuzz选项配置文件 63 ``` 64 652. 命令参数说明,参数可以指定fuzzer名称和fuzzer路径 66 67 ``` 68 gen -t TESTTYPE -fn FUZZERNAME -dp DIRECTORYPATH 69 ``` 70 71 | 参数 | 描述 | 说明 | 备注 | 72 | ---- | ---------- | -------------- | ------------------------------------------ | 73 | -t | testtype | 测试类型 | 目前仅支持"FUZZ" | 74 | -fn | fuzzername | fuzzer名称 | 为显式区分Fuzz用例,名称必须以测试套前缀小写 + _fuzzer形式命名 | 75 | -dp | dirpath | fuzzer生成路径 | 路径不存在则自动创建目录 | 76 773. gen命令示例,-t、-fn和-dp均为必选项 78 79 ``` 80 gen -t FUZZ -fn calculator_fuzzer -dp base/account/os_account/test/fuzztest/osaccount 81 ``` 82 83 这里以os_accont模块作为示例演示fuzz,执行完毕后会在base/account/os_account/test/fuzztest/osaccount目录下生成一个Fuzz用例demo。 84 85 86 87### Fuzz用例编写<a name="section98175917"></a> 88 891. 源文件编写 90 91 Fuzz用例主要在**${fuzzer名称}.cpp**源文件中,一个Fuzz用例仅支持一个接口进行fuzz测试。 92 93 源文件包含两个接口: 94 95 | 接口 | 说明 | 96 | ------------------------------- | -------------------------------- | 97 | LLVMFuzzerTestOneInput | Fuzz入口函数,由Fuzz框架调用 | 98 | DoSomethingInterestingWithMyAPI | 被测试接口,实现各业务被测试逻辑 | 99 100 ![img](../../public_sys-resources/icon-note.gif) **说明:** DoSomethingInterestingWithMyAPI接口名称允许依据业务逻辑修改。两接口参数data和size为fuzz测试标准化参数,不可修改。 101 102 ``` 103 #include "calculator_fuzzer.h" 104 105 #include <stddef.h> 106 #include <stdint.h> 107 108 const int FUZZ_DATA_LEN = 3; 109 const int FUZZ_FST_DATA = 0; 110 const int FUZZ_SND_DATA = 1; 111 const int FUZZ_TRD_DATA = 2; 112 const int FUZZ_FTH_DATA = 3; 113 114 namespace OHOS { 115 bool DoSomethingInterestingWithMyAPI(const uint8_t* data, size_t size) 116 { 117 bool result = false; 118 if (size >= FUZZ_DATA_LEN) { 119 result = data[FUZZ_FST_DATA] == 'F' && 120 data[FUZZ_SND_DATA] == 'U' && 121 data[FUZZ_TRD_DATA] == 'Z' && 122 data[FUZZ_FTH_DATA] == 'Z'; 123 } 124 return result; 125 } 126 } 127 128 /* Fuzzer entry point */ 129 extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) 130 { 131 /* Run your code on data */ 132 OHOS::DoSomethingInterestingWithMyAPI(data, size); 133 return 0; 134 } 135 ``` 136 ### 注意:当data需强制转换为字符串类型时,需要携带size,例如: 137 ``` 138 std::string result((const char*) data, size); 139 ``` 140 141 142 1432. BUILD.gn编写 144 145 基于[ohos_fuzztest]配置Fuzz模板,例如: 146 147 需要注意的是fuzz_config_file, 使用gen命令生成的BUILD.GN文件中没有指明,需要写完测试套后加在BUILD.gn中 148 149 ``` 150 ohos_fuzztest("CalculatorFuzzTest") { #定义测试套名称CalculatorFuzzTest 151 module_out_path = module_output_path 152 fuzz_config_file = "//base/account/os_account/test/fuzztest/osaccount/calculator_fuzzer" 153 include_dirs = [] 154 cflags = [ 155 "-g", 156 "-O0", 157 "-Wno-unused-variable", 158 "-fno-omit-frame-pointer", 159 ] 160 sources = [ "calculator_fuzzer.cpp" ] 161 } 162 ``` 163 164 [group]引用测试套,例如: 165 166 ``` 167 group("fuzztest") { 168 testonly = true 169 deps = [] 170 171 deps += [ 172 # deps file 173 ":CalculatorFuzzTest", #引用测试套 174 ] 175 } 176 ``` 177 **注意:** 178 - #### 测试套名称必须采用大驼峰风格,并且必须以FuzzTest结尾,测试套前缀与fuzzer目录名相对应(例如:calculator_fuzzer,只能有一个下划线)。 179 - module_out_path为测试套编译输出目录,内容为部件+模块名,例如"os_account/os_account"。 180 181 1823. Fuzz配置编写 183 184 project.xml为DTFuzz参数配置文件,提供基于libFuzzer参数的动态配置,更多参数配置可参考[libFuzzer参数配置](https://llvm.org/docs/LibFuzzer.html#options): 185 186 ``` 187 <!-- maximum length of a test input --> 188 <max_len>1000</max_len> 189 <!-- maximum total time in seconds to run the DTFuzz --> 190 <max_total_time>300</max_total_time> 191 <!-- memory usage limit in Mb --> 192 <rss_limit_mb>4096</rss_limit_mb> 193 ``` 194 需要注意的是这里fuzz测试用例设置的默认的运行时间为300s, 实际使用中可以根据自己使用需要进行更改。 195 196 197### Fuzz用例编译<a name="section00816581589"></a> 198 199添加Fuzz用例编译到模块的test_list中: 200 2011. 在需要DTFuzz测试的对应模块bundle.json中添加Fuzz用例路径,如在bundle.json添加: 202 203 ``` 204 "tests": [ 205 "//test/developer_test/examples/calculator/test:unittest", 206 "//test/developer_test/examples/calculator/test:fuzztest", #添加DTFuzz用例路径 207 "//test/developer_test/examples/detector/test:unittest", 208 "//test/developer_test/examples/sleep/test:performance", 209 "//test/developer_test/examples/distributedb/test:distributedtest" 210 ] 211 ``` 212 2132. 在用例路径下的BUILD.gn添加group,例如在os_account/test/fuzztest/osaccount/calculator_fuzzer的BUILD.gn中添加 214 215 ``` 216 group("fuzztest") { 217 testonly = true 218 deps = [] 219 220 deps += [ "calculator_fuzzer:fuzztest" ] 221 } 222 ``` 2233. 编译时添加--build-target选项,如 224 225 ``` 226 ./build.sh --product-name rk3568 --build-target CalculatorFuzzTest 227 ``` 228 229 230### Fuzz用例执行<a name="section7510974319"></a> 231 232Fuzz能力集成,在测试类型-t中新增FUZZ类型,执行Fuzz测试指令示例,其中-t为必选,-ss和-tm为可选 233 234``` 235run -t FUZZ -ss developer_test -tm calculator 236``` 237 238| 参数 | 描述 | 说明 | 备注 | 239| ---- | ---------- | -------- | ------------------------ | 240| -t | TESTTYPE | 测试类型 | | 241| -ss | SUBSYSTEM | 子系统 | 被测试子系统 | 242| -tm | TESTMODULE | 模块 | 被测试模块,如calculator | 243 244- Windows环境脱离源码执行 245 246 首先将testfwk_developer_test 和 testfwk_xdevice 两个仓库的代码下载到本地,这里假设放到D盘并在D:\test 目录下创建developer_test, xdevice和tests三个目录,developer_test和xdevice目录分别存放对应仓库的代码。 247 248 Windows环境可通过归档DTFuzz用例配置文件project.xml、语料corpus和可执行文件执行DTFuzz。 249 250 1. 归档用例配置文件、语料以及用例可执行文件 251 252 新建目录,如: 253 #### 注意:必须是\tests目录 254 255 ``` 256 D:\test\tests 257 ``` 258 259 用例可执行文件为DTFuzz源文件编译产出文件,以二进制形式存储在out/release/tests/fuzztest下。测试用例的配置文件均编译输出在out/release/tests/res目录下对应的xxxx_fuzzer目录中。(release为对应的产品名,如编译rk3568则在out/rk3568下面) 260 将fuzztest目录以及res目录直接拷贝到该路径下即可。 261 262 263 2. 配置用例路径 264 265 在developer_test\config\user_config.xml中配置用例归档路径: 266 267 ``` 268 <!-- configure test cases path --> 269 <test_cases> 270 <dir>D:\test\tests</dir> #用例可执行文件归档路径 271 </test_cases> 272 ``` 273 274 3. 执行用例 275 276 在developer_test下执行./start.bat开启测试框架,执行DTFuzz命令示例 277 278 ``` 279 run -t FUZZ -ts CalculatorFuzzTest 280 ``` 281 282 283 284## 测试结果与日志<a name="section016190470"></a> 285 286- 通过在测试框架中执行测试指令,即可以生成测试日志和测试报告。 287 288- 测试结果 289 290 测试用例的结果会直接显示在控制台上,执行一次的测试结果根路径如下: 291 292 ``` 293 reports/xxxx-xx-xx-xx-xx-xx 294 ``` 295 296 测试用例格式化结果 297 298 ``` 299 result/ 300 ``` 301 302 测试用例日志 303 304 ``` 305 log/plan_log_xxxx-xx-xx-xx-xx-xx.log 306 ``` 307 308 测试报告汇总 309 310 ``` 311 summary_report.html 312 ``` 313 314 测试报告详情 315 316 ``` 317 details_report.html 318 ``` 319 320 最新测试报告 321 322 ``` 323 reports/latest 324 ``` 325 326