模糊测试(fuzzing test)是一种软件测试技术,其核心思想是将自动或半自动生成的随机数据输入到一个程序中,并监视程序异常,如崩溃,断言(assertion)失败,以发现可能的程序错误,比如内存泄漏,访问越界等。
Fuzzing测试框架使用了LLVM编译器框架中的libFuzzer作为Fuzzing引擎进行构建,libFuzzer是一个基于LLVM编译时路径插桩,可以对被测库API进行路径引导测试的Fuzzing引擎。
使用Fuzzing测试框架,需要完成fuzzer测试用例初始化、fuzzer用例编写、fuzzer用例编译和fuzzer用例执行几步。
开发者应该了解自身模块的接口数据输入是基于不可信的来源输入,特别是针对
通过Fuzzing的覆盖引导能力,可以有效的探测和消减外部输入造成的内存安全问题,也可以极大的增强系统稳定性。
将toolchains添加在环境变变中:
在cmd中用hdc_std shell检测
安装Linux扩展组件readline,安装命令如下:
sudo apt-get install libreadline-dev
安装setuptools插件,安装命令如下:
pip3 install setuptools
安装paramiko插件,安装命令如下:
pip3 install paramiko
安装python的rsa插件,安装命令如下:
pip3 install rsa
安装串口插件pyserial,安装命令如下:
pip3 install pyserial
如果设备仅支持串口输出测试结果,则需要安装NFS Server
sudo apt install nfs-kernel-server
root@ubuntu:/home/OpenHarmony/test/developertest#
./start.sh
Please select the current tested product form:
xxx
xxx
xxx
(输入对应数字的产品;回车)
或者
在Windows下点击start.bat;
输入输入对应数字的产品;回车
Fuzz用例初始化
>>> gen -t FUZZ -fn xxx_fuzzer -dp fuzztest/
>>> gen -t FUZZ -fn xxx_fuzzer -dp fuzztest/
>>> quit
在Windows下:
>>> gen -t FUZZ -fn xxx_fuzzer -dp D:\xxx\xxx
>>> gen -t FUZZ -fn xxx_fuzzer -dp D:\file_work
执行gen命令用于fuzzer源文件生成,会自动生成fuzzer源文件、fuzzer配置文件和corpus语料,目录结构如下:
xxx_fuzzer/
├── corpus # Fuzz语料目录
│ ├── init # Fuzz语料
├── BUILD.gn # Fuzz用例编译配置
├── xxx_fuzzer.cpp # Fuzz用例源文件
├── xxx.h # Fuzz用例头文件
├── project.xml # Fuzz选项配置文件
命令参数说明,参数可以指定fuzzer名称和fuzzer路径
gen -t TESTTYPE -fn FUZZERNAME -dp DIRECTORYPATH
参数 | 描述 | 说明 | 备注 |
---|---|---|---|
-t | testtype | 测试类型 | 目前仅支持"FUZZ" |
-fn | fuzzername | fuzzer名称 | 为显式区分Fuzz用例,名称必须以测试套前缀小写 + _fuzzer形式命名 |
-dp | dirpath | fuzzer生成路径 | 路径不存在则自动创建目录 |
gen命令的参数-t、-fn和-dp均为必选项;
注意:更改每个文件注释里的年份为当年!!!
源文件编写
Fuzz用例主要在{fuzzer名称}.cpp源文件中,一个Fuzz用例仅支持一个接口进行fuzz测试。
源文件包含两个接口:
接口 | 说明 |
---|---|
LLVMFuzzerTestOneInput | Fuzz入口函数,由Fuzz框架调用 |
DoSomethingInterestingWithMyAPI | 被测试接口,实现各业务被测试逻辑 |
说明: DoSomethingInterestingWithMyAPI接口名称允许依据业务逻辑修改。两接口参数data和size为fuzz测试标准化参数,不可修改。
#include <stddef.h>
#include <stdint.h>
#include "xxx_fuzzer.h"
void OHOS::xxx::xxxTest(const uint8_t* data, size_t size)
{
if ((data == nullptr) || (size <= 0)) {
return;
}
XXX xxx;
size -= sizeof(uint32_t);
xxx.WriteBuffer(data + sizeof(uint32_t), size);
xxx.RewindRead(0);
XXXX xxxx;
xxxx.Marshalling(xxx);
xxxx.Unmarshalling(xxx);
}
/* Fuzzer entry point */
extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size)
{
/* Run your code on data */
OHOS::xxx::xxxxTest(data, size);
return 0;
}
头文件编写
#ifndef OHOS_xxx_FUZZER_H
#define OHOS_xxx_FUZZER_H
#include <cstdint>
#include <cstdio>
#include <cstdlib>
#include <fcntl.h>
#include <unistd.h>
#define FUZZ_PROJECT_NAME "xxx_fuzzer"
namespace OHOS::xxx{
void xxxTest(const uint8_t* data, size_t size);
}
#endif
BUILD.gn编写
#####################hydra-fuzz###################
import("//build/config/features.gni")
import("//build/test.gni")
module_output_path = "xxx/xxx"
##############################fuzztest##########################################
ohos_fuzztest("xxxFuzzTest") {
module_out_path = module_output_path
fuzz_config_file =
"//foundation/xxx/xxx/xxx_fuzzer"
include_dirs = [
"//foundation/xxx/xxx/test/fuzztest"
]
cflags = ["-g","-O0","-Wno-unused-variable","-fno-omit-frame-pointer"]
sources = [
"xxx_fuzzer.cpp",
]
deps = [
"//foundation/xxx/xxx:xxx",
"//utils/native/base:utils"
]
external_deps = [
"xxx:xxx",
]
}
group("fuzztest") {
testonly = true
deps = []
deps += [
# deps file
":xxxFuzzTest",
]
}
基于[ohos_fuzztest]配置Fuzz模板,例如:
ohos_fuzztest("xxxFuzzTest") { #定义测试套名称CalculatorFuzzTest
module_out_path = module_output_path
include_dirs = [
]
cflags = ["-g","-O0","-Wno-unused-variable","-fno-omit-frame-pointer"]
sources = [
"xxx_fuzzer.cpp",
]
}
[group]引用测试套,例如:
group("fuzztest") {
testonly = true
deps = []
deps += [
# deps file
":xxxFuzzTest", #引用测试套
]
}
Fuzz配置编写
project.xml为DTFuzz参数配置文件,提供基于libFuzzer参数的动态配置,更多参数配置可参考:
https://llvm.org/docs/LibFuzzer.html#options
<!-- maximum length of a test input -->
<max_len>1000</max_len>
<!-- maximum total time in seconds to run the DTFuzz -->
<max_total_time>300</max_total_time>
<!-- memory usage limit in Mb -->
<rss_limit_mb>4096</rss_limit_mb>
int32_t xxx= *(reinterpret_cast<const int32_t*>(data));
bool xxx= *(reinterpret_cast<const bool*>(data));
std::string xxx(reinterpret_cast<const char*>(data), size);
添加Fuzz用例编译到模块的test_list中:
在需要DTFuzz测试的对应模块bundle.json中添加Fuzz用例路径,如在bundle.json添加:
"tests": [
"//foundation/xxx/xxx/test/unittest:unittest",
"//foundation/xxx/xxx/test/fuzztest:fuzztest", #添加DTFuzz用例路径
]
在用例路径下的BUILD.gn添加group,如examples/calculator/test的BUILD.gn
group("fuzztest") {
testonly = true
deps = []
deps += [ "fuzztest/xxx_fuzzer:fuzztest" ]
}
编译命令:
./build.sh --product-name rk3568 --export-para PYCACHE_ENABLE:true --ccache --build-target xxx_test
上32位,下64位;
./build.sh --product-name rk3568 --ccache --target-cpu arm64 --build-target xxx_test
#单独生成group的测试用例
./build.sh --product-name rk3568 --build-target xxxFuzzTest
生成测试用例位置:
root@ubuntu:/home/xxx/OpenHarmony/out/rk3568/tests/fuzztest/xxx/xxx#
windows下测试用例位置:
D:\file_work\22.6.25(FuzzTest)\tests\fuzztest\xxx\xxx
添加路径:D:\file_work\22.6.25(FuzzTest)\developertest\config下的user_config.xml文件
<test_cases>
<dir>D:\file_work\22.6.25(FuzzTest)\tests</dir>
</test_cases>
修改D:\file_work\22.6.25(FuzzTest)\developertest\config下的user_config.xml文件(非必要)
<testcase>false</testcase>
(重要)在D:\file_work\22.6.25(FuzzTest)\tests\res目录下:
添加xxx_fuzzer(测试用例目录)
......
复制两文件到本地
/home/si/openharmony/test$ ls
将developertest和xdevice复制到D:\file_work\22.6.13(test)
#########################################
## Fuzz用例执行
执行前需要重新全量编译+烧录rk3568
全量编译+烧录操作看笔记
运行start.bat文件闪退
熟悉流程可以执行下一步操作
修改start.bat文件(非必要操作)
@echo off
set BASE_HOME_PATH=%CD%
@echo start time is:
TIME /T
cd %BASE_HOME_PATH%\src
python -m main %1
cd %BASE_HOME_PATH%
@echo finish time is:
TIME /T
@echo on
参考启动测试框架
Fuzz能力集成,在测试类型-t中新增FUZZ类型,执行Fuzz测试指令示例,其中-t为必选,-ss和-tm为可选
run -t FUZZ -ss developertest -tm calculator
| 参数 | 描述 | 说明 | 备注 |
| ---- | ---------- | -------- | ------------------------ |
| -t | TESTTYPE | 测试类型 | |
| -ss | SUBSYSTEM | 子系统 | 被测试子系统 |
| -tm | TESTMODULE | 模块 | 被测试模块,如calculator |
执行测试框架:
运行start.bat,输入3,回车(或选择rk3568回车)
执行DTFuzz命令示例:(不加单个测试用例就是全跑)
run -t FUZZ -ts CalculatorFuzzTest
run -t FUZZ
(可选)
在cmd中运行:
hdc_std shell
运行之后输入top查看测试进程运行时间:
top
## 测试结果与日志
- 通过在测试框架中执行测试指令,即可以生成测试日志和测试报告。
路径:D:\file_work\22.6.25(FuzzTest)\developertest\reports
- 测试结果
测试用例的结果会直接显示在控制台上,执行一次的测试结果根路径如下:
reports/xxxx-xx-xx-xx-xx-xx
测试用例格式化结果
result/
测试用例日志
log/plan_log_xxxx-xx-xx-xx-xx-xx.log
测试报告汇总
summary_report.html
测试报告详情
details_report.html
最新测试报告
reports/latest