Syzkaller 源码分析(1):syz-manager
字数 1660 2025-08-25 22:58:56
Syzkaller 源码分析:syz-manager 组件详解
1. Syzkaller 概述
Syzkaller 是由 Google 开发的一款强大的内核模糊测试工具,采用双机调试架构:
- Host 主机:运行 syz-manager,控制整个 fuzzing 流程
- Guest 虚拟机:运行被测试的内核和 fuzzer 组件
核心组件
- syz-manager:控制中枢,管理 VM 实例并通过 RPC 控制 syz-fuzzer
- syz-fuzzer:负责生成输入、执行测试并收集覆盖率信息
- syz-executor:执行单个输入并返回结果
2. syz-manager 核心结构体
2.1 VM 管控相关结构体
Instance 结构体
表示单个 VM 实例,实现以下关键接口:
type Instance interface {
Copy(hostSrc string) (string, error) // 复制文件到 VM
Forward(port int) (string, error) // 设置端口转发
Run(timeout time.Duration, stop <-chan bool, command string) // 在 VM 中执行命令
Diagnose(rep *report.Report) ([]byte, bool) // 检索调试信息
Close() // 销毁 VM
}
Pool 结构体
管理 VM 池,主要接口:
type Pool interface {
Count() int // 返回 VM 数量
Create(workdir string, index int) (Instance, error) // 创建新实例
}
ResourcePool 结构体
管理空闲 VM 的资源池队列,关键方法:
func (pool *ResourcePool) Put(ids ...int) // 添加空闲 VM
func (pool *ResourcePool) Take(cnt int) []int // 取出多个 VM
func (pool *ResourcePool) TakeOne() *int // 取出单个 VM
2.2 全局管控结构体
Manager 结构体
syz-manager 的核心结构,关键字段:
type Manager struct {
cfg *mgrconfig.Config // 配置信息
vmPool *vm.Pool // VM 池
target *prog.Target // 目标系统
reporter *report.Reporter // 报告生成器
serv *RPCServer // RPC 服务器
corpusDB *db.DB // 语料库数据库
targetEnabledSyscalls map[*prog.Syscall]bool // 允许的系统调用
corpus map[string]CorpusItem // 语料库
seeds [][]byte // 变异种子
}
Fuzzing 阶段定义
const (
phaseInit = iota // 初始阶段
phaseLoadedCorpus // 已加载语料库
phaseTriagedCorpus // 已分类语料库
phaseQueriedHub // 已查询 hub
phaseTriagedHub // 已分类 hub 输入
)
2.3 Fuzzing 结果结构体
Crash 结构体
记录 crash 信息:
type Crash struct {
vmIndex int // VM 索引
hub bool // 是否来自 hub
*report.Report // crash 报告
machineInfo []byte // 机器信息
}
Report 结构体
单次执行结果报告:
type Report struct {
Title string // Oops 的第一行文本
Type Type // Bug 类型 (hang, memory leak 等)
Frame string // 关键函数名
Report []byte // 完整 oops 文本
Output []byte // 原始控制台输出
StartPos int // oops 消息起始位置
EndPos int // oops 消息结束位置
}
3. syz-manager 启动流程
3.1 main() 函数
func main() {
cfg, err := mgrconfig.LoadFile(*flagConfig) // 加载配置文件
RunManager(cfg) // 启动管理器
}
3.2 RunManager() 初始化
-
初始化 VM Pool:
vmPool, err := vm.Create(cfg, *flagDebug) -
创建 crash 目录和 reporter:
crashdir := filepath.Join(cfg.Workdir, "crashes") reporter, err := report.NewReporter(cfg) -
初始化 Manager 实例:
mgr := &Manager{ cfg: cfg, vmPool: vmPool, reporter: reporter, // ...其他字段初始化 } -
执行初始化步骤:
preloadCorpus():加载语料库initStats():初始化监控统计initHTTP():创建 HTTP 服务器collectUsedFiles():检查所需文件
-
创建 RPC 服务器:
mgr.serv, err = startRPCServer(mgr) -
启动协程:
- 日志输出协程:每 10 秒记录一次状态
- bench 协程(可选):每分钟最小化语料库并记录基准数据
- dashboard 协程(可选):每分钟上报状态
-
进入 vmLoop():
mgr.vmLoop()
4. vmLoop() 核心逻辑
4.1 初始化阶段
-
VM 分组:
- Fuzzing VMs:执行模糊测试
- Repro VMs:复现 crash
-
创建资源池:
instances := SequentialResourcePool(vmCount, 10*time.Second*mgr.cfg.Timeouts.Scale) -
初始化通道:
runDone := make(chan *RunResult, 1) // fuzzing 结果 reproDone := make(chan *ReproResult, 1) // repro 结果
4.2 主循环逻辑
1. 处理待复现 crash
for crash := range pendingRepro {
if !reproducing[crash.Title] && mgr.needRepro(crash) {
reproducing[crash.Title] = true
reproQueue = append(reproQueue, crash)
}
}
2. 启动 crash 复现协程
if canRepro() {
vmIndexes := instances.Take(instancesPerRepro)
atomic.AddUint32(&mgr.numReproducing, 1)
go func() {
reproDone <- mgr.runRepro(crash, vmIndexes, instances.Put)
}()
}
runRepro() 关键步骤:
- 设置不同 crash 类型的超时时间
- 创建 repro 上下文
- 启动 VM 实例
- 调用
repro.Run()执行复现
3. 启动 fuzzing 协程
idx := instances.TakeOne()
go func() {
crash, err := mgr.runInstance(*idx)
runDone <- &RunResult{*idx, crash, err}
}()
runInstance() 关键步骤:
- 创建 VM 实例
- 设置端口转发
- 拷贝 syz-fuzzer 和 syz-executor
- 执行 fuzzer 命令
- 监控执行结果
4. 处理通道事件
select {
case <-instances.Freed: // VM 释放通知
case stopRequest <- true: // 停止请求
case res := <-runDone: // fuzzing 结果
instances.Put(res.idx)
if res.crash != nil {
pendingRepro[res.crash] = true
}
case res := <-reproDone: // repro 结果
atomic.AddUint32(&mgr.numReproducing, ^uint32(0))
delete(reproducing, res.report0.Title)
case <-shutdown: // 终止信号
shutdown = nil
case crash := <-mgr.hubReproQueue: // 来自 hub 的 crash
pendingRepro[crash] = true
}
5. 关键实现细节
5.1 crash 复现流程
repro.Run() 主要步骤:
-
解析 crash log
-
根据 crash 类型设置不同超时:
- 普通 crash:3×程序超时
- 内存泄漏:20×程序超时
- 挂起:最大超时
-
创建 repro 上下文:
ctx := &context{ target: cfg.SysTarget, reporter: reporter, crashTitle: crashTitle, crashType: crashType, instances: make(chan *reproInstance, len(vmIndexes)), testTimeouts: testTimeouts, } -
启动 VM 实例:
inst, err := instance.CreateExecProgInstance(vmPool, vmIndex, cfg, reporter, opt) -
执行复现:
res, err := ctx.repro(entries, crashStart)extractProg():提取触发 crash 的程序minimizeProg():最小化程序集extractC():尝试生成 C repro
5.2 fuzzing 执行流程
runInstanceInner() 关键步骤:
-
创建 VM 实例:
inst, err := mgr.vmPool.Create(index) -
设置端口转发:
fwdAddr, err := inst.Forward(mgr.serv.port) -
拷贝必要文件:
fuzzerBin, err := inst.Copy(mgr.cfg.FuzzerBin) executorBin, err := inst.Copy(mgr.cfg.ExecutorBin) -
构建并执行 fuzzer 命令:
args := &instance.FuzzerCmdArgs{ Fuzzer: fuzzerBin, Executor: executorBin, Name: instanceName, OS: mgr.cfg.TargetOS, Sandbox: mgr.cfg.Sandbox, Procs: mgr.cfg.Procs, } cmd := instance.FuzzerCmd(args) outc, errc, err := inst.Run(mgr.cfg.Timeouts.VMRunningTime, mgr.vmStop, cmd) -
监控执行结果:
rep := inst.MonitorExecution(outc, errc, mgr.reporter, vm.ExitTimeout)
6. 总结
syz-manager 作为 Syzkaller 的控制中枢,其主要职责包括:
- 管理 VM 实例的生命周期
- 协调 fuzzing 和 crash 复现流程
- 收集和分析测试结果
- 维护语料库和覆盖率信息
通过精细的资源调度和状态管理,syz-manager 实现了高效的并行模糊测试,能够快速发现和复现内核中的各类漏洞。