编写测试文件

安装完成后,您可以使用这个简单的例程来启动模糊测试:

将下面的内容存储为 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原生支持的数据类型和如何扩展类型支持。