本文小结fuzz测试遇到的高频问题及其解答。
场景问题
Q:fuzz主调函数OneInput/Initialize区别?
A:前者fuzz而框架的main函数中无限次循环,后者仅开始运行时调用初始化一次。
Q:如何调用手机当前fuzz目录下的动态库?
A:由于非系统路径动态库,每次运行的时候都需要提前手动配置下。解决方法如下
- 法1:运行时链接当前xx目录下的动态库,指令:
export LD_LIBRARY_PATH=$LD_LIBRARY_PATH:/data/local/tmp/xx
- 法2:运行时链接指定xx.so动态库,指令:
export LD_PRELOAD=/usr/lib/gcc/aarch64-linux-gnu/7.5.0/xx.so
- 法3:运行可执行文件时指定链接动态库目录,指令:
LD_LIBRARY_PATH=./lib/ ./demo_exe -max_len=65535 -len_control=0 -detect_leaks=0 ./corpus
Q:如何回归测试fuzz崩溃的某条码流?
A:末尾指定对应特定码流文件路径,指令:LD_LIBRARY_PATH=./lib/ ./demo_exe -max_len=65535 -len_control=0 -detect_leaks=0 ./crash-ce3xfaf6a5217xxxbf5f95fafa103
Q:如何保存测试崩溃的码流,并指定其文件名前缀?
A:prefix
表示后面加的指定码流前缀文件名,./corpus
表示保存到当前corpus目录下。指令:LD_LIBRARY_PATH=./lib/ ./demo_exe -max_len=65535 -len_control=0 -artifact_prefix=./corpus_crash -detect_leaks=1 ./corpus
Q:运行指令不同参数分别代表什么意思?
A:以当前指令为例,指令:LD_LIBRARY_PATH=./lib/ ./demo_exe -max_len=65535 -len_control=0 -artifact_prefix=./corpus_crash -detect_leaks=1 ./corpus
LD_LIBRARY_PATH,
表示当前非系统动态库目录链接路径max_len,
设定输入的bytes乱流最大字节数len_control,
表示len的控制方式,默认为100,从小到大增长;为0,表示直接按最大len(max_len)来测试-detect_leaks=0,
不开启内存泄漏检测,加快fuzz速度-rss_limit_mb=4096,
给予4096M的内存使用空间-corpus,
存放当前的测试用例输入存放位置,本例中表示码流,对应oneinput中的data输入
Q:运行fuzz过程中的提示含义如何解读?
A:比如:\#52724 cov: 5041 ft: 24878 corp: 501/16Mb lim: 65535 exec/s: 4 rss: 97Mb L: 64554/65535 MS: 4 ChangeByte-CopyPart-CrossOver-ChangeBinInt
,参数解读如下:
- #52724:表示已经测试过52724个数据
- cov:当前已经覆盖的代码块或边数
- ft:尝试覆盖率的信号个数
- corp:当前随机序列的入口个数,及其所占用的内存大小
- lim:当前最大maxlen为65535字节,对应的oneinput函数中的size
- exec/s:迭代速度,每秒xx次
- rss:当前内存消耗
- L:当前输入的实际所占字节
报错解决
编译阶段
报错:ld: error: undefined symbol: std::__ndk1::basic_string<char, std::__ndk1::char_traits<char>, std::__ndk1::allocator<char> >::resize(unsigned long, char)
error:linker command failed with exit code 1
ld: error: undefined symbol: std::__ndk1::basic_streambuf<char, std::__ndk1::char_traits >::~basic_streambuf()
>>> FuzzerDataFlowTrace.cpp.o:(fuzzer::Command::toString() const) in archive D:/ProgramData/android-ndk-r23c/build//…/toolchains/llvm/prebuilt/windows-x86_64\lib64\clang\12.0.9\lib\linux\libclang_rt.fuzzer-aarch64-android.a
>>> referenced 18 more times
分析解决
- 根因:未在application.mk中链接C++相关的基础库
- 解决:在application.mk中,添加代码行:APP_STL := c++_shared
编译报错:无main函数等
ld: error: undefined symbol: LLVMFuzzerTestOneInput
>>> referenced by FuzzerMain.cpp:20 (out/llvm-project/compiler-rt/lib/fuzzer\FuzzerMain.cpp:20)
>>> FuzzerMain.cpp.o:(main) in archive D:/ProgramData/android-ndk-r23c/build//…/toolchains/llvm/prebuilt/windows-x86_64\lib64\clang\12.0.9\lib\linux\libclang_rt.fuzzer-aarch64-android.a
>>> did you mean to declare LLVMFuzzerTestOneInput(unsigned char const*, unsigned long) as extern “C”?【关键】【】【】
分析解决:
- andriod.mk中添加:cflags/ldflags要同时使用(以便于自动调用llvm的fuzzer main框架),这里可以解决无main问题,调用fuzzer框架里的main
LOCAL_CFLAGS += -fsanitize=fuzzer -fno-omit-frame-pointer
LOCAL_LDFLAGS += -fsanitize=fuzzer
- cc文件为CPP代码编译,其中C代码函数要用宏定义隔开,以免函数类型定义用cpp的方式导致无法
...cpp code
#ifdef __cplusplus
extern "C" {
#endif
...c code
#ifdef __cplusplus
}
#endif
运行阶段
报错:无程序报错行的具体偏移地址,无法反解析。ERROR: AddressSanitizer: heap-buffer-overflow on address
=25006==ERROR: AddressSanitizer: heap-buffer-overflow on address 0x0050af50e175 at pc 0x006fd1344550 bp 0x007ffb7aeb10 sp 0x007ffb7ae2a0
READ of size 8300 at 0x0050af50e175 thread T0
AddressSanitizer:DEADLYSIGNAL
AddressSanitizer: nested bug in the same thread, aborting.
分析解决:
- 原因:手机运行缺乏clang生成的asan动态库,无法打桩得到具体出问题的函数信息
- 解决:
- 找到编译附带生成的库:libclang_rt.asan-aarch64-android.so,与libc++_shared.so和可执行文件同目录
- 同时注意编译选项:-g, o0
报错:CANNOT LINK EXECUTABLE “./demo_exe”: cannot locate symbol “__emutls_get_address”
CANNOT LINK EXECUTABLE "./demo_exe: cannot locate symbol “__emutls_get_address” referenced by “/data/local/tmp/”…
分析解决:
- 根因:无C++基础动态库:
libc++_shared.so
- 解决:
- application.mk中添加:
APP_STL := c++_shared
- 推入生成的库文件
libc++_shared.so
到手机 - 当前文件夹作为链接库,指令:
export LD_LIBRARY_PATH=$LD_LIBRARY_PATH:/data/local/tmp/xx
- application.mk中添加:
进阶资料
- The Art of Fuzzing - Demo 3: LibFuzzer Demonstration
- llvm-symbolizer指令解析官方详解,link