syzkaller - kernel fuzzer

本文档翻译自google/syzkaller,大部分借助于ChatGPT。仅作为我个人的参考,如果你想查阅,建议看英文文档,因为我不确定我记录的中文翻译是否完整和正确。

1 syzkaller - kernel fuzzer

翻译自README.md, 翻译时文件的最新提交是c6f10907c38ce49ddc321539f75aabf0a9ad6c71 all: remove akaros support

CI Status OSS-Fuzz Go Report Card Coverage Status GoDoc License

syzkaller ([siːzˈkɔːlə]) 是一个无监督的覆盖率引导内核模糊测试工具。
支持的操作系统: FreeBSDFuchsiagVisorLinuxNetBSDOpenBSDWindows

邮件列表: syzkaller@googlegroups.com(通过网页电子邮件加入)。

发现的漏洞: Darwin/XNUFreeBSDLinuxNetBSDOpenBSDWindows

1.1 文档

最初,syzkaller 是为 Linux 内核模糊测试开发的,但现在它正在扩展以支持其他操作系统内核。 目前大部分文档与 Linux 内核相关。 对于其他操作系统内核,请参阅: Darwin/XNUFreeBSDFuchsiaNetBSDOpenBSDStarnixWindowsgVisorAkaros

1.2 免责声明

这不是谷歌的官方产品。

2 How to set up syzkaller(docs/setup.md)

翻译自docs/setup.md, 翻译时文件的最新提交是c6f10907c38ce49ddc321539f75aabf0a9ad6c71 all: remove akaros support

通用的 Linux 内核模糊测试设置说明请参见这里

其他内核的设置请参见: FreeBSDDarwin/XNUFuchsiaNetBSDOpenBSDWindows

按照这些说明操作后,你应该能够运行 syz-manager,看到它执行程序,并能够访问暴露在 http://127.0.0.1:56741(或你在 manager 配置中指定的其他地址)的统计信息。 如果一切正常,典型的执行日志应如下所示:

$ ./bin/syz-manager -config=my.cfg
2017/06/14 16:39:05 loading corpus...
2017/06/14 16:39:05 loaded 0 programs (0 total, 0 deleted)
2017/06/14 16:39:05 serving http on http://127.0.0.1:56741
2017/06/14 16:39:05 serving rpc on tcp://127.0.0.1:34918
2017/06/14 16:39:05 booting test machines...
2017/06/14 16:39:05 wait for the connection from test machine...
2017/06/14 16:39:59 received first connection from test machine vm-9
2017/06/14 16:40:05 executed 293, cover 43260, crashes 0, repro 0
2017/06/14 16:40:15 executed 5992, cover 88463, crashes 0, repro 0
2017/06/14 16:40:25 executed 10959, cover 116991, crashes 0, repro 0
2017/06/14 16:40:35 executed 15504, cover 132403, crashes 0, repro 0

此时,确保 syzkaller 能够收集已执行程序的代码覆盖率(除非你在配置中指定 "cover": false 或者你正在模糊测试的内核尚不支持覆盖率)。 网页上的 cover 计数器应为非零。

更多关于配置文件格式的信息,请参见这里

故障排除提示请参见此页面

3 How to set up syzkaller(docs/linux/setup.md)

翻译自docs/linux/setup.md,翻译时文件的最新提交是dd26401e5ae3c1fe62beadcfb937ee5d06f304e2 docs: update required Go version

关于如何使用 syzkaller 进行 Linux 内核模糊测试的通用说明如下所示below

特定虚拟机类型或内核架构的说明可以在以下页面找到:

3.1 install

使用 syzkaller 需要以下组件:

如果遇到任何问题,请查看故障排除页面。

3.1.1 Go 和 syzkaller

syzkaller 是用 Go 编写的,构建需要 Go 1.21+ 工具链。 通常我们旨在支持 Go 的两个最新版本。 可以通过以下方式安装工具链:

