Skip to content
 编辑

How to reproduce crashes

如何复现 syzkaller 崩溃

使用 C 语言复现程序

如果该漏洞是由 syzbot 报告的,首先需要构建该工具当时所用的内核版本。syzbot 会在报告里提供必要的信息:

Hello,

syzbot found the following issue on:

HEAD commit:    ae58226b89ac Add linux-next specific files for 20241118
git tree:       linux-next
console+strace: https://syzkaller.appspot.com/x/log.txt?x=14a67378580000
kernel config:  https://syzkaller.appspot.com/x/.config?x=45719eec4c74e6ba
dashboard link: https://syzkaller.appspot.com/bug?extid=2159cbb522b02847c053
compiler:       Debian clang version 15.0.6, GNU ld (GNU Binutils for Debian) 2.40
syz repro:      https://syzkaller.appspot.com/x/repro.syz?x=137beac0580000
C reproducer:   https://syzkaller.appspot.com/x/repro.c?x=177beac0580000

在这种情况下,你需要执行以下操作:

$ git checkout ae58226b89ac
$ wget -O '.config' 'https://syzkaller.appspot.com/x/.config?x=45719eec4c74e6ba`
$ make CC=clang LD=ld.lld olddefconfig
$ make CC=clang LD=ld.lld -j$(nproc)

你还需要一个可启动的磁盘镜像。目前 syzbot 使用基于 Buildroot 的小型镜像,你可以选择在本地构建build locally或直接下载download

下载并构建复现环境:

$ wget -O 'repro.c' 'https://syzkaller.appspot.com/x/repro.c?x=177beac0580000'
$ gcc repro.c -lpthread -static -o repro

运行虚拟机:

$ export DISK_IMAGE='buildroot_amd64_2024.09'
$ qemu-system-x86_64 -m 2G -smp 2,sockets=2,cores=1 -drive file=$DISK_IMAGE,format=raw -net nic,model=e1000 -net user,host=10.0.2.10,hostfwd=tcp::10022-:22 -enable-kvm -nographic -snapshot -machine pc-q35-7.1

运行复现程序:

$ scp -P 10022 -o UserKnownHostsFile=/dev/null  -o StrictHostKeyChecking=no -o IdentitiesOnly=yes ./repro root@127.0.0.1:/root/
$ ssh -p 10022 -o UserKnownHostsFile=/dev/null  -o StrictHostKeyChecking=no -o IdentitiesOnly=yes root@127.0.0.1 'chmod +x ./repro && ./repro'

使用 Syz 复现程序

Syzkaller 总是首先生成一个” Syz “复现程序(使用 Syzkaller DSL 编写)。随后,Syzkaller 会尝试将这个 Syz 复现程序转换为 C 语言代码。但由于 syz-executor 运行环境与 C 复现程序模拟环境之间存在差异,这个转换过程并不总是能成功。因此,在某些情况下,可能只有 Syz 版本的复现程序可用。

要在本地运行 Syz 复现程序,所需的操作步骤与前一节基本相同。

下载并构建 syzkaller。若已安装 Docker,步骤会更简单:

$ git clone https://github.com/google/syzkaller.git
$ cd syzkaller
$ ./tools/syz-env make

按照上文所述步骤构建内核并启动虚拟机。

下载复现程序:

$ wget -O 'repro.syz' 'https://syzkaller.appspot.com/x/repro.syz?x=137beac0580000'

将复现程序和 syzkaller 二进制文件复制到测试机:

