二 Syzkaller学习笔记---更新syz-manager(16)

<-mgr.hubReproQueue://当从mgr.hubReproQueue通道中读取到一个新的崩溃时,表示外部系统(例如fuzzing hub)发送了一个可重现的崩溃,该函数将把它添加到重现队列中 。log.Logf(1, "loop: get repro from hub")pendingRepro[crash] = truecase reply := <-mgr.needMoreRepros://当从mgr.needMoreRepros通道中读取到一个请求时,表示需要检查是否还有待处理的崩溃 。如果所有崩溃都已处理完毕,则回复true;否则回复false 。reply <- phase >= phaseTriagedHub &&len(reproQueue)+len(pendingRepro)+len(reproducing) == 0goto waitcase reply := <-mgr.reproRequest://当从mgr.reproRequest通道中读取到一个请求时,表示需要返回正在进行的重现列表 。该函数将获取所有正在进行的重现,并将它们的标题保存在repros映射中返回 。repros := make(map[string]bool)for title := range reproducing {repros[title] = true}reply <- reprosgoto wait}}}
该函数是一个无限循环,是整个系统的核心 。它使用了多种并发和同步机制,并通过监听各种事件和状态变化来调度虚拟机运行和崩溃重现 。以下是对该函数的详细介绍:
首先,该函数使用一个资源池()来管理可用实例的索引号码 。资源池存储在结构体中,并在函数开头初始化 。资源池可以检测已释放的实例并通知正在等待实例的 。接着,该函数根据配置设置每个崩溃需要重现的虚拟机数量,以及可以使用的最大虚拟机数 。因此,在代码中,有两个变量和,其值都与可用的虚拟机数有关 。如果实际可用的虚拟机数小于要求,则会相应地更改的值 。为了管理虚拟机的运行和崩溃的重现,该函数创建了三个通道:、和 。这些通道分别用于处理运行结果、重现结果和从集线器接收重现请求 。当处理结果时,该函数会根据需要保存崩溃并加入重现队列等待重现 。在函数的主循环中,它会不断检查当前状态,并根据需要调整虚拟机的运行和崩溃的重现 。该函数使用多个映射,包括、和,来跟踪正在处理的重现 。如果发现新的重现请求,则将其添加到中 。如果当前还有空闲实例,则会启动实例以进行测试,并异步地等待运行结果 。如果存在可以使用的虚拟机并且重现队列不为空,则会开始重现流程 。在启动重现之前,该函数会检查当前是否已经有足够数量的虚拟机正在工作(决定),以免占用太多资源 。为了更好地控制整个系统的状态,该函数使用许多事件通道来等待相应的事件发生,例如资源池中某个实例被释放、需要停止正在执行的测试或重现以及从集线器接收重现请求等 。在监听这些事件时,它使用goto语句来重新进入等待状态,直到特定的事件发生并满足某些条件时再继续执行 。crash 复现
调用链:() -> mgr.() -> repro.Run() -> ctx.repro() (重点函数)
位置:pkg/repro/repro.go: (*).repro()
功能:crash 复现,提取出触发crash的C代码 。
测试崩溃,调用repro.run
func (mgr *Manager) runRepro(crash *Crash, vmIndexes []int, putInstances func(...int)) *ReproResult {features := mgr.checkResult.Featuresres, stats, err := repro.Run(crash.Output, mgr.cfg, features, mgr.reporter, mgr.vmPool, vmIndexes)...
pkg/repro/repro.go Run
func Run(crashLog []byte, cfg *mgrconfig.Config, features *host.Features, reporter *report.Reporter,vmPool *vm.Pool, vmIndexes []int) (*Result, *Stats, error) {....res, err := ctx.repro(entries, crashStart)if err != nil {return nil, nil, err}if res != nil {ctx.reproLogf(3, "repro crashed as (corrupted=%v):\n%s",ctx.report.Corrupted, ctx.report.Report)// Try to rerun the repro if the report is corrupted.for attempts := 0; ctx.report.Corrupted && attempts < 3; attempts++ {ctx.reproLogf(3, "report is corrupted, running repro again")if res.CRepro {_, err = ctx.testCProg(res.Prog, res.Duration, res.Opts)} else {_, err = ctx.testProg(res.Prog, res.Duration, res.Opts)}if err != nil {return nil, nil, err}}ctx.reproLogf(3, "final repro crashed as (corrupted=%v):\n%s",ctx.report.Corrupted, ctx.report.Report)res.Report = ctx.report}return res, ctx.stats, nil}这部分代码是程序中的一个函数 Run,它会运行 fuzzing 过程并尝试重现崩溃 。具体来说:首先,该函数会调用上下文对象的 repro 方法来尝试重现崩溃,并将得到的结果保存到 res 变量中 。如果重现成功,则 res 将包含相关的信息,例如重现用时、产生崩溃的程序等 。如果重现失败,或者重现后报告被标记为 "corrupted",则该函数将尝试重新运行重现多达三次,以确保报告数据正确 。最终,该函数将返回重现结果 res、统计数据 ctx.stats 和可能出现的错误 。ctx.repro这个函数