目标是通过对Linux内核做模糊测试,找到尽可能多的位于arch/riscv下的bug。实现路径有两条:
- 静态:使尽可能多的arch/riscv下的代码被编译进内核;
- 动态:使模糊测试朝着触发更多arch/riscv下代码的方向进行。
在静态方面:
- 使尽可能多的arch/riscv下的代码被编译进内核即开启尽可能多的配置项;
- 所以需要对arch/riscv下的代码做分析,即哪些行被哪些配置项管理,且该配置项设置成n/y时,该行才会被编译进内核,此步骤借助undertaker实现;
- 对于给定的.config清单,分析该清单能够让多少被配置项管理的代码行被编译进内核以及因为没满足哪个配置项要求导致没有被编进内核。
- 以上过程见文档,见代码。
在动态方面:
- 现有模糊测试器是全内核空间的覆盖率引导的测试,对于arch/riscv下的测试无益;
- 对于每个执行过的种子中的系统调用进行分析,如果该系统调用执行到了arch/riscv下的代码,那么提高这个系统调用在后续被选择进行种子生成/变异的概率。
补充:在模糊测试时,可以直接使用上次测试得到的种子库。当内核版本迭代后,需要对新的内核做测试,可以复用上次的种子库,从而节约测试资源。对种子库可以进行精简,比如只保留那些触发了arch/riscv路径下代码的种子。
目标是生成一系列配置项清单,这些配置项清单能cover住arch/riscv下的所有代码
这是一个开源项目:https://github.com/6eanut/original-undertaker-tailor
make localpuma
# 这里可能需要配一下puma的环境变量,从makefile里面找思路
make
PREFIX=/pathto/undertaker make install
下面介绍一下它的部分功能。
对file进行分析,看该文件中的哪些行会被配置项管辖
undertaker -j blockrange file
例如,对于arch/riscv/mm/pmem.c而言,分析得到的结果是:
$ undertaker -j blockrange arch/riscv/mm/pmem.c
arch/riscv/mm/pmem.c:B00:0:0
arch/riscv/mm/pmem.c:B0:14:19
arch/riscv/mm/pmem.c:B1:26:31
即第14-19行和第26-31行会被配置项管理。
生成Bi和宏定义的映射
undertaker -j cpppc_decision file
blockrange生成的是config Bi控制着哪些代码行,config Bi是谁未知,cpppc_decision可以生成Bi到宏定义的映射。
注意:这里不仅包含CONFIG_XXX,还有一些与配置项无关的宏定义。
会对file进行分析,生成多个config清单,这些config清单组合在一起能让这个文件的coverage最大化
undertaker -j coverage file
例如,对于arch/riscv/mm/cacheflush.c而言,分析得到的结果是产生两个文件在arch/riscv/mm下:
# arch/riscv/mm/cacheflush.c.config1
CONFIG_MMU=y
CONFIG_SMP=y
# arch/riscv/mm/cacheflush.c.config2
CONFIG_MMU=y
CONFIG_SMP=n
对于coverage模式,还有一些其他的额外选项,比如-C min用来指定生成尽可能少的配置组合,但速度会变慢;-C min_decision/simple_decision会依赖判定覆盖而非语句覆盖。
用于分析某个文件的某一行(甚至某一列)被哪个配置项管理。
$ undertaker -j arch/riscv/mm/cacheflush.c:180:1
B2
&& ( B2 <-> (CONFIG_SMP) )
&& B00
用于分析想要cover到某个文件的某一行,需要开启/关闭什么配置项。
$ undertaker -j blockconf arch/riscv/mm/cacheflush.c:180
CONFIG_SMP=y
先在linux目录下执行undertaker-kconfigdump来生成model文件,然后运行undertaker-linux-tree来检测是否有dead代码。(目前在生成model时不支持riscv)
undertaker的coverage功能能够生成若干config清单,这些清单的总和能够cover这个文件的所有代码,我这里写了一个脚本来完成这个事情,见这里。
它的执行流程是这样的:
结果大致是这样的。
然后借助golem工具的-e选项,输入由前面undertaker的coverage选项生成的config,进而生成整个linux的config清单。
export ARCH=riscv
golem -e include/asm/uaccess.h.config5
理论上可以直接对这327个config进行扩展,得到linux的.config;但是这太多了,而且得到的.config们可能有重复的,所以需要做处理。
一个比较容易想到的strawman approach是:现在已知哪个配置组合可以cover哪个文件,那么如果配置组合A包含配置组合B,就可以把配置组合B所cover的文件放到配置组合A所cover的文件下面并把配置组合B删掉,这样能减少配置组合数。
实现脚本在这里。这一步可以从327个配置组合缩减到155个配置组合。接下来的缩减思路分为两种情况:
太复杂了….换个思路,先把管理arch/riscv的配置项中,取值单一的(y/n)给处理了,看看能不能打开/关闭,然后再去看取值y和n都有的,这一步的分析用脚本。
最终得到的一个结果是这样的,见这,想要覆盖所有code,起码要五十个config清单。接下来可以做的事情是这样的:
后续分析见这里。
对每个系统调用所触发的addr做分析,看其所触发的代码是否位于arch/riscv下,如果是,那么就提高这个系统调用被变异/生成策略选中的概率。
可能需要比对一下效果?