wget https://dl.google.com/go/go1.21.4.linux-amd64.tar.gz
tar -xf go1.21.4.linux-amd64.tar.gz
export GOROOT=`pwd`/go
export PATH=$GOROOT/bin:$PATH

请参阅 Go: 下载和安装 以了解其他选项。

下载和构建 syzkaller:

git clone https://github.com/google/syzkaller
cd syzkaller
make

编译后的二进制文件应出现在 bin/ 目录中。

注意: 如果您想进行跨操作系统/架构测试,您需要在 make 中指定 TARGETOSTARGETVMARCHTARGETARCH 参数。详情请参阅 Makefile

3.1.2 环境

如果您在跨架构环境中进行模糊测试,可能需要正确设置 binutils,具体描述见这里

3.1.3 C 编译器

Syzkaller 是一个覆盖率引导的模糊测试器,因此需要内核用覆盖率支持进行构建,这需要一个最新版本的 GCC。 覆盖率支持已提交到 GCC,并在 GCC 6.1.0 或更高版本中发布。 确保您的 GCC 满足此要求,或者获取一个 syzbot 使用的 GCC,点击这里

3.1.4 Linux 内核

除了 GCC 中的覆盖率支持,您还需要在内核端的支持。 KCOV 在 Linux 内核主线版本 4.6 中添加,并可以通过内核配置选项 CONFIG_KCOV=y 启用。 对于较旧的内核,您至少需要回移提交 kernel: add kcov code coverage。 除此之外,建议回移所有涉及 kernel/kcov.c 的内核补丁。

为了启用更多 syzkaller 功能并提高错误检测能力,建议使用额外的配置选项。 详情请参阅此页面

3.1.5 虚拟机设置

Syzkaller 在工作虚拟机或物理设备上执行内核模糊测试。 这些工作环境被称为虚拟机(VM)。 开箱即用的 syzkaller 支持 QEMU、kvmtool 和 GCE 虚拟机、安卓设备和 Odroid C2 板。

以下是 syzkaller 虚拟机的一般要求:

要使用 QEMU syzkaller 虚拟机,您必须在主机系统上安装 QEMU,详情请参阅 QEMU 文档create-image.sh 脚本可用于创建合适的 Linux 镜像。

有关为 QEMU、安卓和其他类型的虚拟机设置 syzkaller 的说明,请参阅文档顶部的链接。

3.1.6 故障排除

4 Setup: Ubuntu host, QEMU vm, x86-64 kernel

翻译自docs/linux/setup_ubuntu-host_qemu-vm_x86-64-kernel.md,翻译时文件的最新提交是d4d447cd780753901f9e00aa246cc835458a8f06 tools/create-image.sh: upgrade default release to bullseye

以下是如何在主机运行 Ubuntu,QEMU 实例运行 Debian Bullseye 的情况下,在 QEMU 中对 x86-64 内核进行模糊测试的说明。

在下面的说明中,$VAR 表示法(例如 $GCC$KERNEL 等)用于表示在执行说明时创建的目录(例如解压 GCC 压缩包时将创建一个目录),或者你需要在运行说明之前自己创建的目录。手动替换这些变量的值。

4.1 安装先决条件

命令:

sudo apt update
sudo apt install make gcc flex bison libncurses-dev libelf-dev libssl-dev

4.2 GCC

如果你的发行版中的 GCC 版本较旧,最好从此列表获取最新的 GCC。下载并解压到 $GCC,你应该在 $GCC/bin/ 目录下有 GCC 二进制文件。

Ubuntu 20.04 LTS: 你可以忽略本节。GCC 已经是最新的版本。

命令:

ls $GCC/bin/
# Sample output:
# cpp     gcc-ranlib  x86_64-pc-linux-gnu-gcc        x86_64-pc-linux-gnu-gcc-ranlib
# gcc     gcov        x86_64-pc-linux-gnu-gcc-9.0.0
# gcc-ar  gcov-dump   x86_64-pc-linux-gnu-gcc-ar
# gcc-nm  gcov-tool   x86_64-pc-linux-gnu-gcc-nm

