我估计公众号里最好的避坑文都只写到了"照着官方 README 敲命令",完全没告诉你 manager 这台机器到底在 syzkaller 全家桶里扮演什么角色。如果你已经把内核代码在开发机上编好了,或者正打算把 fuzzing 这套东西跑起来,那么本篇要聊的"Step4 - 测试电脑安装 syzkaller manager",就是你从"代码能编过"跨到"crash 能复现"最关键的一道门槛。
很多人第一次接触 syzkaller 时,会被它的组件名字绕晕:syz-manager、syz-fuzzer、syz-executor、syz-reproducer…… 我一开始也以为 manager 就是个"启动按钮",后来排查问题排到头皮发麻才意识到,manager 是整套系统的调度中枢。它管着虚拟机池的起停、测试用例的分配、crash 的收集去重、甚至还会自动包一组复现参数。你在开发机上跑交叉编译,在测试电脑上部署 manager,manager 再拉起一台台 qemu 虚拟机跑 fuzzer,这一套链路缺了哪个环节都不转。
这篇文章我不打算复读一遍官方文档。我会直接按我自己搭环境的顺序,把"为什么测试电脑要单独装一个 manager""什么系统配置最容易坑人""config 文件里哪些字段必须逐字吃透""启动后如何判断它真的在干正事""日常跑挂怎么自救"这几个问题一条龙讲清楚。如果你正好卡在 Step4 这个位置,照着这篇文章走完,你的 manager 应该能稳稳把 fuzzing 任务跑起来。
1. 为什么测试电脑要单独装一个 syzkaller manager,它到底管什么
先别急着敲 go get 或者 git clone。我见过不少人在自己日常开发的笔记本上直接装 manager,然后在同一台机器上编内核、开 qemu、跑 fuzz,最后把系统搞得又卡又乱。这不是不能跑,而是你会分不清"到底是内核崩了还是宿主机的资源被榨干了"。syzkaller 官方推荐的拓扑里,manager 通常跑在一台独立的测试电脑上,这台机器不干别的,就负责调度和收集结果。
1.1 manager 的工作职责,可能比你想的多得多
syzkaller manager 从功能上拆解,承担四类核心任务:
- 管理 qemu/KVM 虚拟机生命周期。它会根据配置文件里的 vm 数量,并行拉起多台快照式虚拟机,每台 vm 里跑一个 syz-fuzzer 实例。
- 分发测试用例。syz-fuzzer 在虚拟机内部生成一批系统调用序列,manager 负责统筹这些序列的覆盖率和去重,避免重复劳动。
- 收集并过滤内核崩溃信息。当测试触发内核 panic 或者 KASAN 报告时,fuzzer 会把报告传给 manager,manager 对它去重、归类,并生成可读的 crash log。
- 自动构造复现程序。大部分情况下,manager 能在收集到 crash 之后自动调用 syz-reproducer,从那串随机序列里提炼出最小化复现代码,这对后续定位 bug 太重要了。
如果你把这四件事全塞进一台日常开发机里,光是 vm 占用的内存和 CPU 就已经够你喝一壶。所以 Step4 里的"测试电脑",不是你随手拿一台能开机的机器就行的,它需要承担高强度的虚拟化负载。
1.2 为什么是"单独一台"
从实操角度看,单独一台测试电脑有三个好处:
第一,隔离噪音。fuzzing 过程中内核崩溃信息首先落在 vm 里,但如果宿主机本身负载爆满,qemu 的调度延迟会让 syz-fuzzer 的 syscall 超时率飙升,误报"no output from test machine"。这种误报特别让人崩溃,因为你排查半天,最后发现是宿主机卡了。
第二,方便重置环境。manager 在跑的过程中会产生大量临时镜像和覆盖文件,如果你在开发机上去跑,这些脏数据会直接影响你编译内核的目录。单独一台测试电脑,你想怎么折腾就怎么折腾,不对了就整机重装。
第三,方便同时盯多个内核版本。syzkaller manager 本身支持多配置切换,你完全可以在测试电脑上放好几个 instance 目录,分别对应不同内核版本或不同 config,哪个出 crash 就去哪个目录里看。
提示:如果你的开发机内存超过 64G,CPU 核心数也够多,那确实可以在开发机上跑;但如果条件允许,还是一台独立的测试机更省心,我自己就是从"省机器"吃过大亏后才改过来的。
1.3 官方文档之外,manager 在部署链路中处于哪一环
整个 syzkaller 部署链路大致是:
- Step1:准备一台 Linux 开发机,安装 Go、gcc 等基础工具链。
- Step2:下载你想测试的内核源码,配置好内核 config,编译出内核镜像和 bzImage。
- Step3:在开发机上用 buildroot 或手动方式制作一个根文件系统镜像(通常叫 bullseye.img 或 stretch.img)。
- Step4:也就是本篇所在位置,在测试电脑上安装 manager,把内核镜像和根文件系统镜像路径写好,准备一堆 ssh 密钥。
- Step5:跑起来,观察管理器 web 界面,开始出结果。
从这里你就能看出来,manager 并不是一个孤立的"工具安装"步骤,它更像是把所有前序产物串起来的那个胶水层。所以,这台机器上不光要装 syzkaller 二进制,还要装 qemu、配置 ssh 免密、规划好目录结构。
需要模型API调用? 免费领10W Token,多模型网关一键接入 Claude、DeepSeek 等主流模型。
2. 动手前的环境准备,哪些坑最容易在 Step4 埋雷
很多人在 Step4 卡住,并不是因为 manager 本身编译不过,而是前置环境没准备好。我把我踩过和帮人排过的坑集中说一下,你对照着检查,能省至少半天时间。
2.1 操作系统与内核虚拟化支持
测试电脑的操作系统,首选还是 Ubuntu 系或者 Debian 系,理由无他,qemu 和 syzkaller 的依赖在这两个发行版上最全,遇到问题也最好搜到答案。
装完系统后第一件事,检查 KVM 是否可用:
bash复制ls -l /dev/kvm
grep -c 'vmx\|svm' /proc/cpuinfo
如果 /dev/kvm 不存在,需要重启进 BIOS 打开 Intel VT-x 或者 AMD-V。如果是在云主机上跑,大部分云厂商的虚拟机默认没有嵌套虚拟化,这点要特别注意,否则 qemu 起不来虚拟机,manager 只能干瞪眼。
注意:如果你手上只有一台云服务器,而且客服明确告诉你没法开嵌套虚拟化,那 syzkaller 的 qemu 模式基本跑不起来。不过还有一个 fallback 思路——syzkaller 也可以直接对物理机进行 fuzz(vm 类型设置为 none),但这种模式风险较高,不建议新手尝试。
2.2 Go 与必要依赖库:版本不对,编译期就开始报错
syzkaller 是 Go 写的,对 Go 版本有一定要求。官方 README 通常给的是"latest Go",但实际操作中我建议装一个稳定的 Go 1.17 以上版本,目前新版本对 1.21+ 支持更好。装 Go 之后,先确认一下:
bash复制go version
另外有几个系统依赖包,缺了会在编译或者运行期随机报错:
- gcc、g++、make:编译 syzkaller 本身及部分辅助工具需要。
- flex、bison:内核和工具链的解析器生成器,可能被间接依赖。
- libssl-dev:某些内核配置下需要。
- qemu-system-x86:这是 vm 模式的核心依赖,一定要装。
- sshpass 或者 openssh-client:manager 需要 ssh 到 vm 里执行命令。
在 Ubuntu 上,一条命令装齐:
bash复制sudo apt-get update
sudo apt-get install -y gcc g++ make flex bison libssl-dev qemu-system-x86 openssh-client sshpass
这里特别想提醒一下 gcc 版本。如果你测试的内核比较老,用了很旧的编译选项,高版本 gcc 可能会编不过去;如果你用的是最新内核,gcc 版本又太老也不行。我自己习惯在测试电脑上和开发机保持相同 gcc 版本,虽然不是必须,但能省掉不少版本不一致导致的诡异问题。
2.3 目录规划与磁盘空间:别让日志把根分区撑爆
manager 跑起来之后,会产生这些数据:
- workdir 下的 crash 目录(最占空间的往往是这里)
- workdir 下的 qemu 虚拟机镜像快照
- 日志文件
- 内核编译产物(如果你把编译也放在这台机器上)
我建议单独划一个分区或者至少单独的目录,比如 /syzkaller,给它预留 200G 以上空间。用 df -h 提前确认,别跑到一半发现磁盘写满,然后 manager 直接罢工。
bash复制sudo mkdir -p /syzkaller/workdir
sudo chown -R $USER:$USER /syzkaller
这里把 workdir 权限给当前用户,是为了让 manager 能直接读写,不用每次 sudo。
经验之谈: 目录结构上,我习惯把 syzkaller 源码放 /syzkaller/syzkaller,内核镜像放 /syzkaller/image,工作目录放 /syzkaller/workdir。这样后续无论切换多少套配置,所有脏数据都集中在 /syzkaller 下,删起来也方便。
2.4 网络与 ssh 免密:manager 连不上 vm 是最常见的失败点
qemu 虚拟机起来之后,manager 会通过 ssh 连进去执行 syz-fuzzer。如果 ssh 免密没配好,vm 起来一秒就断了。最稳妥的做法是专门为 syzkaller 生成一套独立的 ssh 密钥:
bash复制ssh-keygen -t rsa -b 4096 -f ~/.ssh/syzkaller_rsa -N ""
然后把公钥内容放到你的根文件系统镜像里。这一步通常在 buildroot 配置阶段就做进去了,但如果你拿的是现成镜像,需要手动挂载镜像把公钥塞进 authorized_keys。
一个比较隐蔽的问题:如果你的根文件系统里没有 ssh 服务端,或者 sshd 配置禁用了 root 登录,那你无论如何都连不上。检查镜像里 /etc/ssh/sshd_config 是否包含:
code复制PermitRootLogin yes
UsePAM no
一个比较隐蔽的问题:如果你的根文件系统里没有 ssh 服务端,或者 sshd 配置禁用了 root 登录,那你无论如何都连不上。检查镜像里 /etc/ssh/sshd_config 是否包含:
如果忘了配公钥,也可以临时用 sshpass 方式,但 manager 的配置文件里没有直接的密码登录方案,所以还是建议用密钥。
3. 下载编译 syzkaller 及生成 manager 二进制
前置环境就绪之后,就可以正式把 syzkaller 搞到测试电脑上了。这一步本身不复杂,但我想多聊两句版本选择。
3.1 用发布版还是 git master
syzkaller 更新非常快,几乎每周都有新改动。如果你是第一次跑,我建议直接用 master 分支的稳定版本,不要下载那种"last release"——我的体验是,master 在 fuzzing 效率和 bug 修复上都比旧 release 好很多。但如果你是为了复现某个已知问题,那最好切换到 issue 里提到的那个 commit。
在测试电脑上下载源码:
bash复制git clone https://github.com/google/syzkaller.git /syzkaller/syzkaller
cd /syzkaller/syzkaller
make
make 会编译生成 bin/ 目录下的一堆工具,其中包括我们最需要的 syz-manager。
3.2 只编译 manager 还是全量编译
如果你想省时间,可以在源码根目录执行:
bash复制make manager
这个命令只构建 syz-manager,以及它运行时所必需的辅助工具。但我个人建议还是执行一次全量 make,因为后续你要用 syz-reproducer、syz-trace2syz、syz-db 等工具时,直接 bin/ 里就有,不用再单独编。
编译完之后,检查一下:
bash复制ls -l /syzkaller/syzkaller/bin/
正常情况下你会看到 syz-manager、syz-fuzzer、syz-execprog、syz-reproducer 等一堆可执行文件。
3.3 关于交叉编译的说明
有些教程会提到,manager 要连目标架构的 fuzzer 一起编译,比如你想测试 arm64 内核,就得交叉编译 syz-fuzzer。但 Step4 场景下,我更建议先用 x86_64 跑通全流程,等逻辑理顺了再去搞交叉架构。因为 Step4 的核心目标是确保 manager 这一层没问题,交叉编译会引入太多额外变量,反而让你分不清问题出在 manager 还是架构支持上。
4. 配置 manager:config 文件里的每个字段我都怎么踩过来的
当所有依赖都就绪,syzkaller 二进制也编译好了,重头戏就是配置 manager。这一步我把配置打印出来,再逐段解释,因为它直接关系到最后是否能跑起来。
4.1 一份能跑通的最简 my.cfg
json复制{
"name": "testvm",
"workdir": "/syzkaller/workdir",
"target": "linux/amd64",
"kernel_obj": "/syzkaller/linux",
"kernel_src": "/syzkaller/linux",
"image": "/syzkaller/image/bullseye.img",
"sshkey": "/home/syz/.ssh/syzkaller_rsa",
"syzkaller": "/syzkaller/syzkaller",
"procs": 4,
"type": "qemu",
"vm": {
"count": 4,
"cpu": 2,
"mem": 2048,
"kernel": "/syzkaller/linux/arch/x86/boot/bzImage",
"qemu_args": "-enable-kvm -smp 2"
},
"http": "127.0.0.1:56741",
"cover": true,
"enable_syscalls": ["accept4", "openat", "close", "mmap", "read", "write"]
}
4.2 字段逐段拆解与易错点
先看 "name",它是当前 fuzzing 实例的名字,只要别跟其他实例重名就行,但要注意,它会出现在 web 界面和管理日志里,起个有辨识度的名字会省很多事。
"workdir" 是所有运行时数据的存放位置,务必确保已经创建好,并且磁盘空间充足。manager 启动的一瞬间会在这个目录下创建一堆子目录,如果目录不存在,它会直接报错退出。
"target" 和 "kernel_obj"、"kernel_src" 这三个字段要一起讲。target 是目标操作系统和架构,kernel_obj 是你编译内核时用的 build 目录,kernel_src 是内核源码目录。很多场景下这两个目录是同一个,但如果你用了 out-of-tree 编译,就要分开指。里面存有 vmlinux、模块等信息,manager 需要它来解析 crash 地址。
"image" 和 "sshkey" 我放在一起说。image 是根文件系统镜像的完整路径,sshkey 是私钥路径。注意,manager 对 ssh 私钥的权限很敏感,权限太松会直接拒绝连接,务必执行 chmod 600。
"procs" 表示每个虚拟机内部跑几个并行 fuzzer 进程。这个值不是越大越好,它是乘法关系——每个 fuzzer 进程都会生成大量 syscall 组合,procs 太大反而会让覆盖率和去重效率下降。我一般按虚拟机的虚拟 CPU 数来定,比如 vCPU 给 2,procs 设 2 就够。
"type" 和 "vm" 子配置是核心。type 设置为 qemu,然后 vm.count 表示同时启动几个虚拟机。这里需要注意,count 乘以 mem 就是宿主机需要预留的内存。假设你机器有 32G 内存,count 设 4,mem 每台 2048M,那就占掉 8G,再加上 host 本身的占用,问题不大。但如果 count 设太高,比如 20,轻则启动失败,重则宿主机 OOM,前功尽弃。
"http" 是 manager 内置 web 界面监听地址。我用 127.0.0.1 是因为测试电脑一般只有我自己访问;如果你需要用另一台机器远程看,就改成 0.0.0.0:56741,但记得加防火墙规则,别直接裸奔。
"cover" 表示是否开启代码覆盖率收集。如果你要用覆盖率引导(coverage-guided)的 fuzzing,这个必须打开。它对性能有一些影响,但收益远大于开销。
"enable_syscalls" 是一个很多人都忽略的字段。它的作用是指定允许 fuzzer 使用的系统调用白名单。新手可以暂时不加,让 syzkaller 用默认的系统调用集;但如果你想针对某个子系统(比如文件系统、网络栈)做聚焦测试,这个白名单就是最重要的控制手段。
提示: 别把 "kernel_obj" 和 "kernel_src" 指向同一个目录就觉得万事大吉。如果编译内核时你用了 O= 参数把输出目录单独放了他处,kernel_obj 必须指向输出目录,否则 manager 解析符号表时会找错 vmlinux,各种 crash 报告都会变成一堆不可读的地址。
4.3 vi 保存与配置文件校验
我习惯把配置放到 /syzkaller/my.cfg,保存之后先做个简单的 JSON 校验:
bash复制python3 -m json.tool /syzkaller/my.cfg
如果返回的仍然是格式化后的 JSON,说明语法没问题。这一步特别适合在启动之前排除掉因为少逗号、多括号导致的低级错误,不然 manager 启动时报"invalid config"你还要回来找半天。
4.4 常见配置报错与处理思路
我遇到过三类高频配置错误:
- 路径不存在。比如 image 路径写错,manager 启动失败,检查 log 里有没有 "failed to open image file"。解决方案就是逐项核对路径是否真实存在。
- sshkey 权限错误。通常日志会出现 "permissions too open" 之类的字眼。此时 chmod 600 即可。
- 内存参数单位写错。vm.mem 单位是 MB,如果你写成 2,那虚拟机只有 2M 内存,内核还没启动就 OOM 了。确保你写的是 2048 而不是 2。
5. 启动 manager 前后,怎么判断它到底有没有在干活
配置写好了,就该启动 manager 了。这一步看似简单,但启动之后你是"干等"还是"带着预期去观察",结果完全不同。我从启动命令讲到 web 界面,再讲到日志解读,给你一条完整的判断链路。
5.1 启动命令与日志观察
在 /syzkaller/syzkaller 目录下执行:
bash复制./bin/syz-manager -config /syzkaller/my.cfg
如果一切正常,终端会滚动日志,显示类似:
code复制loading corpus...
fetching corpus...
starting 4 vm(s)...
boot medium: qemu
vm-0: started
vm-1: started
看到 vm-x: started 字样,说明虚拟机已经被成功拉起。如果这里就报错,比如 "failed to create vm-0: qemu: exec: "qemu-system-x86_64": executable file not found in $PATH",那就是 qemu 没装或者不在 PATH 里,直接安装对应包即可。
为了让 manager 在后台长时间运行,我建议用 nohup 或者 tmux:
bash复制nohup ./bin/syz-manager -config /syzkaller/my.cfg > /syzkaller/manager.log 2>&1 &
因为 fuzzing 测试经常要跑几十个小时,关掉终端进程就断了是新手最容易犯的错误。
5.2 十分钟之后,去看 web 界面
启动成功后,浏览器打开 http://127.0.0.1:56741。你会看到一个监控面板,重点观察三个区域:
- 左侧的统计数字:execs(系统调用执行次数)、coverage(覆盖率)、crash 数量。正常情况下,execs 数字会一直增长,coverage 也会慢慢变多。如果 execs 一直停在某个数字不动,说明某个 vm 卡住了。
- 右侧的 crash 列表:如果这里出现了条目,恭喜,你已经找到了至少一个内核 bug。点进去可以看报告全文。
- 日志面板:manager 会把每个 vm 的状态变化和错误输出都列在这里。排查问题时要习惯先看日志。
5.3 日志文件是你的第一排查线索
manager.log 里最常见的几条信息,我先把它们和对应的处理方法列出来:
| 日志片段 | 含义 | 处理方式 |
|---|---|---|
failed to create vm |
虚拟机创建失败 | 检查 qemu 是否安装、KVM 是否可用、镜像路径是否正确 |
no output from test machine |
虚拟机内 fuzzer 失联 | 多半是 ssh 网络配置问题,看一下 vm 网络和 sshd 状态 |
failed to connect to ssh |
连不上虚拟机的 ssh | 检查 sshkey 路径和权限,检查镜像内是否放入了公钥 |
out of memory |
宿主机内存不足 | 调低 vm.count 或 vm.mem |
corpus is empty |
初始语料为空 | 稍微跑一会再观察,或者检查系统调用白名单是否过窄 |
别一看到 "no output" 就慌,先把日志里对应的 vm id 找出来,然后手动把这台 vm 拉起来看状态。我的经验是,超过一半的 问题都出在 qemu 网桥配置上,而不是 syzkaller 本身。
5.4 手动验证单台虚拟机状态
这里教你一个手动验证手段。如果日志显示 vm-2 出现问题,你可以直接用 qemu 命令把对应的镜像拉起来,手动 ssh 进去看看:
bash复制qemu-system-x86_64 -enable-kvm -m 2048 -smp 2 \
-kernel /syzkaller/linux/arch/x86/boot/bzImage \
-drive file=/syzkaller/image/bullseye.img,format=raw \
-append "root=/dev/sda console=ttyS0" \
-net user -net nic -nographic
启动后如果能在串口看到内核日志,说明镜像和内核都没有问题;如果能在 shell 里手动启动 sshd,说明 ssh 服务没问题。这样就能把问题隔离在 syzkaller 配置层,而不是虚拟化环境层。
6. fuzzing 跑起来之后,这些反直觉现象与处理姿势
manager 一旦稳定跑起来,不代表万事大吉。真正让人上头的,往往是跑了几小时甚至几天之后出现的各种反直觉现象。我把觉得值得说道的几条写在这里,全是我自己在真实 fuzzing 里踩过的。
6.1 机器不卡了,但 coverage 增长非常慢,是正常的吗
新手看到 coverage 数字半天不动,会以为 fuzzer 挂掉了。实际上,当 syzkaller 跑到一定深度之后,覆盖率增长趋缓是常态,尤其你对准的是某个已经被广泛测试的子系统,比如 VFS,几小时后 coverage 可能就涨得很慢。
此时该关注的是 execs 速率,以及有没有新的 crash。只要 execs 速率稳定、crash 列表里偶尔有新增,那这个 fuzzing 就是健康的。
如果你想提高新路径发现率,可以尝试:
- 增大 procs 数,但要同时看 CPU 是否吃紧。
- 调整启用系统调用的范围,放宽白名单,让 fuzzer 有更大探索空间。
- 定期把新的 corpus 导入导出,让其他实例共享有价值输入。
6.2 一堆"重复"的 crash,该不该马上去处理
manager 的去重逻辑并不完美,它会按调用栈把 crash 聚合为"title",所以同一个 root cause 可能因为不同的触发路径而显示成多条。如果你发现 crash 列表里有几条 title 很像,先别急着一个个去分析,而是找到其中一条点进去,看它附带的 reproducer 是否能在你的内核上复现。
真正高效的流程是"收集一批,然后批量复现"。我在跑多实例时,会每隔几小时去 web 界面扫一眼 crash 总数,如果新增了某个新的 title,我就手动把它交给 syz-reproducer 去跑,而不是打断整个 fuzz 进程去深挖。
6.3 manager 突然 OOM,或者宿主机失去响应
这种情况多半是配置时 count 和 mem 乘出来太大。举个例子,你机器 16G 内存,count 设 8,mem 设 2048,那一共就是 16G,宿主机自身还要占一部分,肯定会 OOM。
处理方式:停掉 manager,把 count 调低到 4 或 2,mem 调到 1024 或 2048,再启动。如果还是不稳,可以开 swap 做缓冲,但别完全依赖 swap,毕竟 fuzzing 对延迟敏感,swap 太多会让虚拟机里的 fuzzer 超时率上升。
6.4 磁盘空间被 crash 日志填满
crash 日志和内核转储都很占空间。跑一两个星期,workdir 下可能累积几十 GB 甚至上百 GB。
建议写个简单的清理脚本,定期把不需要的旧 crash 目录压缩归档:
bash复制find /syzkaller/workdir/crashes -type f -mtime +7 -exec gzip {} \;
或者只保留最近的 50 个崩溃目录,其余移动到冷存储:
bash复制ls -1 /syzkaller/workdir/crashes | tail -n +51 | xargs -I {} mv /syzkaller/workdir/crashes/{} /syzkaller/archive/
注意:清理 crash 目录时不要只删除 "crashes" 下的子目录,workdir 下的重放临时文件、镜像快照同样可能占掉大量空间。用 du -sh /syzkaller/workdir/* 查一下哪些目录最大,再对症处理。
6.5 当 fuzzer 跑出 panic,但你没有保留 vmlinux 时
这是最让人抓狂的情况。 manager 解析 crash 堆栈时依赖 vmlinux 里的符号表,如果你编译内核后把 vmlinux 删了或挪了位置,后续 crash 报告就会变成一堆十六进制地址,根本无法定位函数名。
所以从 Step4 开始就要立好规矩:整个 fuzzing 期间,编译产物目录绝对不要清理。每次重新编译内核前,要么把旧的 vmlinux 备份一份,要么直接让 manager 指向新的 vmlinux,然后在报告页面上标注时间点。
我个人踩过的坑是,某次为了释放磁盘空间,把 /syzkaller/linux 下的 vmlinux 删了,结果 manager 还一直跑着,后来报了十几个 crash,我全都只能看到地址,完全没用,懊恼了很久。
6.6 跑着跑着 vm 全挂,manager 却还在
qemu 虚拟机偶尔会因为内核 panic 或资源问题而失联。正常流程是,manager 检测到超时后会重新启动 vm。但如果你看到日志里连续出现大量 "restarting vm",说明 vm 处于一直起不来的状态。
这时候可以到 web 界面的日志面板里看最近一次 vm 的启动过程,常见的根因有:
- 根文件系统镜像损坏,或者磁盘满了。
- 内核 panic 发生得太早,vm 根本没进入 ssh 阶段。
- 宿主机负载过高,qemu 进程频繁被调度出去,导致 vm 内假死。
我遇到最多的是镜像磁盘满。fuzzer 在 vm 内也会写日志和临时文件,时间长了会把镜像撑满,导致系统行为异常。解决思路是在镜像的启动脚本里加一个定时清理任务,或者把 /tmp 做成 tmpfs。
7. 多实例扩展与后续进阶方向
如果你已经能稳定跑通单个 manager,并且拿到过几个 crash,那就可以考虑更复杂的部署姿势了。 syzkaller 在实战中很少只跑一个实例,因为不同内核配置、不同子系统、不同 syscall 集合之间的交集非常少,分开跑才能最大化产出。
7.1 同一台测试电脑上跑多个实例
syzkaller 天然支持多个 manager 实例共存在一台机器上,前提是每个实例有自己的 workdir、自己的 name、自己的 http 端口、自己的 vm 参数。
我的习惯是:
bash复制/syzkaller/my_instance1.cfg
/syzkaller/my_instance2.cfg
两者只有 name、workdir、http 端口、enable_syscalls 不同,其余保持一致。这样可以在同一个内核上,分别聚焦网络子系统和文件系统子系统,相互之间互不干扰。
启动时,分别用独立日志:
bash复制nohup ./bin/syz-manager -config /syzkaller/my_instance1.cfg > /syzkaller/log1.txt 2>&1 &
nohup ./bin/syz-manager -config /syzkaller/my_instance2.cfg > /syzkaller/log2.txt 2>&1 &
这里要记得给每个实例分配不同的 http 端口,否则后启动的 manager 会绑定失败。
7.2 把 crash 结果同步回开发机
测试电脑只管产出 crash,真正分析 bug 通常还是在开发机上舒服。我一般会把测试电脑上 workdir/crashes 目录定期同步回开发机,或者建立一个共享目录,让两端能直接访问。
如果两边系统版本一致,可以用 rsync:
bash复制rsync -avz --progress /syzkaller/workdir/crashes/ dev-machine:/home/user/crashes/
这样做的好处是,你可以把 crash 原始文件和 vmlinux 放在一起分析,便于结合 objdump、addr2line 或者 gdb 做深度定位。
7.3 接入 syzkaller dashboard 或者自动告警
当你手里同时跑着多个实例,每天盯 web 界面就不现实了。更合理的做法是接入类似 syzkaller dashboard 或简单的日志告警方案:检测到 crash 目录下新增文件,就通过邮件、Slack 或企业微信机器人推给你。
我写过一个最简单的监控脚本:
bash复制while true; do
new_crashes=$(find /syzkaller/workdir/crashes -name "*.log" -newer /tmp/.last_snapshot 2>/dev/null | wc -l)
if [ "$new_crashes" -gt 0 ]; then
# 调用 webhook 推送
curl -X POST -H "Content-Type: application/json" \
-d "{\"msg\":\"new crashes count: $new_crashes\"}" \
"https://your-webhook-url"
touch /tmp/.last_snapshot
fi
sleep 300
done
虽然简单,但很实用。尤其在长时间无人值守时,它能帮你第一时间知道出了新 crash,而不是等你第二天去看才发现昨晚已经崩了很多个。
7.4 从"跑通"到"能产出有效 bug"的关键转变
很多人把 Step4 理解为"装好 manager",但我更愿意把它理解为"搭建好从测试到产出的完整管道"。装好 manager 只代表你能把一个 fuzz 任务跑起来,真正有价值的是后续这个循环:
- 持续观察 execs 变化,调优白名单和并发参数;
- 定期检查 crash 列表,及时用 reproducer 复现和归类;
- 根据覆盖率数据调整测试目标,决定是扩大还是收窄 syscall 范围;
- 把有效输入回灌到 corpus 库,让后续 fuzzing 更高效。
manager 只是替你把虚拟机管起来,而你怎么驱动这套系统产出有效 bug,才是经验所在。我见过不少团队搭好环境跑了一周,一个有效 crash 都没出,最后发现纯粹是 config 里 enable_syscalls 太保守,或者 cover 忘开。所以,跑起来只是第一步,跑出东西才是目的。
8. 最后再分享几个小技巧,都是把 manager 用好的人才会注意到的东西
第一,在配置文件里把 "debug": false 改成 "debug": true 会输出更多调试信息,能帮你看到 manager 到底在干什么,但代价是日志量翻倍,生产环境建议不要常开。
第二,如果你的宿主机器上有多块网卡,qemu 默认的网桥选择可能有坑。可以在 vm 配置里显式指定:
json复制"qemu_args": "-enable-kvm -netdev user,id=net0 -device e1000,netdev=net0"
这套配置能避开很多奇怪的网络问题。
第三,及时查看 corpus 目录下的数据。workdir/corpus 里保存了所有有效输入,它们是你的宝贵资产。如果管理器因为种种原因需要重装或者切换实例,先把 corpus 复制出来,新实例可以直接放进 workdir 里,让 syzkaller 从已有基础上继续跑,而不是从零开始。
第四,在你对 syzkaller 内部机制还不够熟悉之前,不要随意改动 max_corpus_size 和 max_corpus_discard 这类高级参数。默认值在很多场景下足够优秀,调错了反而会让 fuzzing 效率明显下降。
第五,给虚拟机镜像做"快照式"隔离。如果你自己动手做根文件系统,建议配置一个开机自启动脚本,每次 vm 启动时自动清空 /tmp、/var/log 等临时目录,让每次 fuzzing 都尽量干净。manager 天然支持这种方式,vm 崩溃后重新拉起时,镜像会自动回到初始状态,但这需要你的镜像本身没有把临时数据写入 overlay 层之外。
如果你能把这几点都做到位,lyzkaller manager 就不只是一个"能启动的服务"了,而是一个真正可以持续产生有效结果的测试平台。等你跑了第一个完整周末、拿到第一个可复现的内核崩溃报告之后,再回来看这些经验,会发现每一句话都踩在点子上,值得你多花一点时间在 Step4 上。
好了,回到开头那句话:manager 在整个 syzkaller 体系里承担的是一个"调度中枢"的职责,配好它、理解它、用好它,你的 fuzzing 产出效率至少翻一倍。接下来就可以进入后续的降级分析、bug 复现和补丁验证阶段了。祝你在内核世界里抓到第一个属于自己的 crash。