$ export SYZKALLER_PATH="~/syzkaller"
$ scp -P 10022 -o UserKnownHostsFile=/dev/null  -o StrictHostKeyChecking=no -o IdentitiesOnly=yes $SYZKALLER_PATH/bin/linux_amd64/* ./repro.syz root@127.0.0.1:/root/

现在您可以使用 syz-execprog 工具来真正执行程序。

$ ssh -p 10022 -o UserKnownHostsFile=/dev/null  -o StrictHostKeyChecking=no -o IdentitiesOnly=yes root@127.0.0.1 './syz-execprog -enable=all -repeat=0 -procs=6 ./repro.syz'

以下是 syz-execprog 工具的几个实用参数说明:

  -procs int
        指定并行执行的进程数量(默认值:1)
  -repeat int
    	设置程序重复执行次数(0表示无限循环,默认值:1)
  -sandbox string
    	指定模糊测试使用的沙箱类型(可选值:none/setuid/namespace,默认值:"setuid")
  -threaded
    	在执行器中启用线程模式(默认启用)

当设置 -threaded=0 时,所有系统调用将在同一线程中顺序执行;而 -threaded=1 则会强制每个系统调用在独立线程中执行,从而确保即使遇到阻塞式系统调用也能继续推进测试流程。

在 2021 年之前的版本中,syz-execprog 还支持以下附加参数:

  -collide
        强制系统调用并发执行以触发数据竞争(默认启用)

-collide=1 参数会强制在系统调用对并发执行时进行第二轮调用,以检测潜在的数据竞争问题。

从版本修订 fd8caa54 开始,该行为已改由 syzlang 语言直接控制。如果您运行的是旧版本的复现程序,可能仍需显式设置 -collide=1 参数。

当您复现的程序包含如下格式的头部声明时:

# {Threaded:true Repeat:true RepeatTimes:0 Procs:8 Slowdown:1 Sandbox:none Leak:false NetInjection:true NetDevices:true NetReset:true Cgroups:true BinfmtMisc:true CloseFDs:true KCSAN:false DevlinkPCI:false USB:true VhciInjection:true Wifi:true IEEE802154:true Sysctl:true UseTmpDir:true HandleSegv:true Repro:false Trace:false LegacyOptions:{Collide:false Fault:false FaultCall:0 FaultNth:0}}

则需要根据头部参数显式配置 syz-execprog 的运行选项。具体对应关系如下:

使用 ktest 工具

ktest 是 Linux 内核测试套件,提供了一套简化本地测试的基础设施。

其中特别集成了 syzbot-repro.ktest 测试用例,能够自动化完成构建指定版本内核、启动虚拟机环境、获取 syzbot 漏洞报告详情、执行复现测试的流程。

​安装步骤如下:

$ git clone git://evilpiepirate.org/ktest.git
$ cd ktest
$ export KTEST_PATH=$(pwd)
$ sudo ./root_image init
$ sudo ./root_image create
$ cargo install --path $KTEST_PATH

复现 syzbot 漏洞流程如下:

$ cd ~/linux
$ git checkout <kernel-commit>
$ $KTEST_PATH/build-test-kernel run $KTEST_PATH/tests/syzbot-repro.ktest <bug-id>

​​ 其中 <bug-id> 可从 syzbot 报告中获取,例如:

dashboard link: https://syzkaller.appspot.com/bug?extid=2159cbb522b02847c053

此处 <bug-id> 即为 2159cbb522b02847c053.

使用可下载资源

每个 syzbot 报告都会提供发现漏洞时使用的精确磁盘镜像、内核镜像和 vmlinux 文件。具体使用方法请参考对应文档

从执行日志复现

虽然 syzbot 自动生成复现程序的过程已实现自动化,但在某些情况下仍无法精确定位到导致内核崩溃的单个程序。

本地 syzkaller 实例

​​ 管理器目录 workdir/crashes 中的崩溃日志包含崩溃前执行的程序。在并行模式(manager 配置中 procs 参数 > 1)下,导致崩溃的程序不一定紧邻崩溃记录,可能出现在更早的位置。

syzbot 报告

​​ 提供的执行日志:

console output: https://syzkaller.appspot.com/x/log.txt?x=148914c0580000

手动制作复现程序

有两个工具可以帮助你定位并最小化导致崩溃的程序:syz-execprogsyz-prog2c。你可以分别通过 make execprogmake prog2c 来构建它们。

syz-execprog 可以执行单个 syzkaller 程序或一组程序,支持多种模式(单次执行或无限循环;线程模式/collide 模式;是否收集覆盖率)。

您可以先从崩溃日志中运行所有程序,检查是否确实会导致内核崩溃:

./syz-execprog -executor=./syz-executor -repeat=0 -procs=8 -cover=0 crash-log-file.txt

注意:syz-execprog 需在本地执行,因此您需要将 syz-execprogsyz-executor 拷贝到运行测试内核的虚拟机中执行。详情参考 Using a Syz reproducer 一节。

要定位是哪一个程序触发了崩溃,您可以从 crash-log-file.txt 中逐个提取程序,并单独运行 syz-execprog 进行测试。

当您找到一个能触发崩溃的单独程序后,可以尝试以下方式进行最小化:

每次修改后,记得用 syz-execprog 工具测试最小化结果是否仍能触发崩溃。

在获得最小化程序后,使用 ./syz-execprog -threaded=0 -collide=0 命令检查崩溃是否仍能在非线程模式下复现;若不可复现,则后续需要执行额外步骤。

使用 syz-prog2c 工具将程序转换为可执行的 C 源码。如果崩溃在 -threaded=0 -collide=0 模式下仍能复现,那么生成的 C 程序也应该能触发相同崩溃。

若上述方法无效,需模拟线程执行环境。可将各系统调用分配到独立线程中执行,示例参考:https://groups.google.com/d/msg/syzkaller/fHZ42YrQM-Y/Z4Xf-BbUDgAJ.

syz-repro 工具可部分自动化上述流程。您需要提供 manager 的配置文件和崩溃报告文件,可参考示例配置文件

./syz-repro -config my.cfg crash-qemu-1-1455745459265726910

它会尝试定位并最小化触发崩溃的程序。由于影响复现的因素众多,其效果并不总是理想。