4.3 内核

4.3.1 检出 Linux 内核源码

命令:

git clone --branch v6.2 git://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git $KERNEL

我们建议从最新的稳定版本开始。v6.2 是一个示例。

4.3.2 生成默认配置

命令:

cd $KERNEL
make defconfig
make kvm_guest.config

或者如果你想指定一个编译器。

命令:

cd $KERNEL
make CC="$GCC/bin/gcc" defconfig
make CC="$GCC/bin/gcc" kvm_guest.config

4.3.3 启用必需的配置选项

根据kernel_configs.md中的描述,启用内核配置选项以支持syzkaller。 不需要全部启用,但至少需要如下配置:

# Coverage collection.
CONFIG_KCOV=y

# Debug info for symbolization.
CONFIG_DEBUG_INFO_DWARF4=y

# Memory bug detector
CONFIG_KASAN=y
CONFIG_KASAN_INLINE=y

# Required for Debian Stretch and later
CONFIG_CONFIGFS_FS=y
CONFIG_SECURITYFS=y

编辑.config文件并手动启用这些选项(或者如果你更喜欢,可以通过make menuconfig完成)。

由于启用这些选项会导致更多的子选项可用,我们需要重新生成配置:

命令:

make olddefconfig

或者如果你想指定一个编译器。

命令:

make CC="$GCC/bin/gcc" olddefconfig

你可能还希望禁用可预测的网络接口命名机制。这可以在syzkaller配置中禁用(详细信息请参见troubleshooting.md),或者通过更新以下内核配置参数来实现:

CONFIG_CMDLINE_BOOL=y
CONFIG_CMDLINE="net.ifnames=0"

4.3.4 Build the Kernel

命令:

make -j`nproc`

或者如果你想指定一个编译器。

命令:

make CC="$GCC/bin/gcc" -j`nproc`

现在你应该有vmlinux(内核二进制文件)和bzImage(压缩内核镜像):

命令:

ls $KERNEL/vmlinux
# sample output - $KERNEL/vmlinux
ls $KERNEL/arch/x86/boot/bzImage
# sample output - $KERNEL/arch/x86/boot/bzImage

4.4 Image

4.4.1 Install debootstrap

命令:

sudo apt install debootstrap

4.4.2 创建 Debian Bullseye Linux 映像

创建一个包含最小必要软件包集的Debian Bullseye Linux镜像。

命令:

mkdir $IMAGE
cd $IMAGE/
wget https://raw.githubusercontent.com/google/syzkaller/master/tools/create-image.sh -O create-image.sh
chmod +x create-image.sh
./create-image.sh

结果应该是 $IMAGE/bullseye.img 磁盘映像。

4.4.3 或者创建不同版本的 Debian Linux 映像

要创建不同版本的 Debian 映像(例如 buster、stretch、sid),请指定 --distribution 选项。

命令:

./create-image.sh --distribution buster

4.4.4 映像额外工具

有时在虚拟机中拥有一些额外的软件包和工具是有用的,即使它们不是运行 syzkaller 所必需的。要安装我们认为有用的一组工具,请执行以下操作(请随意编辑脚本中的工具列表):

命令:

./create-image.sh --feature full

要安装 perf(不需要运行 syzkaller;需要 $KERNEL 指向内核源代码):

命令:

./create-image.sh --add-perf

有关 create-image.sh 的其他选项,请参考 ./create-image.sh -h

4.5 QEMU

4.5.1 Install QEMU

命令:

sudo apt install qemu-system-x86

4.5.2 验证

确保内核启动并且 sshd 启动。

命令:

