Syzkaller 源码分析(1):syz-manager
字数 1660 2025-08-25 22:58:56

Syzkaller 源码分析:syz-manager 组件详解

1. Syzkaller 概述

Syzkaller 是由 Google 开发的一款强大的内核模糊测试工具,采用双机调试架构:

  • Host 主机:运行 syz-manager,控制整个 fuzzing 流程
  • Guest 虚拟机:运行被测试的内核和 fuzzer 组件

核心组件

  1. syz-manager:控制中枢,管理 VM 实例并通过 RPC 控制 syz-fuzzer
  2. syz-fuzzer:负责生成输入、执行测试并收集覆盖率信息
  3. 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() 初始化

  1. 初始化 VM Pool

    vmPool, err := vm.Create(cfg, *flagDebug)
    
  2. 创建 crash 目录和 reporter

    crashdir := filepath.Join(cfg.Workdir, "crashes")
    reporter, err := report.NewReporter(cfg)
    
  3. 初始化 Manager 实例

    mgr := &Manager{
        cfg:      cfg,
        vmPool:   vmPool,
        reporter: reporter,
        // ...其他字段初始化
    }
    
  4. 执行初始化步骤

    • preloadCorpus():加载语料库
    • initStats():初始化监控统计
    • initHTTP():创建 HTTP 服务器
    • collectUsedFiles():检查所需文件
  5. 创建 RPC 服务器

    mgr.serv, err = startRPCServer(mgr)
    
  6. 启动协程

    • 日志输出协程:每 10 秒记录一次状态
    • bench 协程(可选):每分钟最小化语料库并记录基准数据
    • dashboard 协程(可选):每分钟上报状态
  7. 进入 vmLoop()

    mgr.vmLoop()
    

4. vmLoop() 核心逻辑

4.1 初始化阶段

  1. VM 分组

    • Fuzzing VMs:执行模糊测试
    • Repro VMs:复现 crash
  2. 创建资源池

    instances := SequentialResourcePool(vmCount, 10*time.Second*mgr.cfg.Timeouts.Scale)
    
  3. 初始化通道

    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() 关键步骤:

  1. 设置不同 crash 类型的超时时间
  2. 创建 repro 上下文
  3. 启动 VM 实例
  4. 调用 repro.Run() 执行复现

3. 启动 fuzzing 协程

idx := instances.TakeOne()
go func() {
    crash, err := mgr.runInstance(*idx)
    runDone <- &RunResult{*idx, crash, err}
}()

runInstance() 关键步骤:

  1. 创建 VM 实例
  2. 设置端口转发
  3. 拷贝 syz-fuzzer 和 syz-executor
  4. 执行 fuzzer 命令
  5. 监控执行结果

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() 主要步骤:

  1. 解析 crash log

  2. 根据 crash 类型设置不同超时:

    • 普通 crash:3×程序超时
    • 内存泄漏:20×程序超时
    • 挂起:最大超时
  3. 创建 repro 上下文:

    ctx := &context{
        target:       cfg.SysTarget,
        reporter:     reporter,
        crashTitle:   crashTitle,
        crashType:    crashType,
        instances:    make(chan *reproInstance, len(vmIndexes)),
        testTimeouts: testTimeouts,
    }
    
  4. 启动 VM 实例:

    inst, err := instance.CreateExecProgInstance(vmPool, vmIndex, cfg, reporter, opt)
    
  5. 执行复现:

    res, err := ctx.repro(entries, crashStart)
    
    • extractProg():提取触发 crash 的程序
    • minimizeProg():最小化程序集
    • extractC():尝试生成 C repro

5.2 fuzzing 执行流程

runInstanceInner() 关键步骤:

  1. 创建 VM 实例:

    inst, err := mgr.vmPool.Create(index)
    
  2. 设置端口转发:

    fwdAddr, err := inst.Forward(mgr.serv.port)
    
  3. 拷贝必要文件:

    fuzzerBin, err := inst.Copy(mgr.cfg.FuzzerBin)
    executorBin, err := inst.Copy(mgr.cfg.ExecutorBin)
    
  4. 构建并执行 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)
    
  5. 监控执行结果:

    rep := inst.MonitorExecution(outc, errc, mgr.reporter, vm.ExitTimeout)
    

6. 总结

syz-manager 作为 Syzkaller 的控制中枢,其主要职责包括:

  1. 管理 VM 实例的生命周期
  2. 协调 fuzzing 和 crash 复现流程
  3. 收集和分析测试结果
  4. 维护语料库和覆盖率信息

通过精细的资源调度和状态管理,syz-manager 实现了高效的并行模糊测试,能够快速发现和复现内核中的各类漏洞。

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 实例,实现以下关键接口: Pool 结构体 管理 VM 池,主要接口: ResourcePool 结构体 管理空闲 VM 的资源池队列,关键方法: 2.2 全局管控结构体 Manager 结构体 syz-manager 的核心结构,关键字段: Fuzzing 阶段定义 2.3 Fuzzing 结果结构体 Crash 结构体 记录 crash 信息: Report 结构体 单次执行结果报告: 3. syz-manager 启动流程 3.1 main() 函数 3.2 RunManager() 初始化 初始化 VM Pool : 创建 crash 目录和 reporter : 初始化 Manager 实例 : 执行初始化步骤 : preloadCorpus() :加载语料库 initStats() :初始化监控统计 initHTTP() :创建 HTTP 服务器 collectUsedFiles() :检查所需文件 创建 RPC 服务器 : 启动协程 : 日志输出协程:每 10 秒记录一次状态 bench 协程(可选):每分钟最小化语料库并记录基准数据 dashboard 协程(可选):每分钟上报状态 进入 vmLoop() : 4. vmLoop() 核心逻辑 4.1 初始化阶段 VM 分组 : Fuzzing VMs:执行模糊测试 Repro VMs:复现 crash 创建资源池 : 初始化通道 : 4.2 主循环逻辑 1. 处理待复现 crash 2. 启动 crash 复现协程 runRepro() 关键步骤: 设置不同 crash 类型的超时时间 创建 repro 上下文 启动 VM 实例 调用 repro.Run() 执行复现 3. 启动 fuzzing 协程 runInstance() 关键步骤: 创建 VM 实例 设置端口转发 拷贝 syz-fuzzer 和 syz-executor 执行 fuzzer 命令 监控执行结果 4. 处理通道事件 5. 关键实现细节 5.1 crash 复现流程 repro.Run() 主要步骤: 解析 crash log 根据 crash 类型设置不同超时: 普通 crash:3×程序超时 内存泄漏:20×程序超时 挂起:最大超时 创建 repro 上下文: 启动 VM 实例: 执行复现: extractProg() :提取触发 crash 的程序 minimizeProg() :最小化程序集 extractC() :尝试生成 C repro 5.2 fuzzing 执行流程 runInstanceInner() 关键步骤: 创建 VM 实例: 设置端口转发: 拷贝必要文件: 构建并执行 fuzzer 命令: 监控执行结果: 6. 总结 syz-manager 作为 Syzkaller 的控制中枢,其主要职责包括: 管理 VM 实例的生命周期 协调 fuzzing 和 crash 复现流程 收集和分析测试结果 维护语料库和覆盖率信息 通过精细的资源调度和状态管理,syz-manager 实现了高效的并行模糊测试,能够快速发现和复现内核中的各类漏洞。