编写测试文件
安装完成后,您可以使用这个简单的例程来启动模糊测试:
将下面的内容存储为 first-fuzz.cpp
:
#include <stdio.h>
#include <string.h>
#include <string>
#include <wfuzz.h>
void echo(std::string content) {
char buf[10];
strncpy(buf, content.c_str(), 10);
printf("%s\n", buf);
}
WFUZZ_TEST_ENTRYPOINT(echo);
这段程序的错误在哪?
strncpy
函数虽然在写入的时候,不会超过目标的长度, 但它并不保证目标会以\0
结束,当源字符串长度大于目标长度时, 它会拷贝源字符串的前n个字符,并不会写入\0
。后续的
printf
函数的%s
接受一个 c-style-string, 它假设字符串以\0
结束,如果发生了上面的情况, 则打印字符串的长度会超出buf的范围,直到在后续的内存中找到\0
为止。 由于buf
在栈上,这里是一个典型的栈缓冲区溢出(stack-buffer-overflow)错误。
WFUZZ_TEST_ENTRYPOINT(echo)
是什么?这是 WINGFUZZ 的测试入口,由引擎生成的测试用例数据将从
echo
函数传入。关于测试入口、测试驱动的详细介绍,可参看【编写测试驱动】章节。
我用过开源的模糊测试工具 LibFuzzer / AFL,例程好像不太一样?
首先,WINGFUZZ 兼容 LibFuzzer 的测试入口,如果您手头有现成程序,可以直接使用。
那我们为什么要做这样的优化呢?
开源的模糊测试工具一般都是接受一个原始缓冲区,例如 LibFuzzer 的测试入口写法就只能是
LLVMFuzzerTestOneInput(char *buffer, size_t length)
, 但实际程序中函数调用的参数一般是结构化的数据。 对于开源的模糊测试工具来说,我们需要手写转换器,将入口传入的数据对接到实际待测函数可接受的类型上。 这个过程既麻烦,又容易出错,可能导致测出的问题只是转换过程中的问题,而非实际代码的问题。WINGFUZZ的测试入口支持结构化的输入,可以直接使用目标数据结构, 由WINGFUZZ测试框架自动生成结构化的数据,避免了上面的问题。 同时,数据结构的信息还会反馈给生成器,能够更有效的生成测试数据,增加测试覆盖率。
阅读章节【进阶使用-数据类型支持】可以进一步了解WINGFUZZ原生支持的数据类型和如何扩展类型支持。