qemu-system-x86_64 \
    -m 2G \
    -smp 2 \
    -kernel $KERNEL/arch/x86/boot/bzImage \
    -append "console=ttyS0 root=/dev/sda earlyprintk=serial net.ifnames=0" \
    -drive file=$IMAGE/bullseye.img,format=raw \
    -net user,host=10.0.2.10,hostfwd=tcp:127.0.0.1:10021-:22 \
    -net nic,model=e1000 \
    -enable-kvm \
    -nographic \
    -pidfile vm.pid \
    2>&1 | tee vm.log
early console in setup code
early console in extract_kernel
input_data: 0x0000000005d9e276
input_len: 0x0000000001da5af3
output: 0x0000000001000000
output_len: 0x00000000058799f8
kernel_total_size: 0x0000000006b63000

Decompressing Linux... Parsing ELF... done.
Booting the kernel.
[    0.000000] Linux version 4.12.0-rc3+ ...
[    0.000000] Command line: console=ttyS0 root=/dev/sda debug earlyprintk=serial
...
[ ok ] Starting enhanced syslogd: rsyslogd.
[ ok ] Starting periodic command scheduler: cron.
[ ok ] Starting OpenBSD Secure Shell server: sshd.

之后,你应该能够在另一个终端中 ssh 到 QEMU 实例。

命令:

ssh -i $IMAGE/bullseye.id_rsa -p 10021 -o "StrictHostKeyChecking no" root@localhost

4.5.3 疑难解答

如果出现 “too many tries” 错误,可能是 ssh 在传递显式传递的 -i 密钥之前传递了默认密钥。添加选项 -o "IdentitiesOnly yes"

要终止正在运行的 QEMU 实例,按 Ctrl+A 然后 X 或运行:

命令:

kill $(cat vm.pid)

如果 QEMU 正常工作,内核启动并且 ssh 成功,你可以关闭 QEMU 并尝试运行 syzkaller。

4.6 syzkaller

按照此处所述构建 syzkaller。 然后创建如下所示的管理器配置,使用实际值替换环境变量 $GOPATH$KERNEL$IMAGE

{
    "target": "linux/amd64",
    "http": "127.0.0.1:56741",
    "workdir": "$GOPATH/src/github.com/google/syzkaller/workdir",
    "kernel_obj": "$KERNEL",
    "image": "$IMAGE/bullseye.img",
    "sshkey": "$IMAGE/bullseye.id_rsa",
    "syzkaller": "$GOPATH/src/github.com/google/syzkaller",
    "procs": 8,
    "type": "qemu",
    "vm": {
        "count": 4,
        "kernel": "$KERNEL/arch/x86/boot/bzImage",
        "cpu": 2,
        "mem": 2048
    }
}

运行 syzkaller 管理器:

mkdir workdir
./bin/syz-manager -config=my.cfg

现在 syzkaller 应该正在运行,你可以使用浏览器在 127.0.0.1:56741 查看管理器状态。

如果在 syz-manager 启动后遇到问题,请考虑使用 -debug 标志运行它。 还可以参考此页面获取故障排除技巧。

5 How to use syzkaller

翻译自README.md, 翻译时文件的最新提交是7016057751ec811d92392186ea96c53a7253ea5e docs/usage.md: correct grammatical error

5.1 运行

启动 syz-manager 进程,命令如下:

./bin/syz-manager -config my.cfg

syz-manager 进程将启动虚拟机并在其中开始模糊测试。 -config 命令行选项指定配置文件的位置,配置文件描述见这里。 发现的崩溃、统计数据和其他信息会显示在管理器配置中指定的 HTTP 地址上。

5.2 崩溃

一旦 syzkaller 在某个虚拟机中检测到内核崩溃,它将自动开始重现该崩溃的过程(除非你在配置中指定了 "reproduce": false)。 默认情况下,它将使用 4 个虚拟机来重现崩溃,然后最小化导致崩溃的程序。 这可能会暂停模糊测试,因为所有虚拟机可能都忙于重现检测到的崩溃。

