README_zh.md
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/test_developertest/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 test/developertest/example/calculator/test/fuzztest/common
81 ```
82
83 执行完毕后会在test/developertest/example/calculator/test/fuzztest/common目录下生成一个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  **说明:** 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 = "//test/developertest/examples/calculator/test/fuzztest/common/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为测试套编译输出目录,内容为部件+模块名。
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
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/developertest/examples/calculator/test:unittest",
206 "//test/developertest/examples/calculator/test:fuzztest", #添加DTFuzz用例路径
207 "//test/developertest/examples/detector/test:unittest",
208 "//test/developertest/examples/sleep/test:performance",
209 "//test/developertest/examples/distributedb/test:distributedtest"
210 ]
211 ```
212
2132. 在用例路径下的BUILD.gn添加group,如examples/calculator/test的BUILD.gn
214
215 ```
216 group("fuzztest") {
217 testonly = true
218 deps = []
219
220 deps += [ "fuzztest/common/calculator_fuzzer:fuzztest" ]
221 }
222 ```
223
224
225
226### Fuzz用例执行<a name="section7510974319"></a>
227
228Fuzz能力集成,在测试类型-t中新增FUZZ类型,执行Fuzz测试指令示例,其中-t为必选,-ss和-tm为可选
229
230```
231run -t FUZZ -ss developertest -tm calculator
232```
233
234| 参数 | 描述 | 说明 | 备注 |
235| ---- | ---------- | -------- | ------------------------ |
236| -t | TESTTYPE | 测试类型 | |
237| -ss | SUBSYSTEM | 子系统 | 被测试子系统 |
238| -tm | TESTMODULE | 模块 | 被测试模块,如calculator |
239
240- Windows环境脱离源码执行
241
242 Windows环境可通过归档DTFuzz用例配置文件project.xml、语料corpus和可执行文件执行DTFuzz。
243
244 1. 归档用例配置文件、语料以及用例可执行文件
245
246 新建目录,如:
247 #### 注意:必须是\tests目录
248
249 ```
250 D:\test\tests
251 ```
252
253 用例可执行文件为DTFuzz源文件编译产出文件,以二进制形式存储在out/release/tests/fuzztest下。测试用例的配置文件均编译输出在out/release/tests/res目录下对应的xxxx_fuzzer目录中。
254 将fuzztest目录以及res目录直接拷贝到该路径下即可。
255
256
257 2. 配置用例路径
258
259 config\user_config.xml中配置用例归档路径:
260
261 ```
262 <!-- configure test cases path -->
263 <test_cases>
264 <dir>D:\test\tests</dir> #用例可执行文件归档路径
265 </test_cases>
266 ```
267
268 3. 执行用例
269
270 执行DTFuzz命令示例
271
272 ```
273 run -t FUZZ -ts CalculatorFuzzTest
274 ```
275
276
277
278## 测试结果与日志<a name="section016190470"></a>
279
280- 通过在测试框架中执行测试指令,即可以生成测试日志和测试报告。
281
282- 测试结果
283
284 测试用例的结果会直接显示在控制台上,执行一次的测试结果根路径如下:
285
286 ```
287 reports/xxxx-xx-xx-xx-xx-xx
288 ```
289
290 测试用例格式化结果
291
292 ```
293 result/
294 ```
295
296 测试用例日志
297
298 ```
299 log/plan_log_xxxx-xx-xx-xx-xx-xx.log
300 ```
301
302 测试报告汇总
303
304 ```
305 summary_report.html
306 ```
307
308 测试报告详情
309
310 ```
311 details_report.html
312 ```
313
314 最新测试报告
315
316 ```
317 reports/latest
318 ```
319
320