syzkaller运行流程结构如下图所示,红色标签表示需要配置的选项:syz-manager
用来启动、监控、和重启多个虚拟机实例,并在虚拟机里启动一个syz-fuzzer
进程。它负责持久化corpus和存储crash。
syz-fuzzer
在要测试的内核虚拟机上运行,syz-fuzzer指导fuzz进程(产生输入、变异、精简等)并通过RPC方式发送触发新路径的输入返回给syz-manager
进程。它也会启动一个暂态syz-executor
进程。
每个syz-executor
进程执行一个输入(一套syscalls)。如:
1 | mmap(&(0x7f000000000),(0x1000), 0x3, 0x32, -1, 0) |
从syz-fuzzer
进程接收输入来执行,并将结果返回。它被设计的尽可能简单(为了不干扰fuzz进程),用C++实现,编译为静态二进制,用共享内存通信。
源码分析
先从启动syzkaller的命令行工具syz-manager的源码开始分析,syz-manager的源码位于syz-manager/manager.go文件,首先是一个Manager结构体,里面包含了fuzz过程中的重要信息,如配置信息、虚拟机信息、测试目标信息等,具体内容后面会分析到。
接下来是syz-manager运行的main函数:
1 | func main() { |
pkg/mgrconfig里的config.go里定义了Config结构体,用来保存配置文件里的信息,load.go文件里定义了读取配置文件的方法。
prog/target.go中定义了Target结构体,GetTarget方法中用到了targets变量,targets变量在RegisterTarget方法中初始化,在RegisterTarget中添加debug.PrintStack(),发现RegisterTarget位于栈底,不知道是哪里调用了它。其实是在manager.go文件开头,import了sys包,在sys/sys.go文件中,import ( _ “**/sys/linux/gen)导入包前的下划线表示这个包里所有文件的init方法都会被执行,sys/linux/gen/里有386.go、amd64.go、arm64.go等,这些文件里都有init方法,init里调用了RegisterTarget方法,初始化了各个目标平台的target信息。GetTarget最后用sync.Once的Do方法确保target的初始化在整个程序(多线程环境)中只执行一次,内部通过互斥锁实现。
sysTarget的用处还没有细看。
接下来是RunManager方法,RunManager实现了启动虚拟机、http服务、rpc服务和log fuzz进程等操作。
1 | func RunManager(cfg *mgrconfig.Config, target *prog.Target, sysTarget *targets.Target, syscalls []int) { |
在vm/vm.go文件中,还是用import (_ “**/vm/qemu”)的方法,调用了所有导入文件里的init方法,qemu的init方法:
1 | func init() { |
调用了vm/vmimpl/vmimpl.go的Register方法:
1 | func Register(typ string, ctor ctorFunc, allowsOvercommit bool) { |
再看vm/vm.go的Create方法:
1 | func Create(cfg *mgrconfig.Config, debug bool) (*Pool, error) { |
再回到RunManager,进行mgr := &Manager创建mgr管理信息,initHTTP()创建HTTP服务器,startRPCServer(mgr)为fuzzer创建RPC服务器。接下来的go func() { for log },并发执行一个匿名函数,不停log fuzz进度。go加上方法表示并发执行这个方法。最后RunManager执行一个mgr.vmLoop()方法,vmLoop()方法会调用一个runInstance方法,runInstance调用mgr.vmPool.Create(index),这里是vm/vm.go的Create(),这个Create()会调用vmPool的impl的Create(workdir, index)方法,这里也就是qemu的Create(workdir, index)方法,qemu的Create方法会创建sshkey,并调用ctor方法,ctor方法会调用boot方法,boot方法会执行启动qemu的命令。vm.go的Create方法会返回启动虚拟机的实例,接下来runInstance方法会通过ssh拷贝fuzzerBin和executorBin到虚拟机实例,然后执行fuzzer二进制文件,并监控虚拟机的执行过程。
reference
How syzkaller works