重现一次崩溃的过程可能需要几分钟到一个小时不等,这取决于崩溃是否容易重现或根本无法重现。 由于这个过程并不完美,因此可以按照这里描述的方式尝试手动重现崩溃。

如果成功找到重现器,它可以生成两种形式之一: syzkaller 程序或 C 程序。 Syzkaller 总是尝试生成更用户友好的 C 重现器,但有时由于各种原因(例如略有不同的时间安排)会失败。 如果 syzkaller 仅生成了 syzkaller 程序,可以按照这里的方式手动执行它们以重现和调试崩溃。

5.3 集线器

如果你运行多个 syz-manager 实例,可以将它们连接在一起并允许交换程序和重现器,详细信息见这里

5.4 报告漏洞

有关如何报告 Linux 内核漏洞的说明,请查看这里

6 How to reproduce crashes

翻译自docs/reproducing_crashes.md, 翻译时文件的最新提交是52c8379f77b5f292e2d527c66dfe17a899381d20 docs: update docs to reflect the new `async` flag

创建syzkaller错误的重现程序的过程是自动化的,但它并不完美,因此syzkaller提供了一些工具用于手动执行和重现程序。

在manager workdir/crashes 目录中创建的崩溃日志包含在崩溃前执行的程序。在并行执行模式下(当manager配置中的procs参数设置为大于1的值时),导致崩溃的程序不一定立即在崩溃前执行;有问题的程序可能在之前的某个地方。有两个工具可以帮助你识别和最小化导致崩溃的程序: tools/syz-execprogtools/syz-prog2c

tools/syz-execprog 在各种模式下执行单个syzkaller程序或一组程序(一次或无限循环;在线程/碰撞模式下(见下文),有或没有覆盖收集)。你可以通过循环运行崩溃日志中的所有程序来开始,以检查是否至少有一个程序确实使内核崩溃: ./syz-execprog -executor=./syz-executor -repeat=0 -procs=16 -cover=0 crash-log。然后尝试识别导致崩溃的单个程序,可以用 ./syz-execprog -executor=./syz-executor -repeat=0 -procs=16 -cover=0 file-with-a-single-program 测试程序。

注意: syz-execprog 在本地执行程序。所以你需要将 syz-execprogsyz-executor 复制到带有测试内核的虚拟机中并在那里运行。

一旦你有了导致崩溃的单个程序,尝试通过从程序中删除单个系统调用来最小化它(你可以在行首添加#来注释掉单行),以及删除不必要的数据(例如,将&(0x7f0000001000)="73656c6600"系统调用参数替换为 &(0x7f0000001000)=nil)。你还可以尝试将所有的mmap调用合并为一个映射整个所需区域的单个mmap调用。再次使用 syz-execprog 工具测试最小化。

现在你有了一个最小化的程序,检查如果使用 ./syz-execprog -threaded=0 -collide=0 标志崩溃仍然重现。如果没有,那么你需要稍后做一些额外的工作。

现在,运行 syz-prog2c 工具处理程序。它会给你可执行的C源代码。如果崩溃在使用 -threaded/collide=0 标志时重现,那么这个C程序也应该导致崩溃。

如果崩溃在使用 -threaded/collide=0 标志时不可重现,那么你需要最后一步。你可以将线程模式视为每个系统调用在其自己的线程中执行。为了模拟这种执行模式,将单个系统调用移动到单独的线程中。你可以在这里看到一个例子: https://groups.google.com/d/msg/syzkaller/fHZ42YrQM-Y/Z4Xf-BbUDgAJ。

这个过程在 syz-repro 实用程序中有一定程度的自动化。你需要提供你的manager配置和崩溃报告文件。你可以参考示例配置文件

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

它将尝试找到有问题的程序并最小化它。但由于有很多因素可以影响可重现性,它并不总是有效。