Go语言精进之路:从新手到高手的编程思想、方法和技巧(2)
上QQ阅读APP看书,第一时间看更新

45.2 go-fuzz的初步工作原理

go-fuzz实际上是基于前面提到的老牌模糊测试项目afl-fuzz的逻辑设计和实现的。不同的是在使用的时候,afl-fuzz对于每个输入用例(input case)都会创建(fork)一个进程(process)去执行,而go-fuzz则是将输入用例中的数据传给下面这样一个Fuzz函数,这样就无须反复重启程序。

func Fuzz(data []byte) int

go-fuzz进一步完善了Go开发测试工具集,很多较早接受Go语言的公司(如Cloudflare等)已经开始使用go-fuzz来测试自己的产品以提高产品质量了。

go-fuzz的工作流程如下:

1)生成随机数据;

2)将上述数据作为输入传递给被测程序;

3)观察是否有崩溃记录(crash),如果发现崩溃记录,则说明找到了潜在的bug。

之后开发者可以根据crash记录情况去确认和修复bug。修复bug后,我们一般会为被测代码添加针对这个bug的单元测试用例以验证bug已经修复。

go-fuzz采用的是代码覆盖率引导的fuzzing算法(Coverage-guided fuzzing)。go-fuzz运行起来后将进入一个死循环,该循环中的逻辑的伪代码大致如下:

// go-fuzz-build在构建用于go-fuzz的二进制文件(*.zip)的过程中
// 在被测对象代码中埋入用于统计代码覆盖率的桩代码及其他信息
Instrument program for code coverage

Collect initial corpus of inputs  // 收集初始输入数据语料(位于工作路径下的corpus目录下)
for {
    // 从corpus中读取语料并做随机变化
    Randomly mutate an input from the corpus

    // 执行Fuzz,收集代码覆盖率数据
    Execute and collect coverage

    // 如果输入数据提供了新的代码覆盖率,则将该输入数据存入语料库(corpus)
    If the input gives new coverage, add it to corpus
}

go-fuzz的核心是对语料库的输入数据如何进行变化。go-fuzz内部使用两种对语料库的输入数据进行变化的方法:突变(mutation)和改写(versify)。突变是一种低级方法,主要是对语料库的字节进行小修改。下面是一些常见的突变策略:

  • 插入/删除/重复/复制随机范围的随机字节;
  • 位翻转;
  • 交换2字节;
  • 将一个字节设置为随机值;
  • 从一个byte/uint16/uint32/uint64中添加/减去;
  • 将一个byte/uint16/uint32替换为另一个值;
  • 将一个ASCII数字替换为另一个数字;
  • 拼接另一个输入;
  • 插入其他输入的一部分;
  • 插入字符串/整数字面值;
  • 替换为字符串/整数字面值。

例如,下面是对输入语料采用突变方法的输入数据演进序列:

""
"", "A"
"", "A", "AB"
"", "A", "AB", "ABC"
"", "A", "AB", "ABC", "ABCD"

改写是比较先进的高级方法,它会学习文本的结构,对输入进行简单分析,识别出输入语料数据中各个部分的类型,比如数字、字母数字、列表、引用等,然后针对不同部分运用突变策略。 下面是应用改写方法进行语料处理的例子:

原始语料输入:

`<item name="foo"><prop name="price">100</prop></item>`

运用改写方法后的输入数据例子:

<item name="rb54ana"><item name="foo"><prop name="price"></prop><prop/></item>
    </item>
<item name=""><prop name="price">=</prop><prop/> </item>
<item name=""><prop F="">-026023767521520230564132665e0333302100</prop><prop/>
    </item>
<item SN="foo_P"><prop name="_G_nx">510</prop><prop name="vC">-9e-07036514
    </prop></item>
<item name="foo"><prop name="c8">prop name="p"</prop>/}<prop name=" price">01e-6
    </prop></item>
<item name="foo"><item name="foo"><prop JY="">100</prop></item>8<prop/></item>