syzkaller - 内核模糊测试工具

参考:

1 软件环境

打开All releases - The Go Programming Language,下载最新版本,如go1.22.5.linux-amd64.tar.gz:

wget https://go.dev/dl/go1.22.5.linux-amd64.tar.gz
tar xvf go1.22.5.linux-amd64.tar.gz
export GOROOT=`pwd`/go
export PATH=$GOROOT/bin:$PATH

编译syzkaller源码:

git clone https://github.com/google/syzkaller
cd syzkaller
make # 编译结果在 bin/

安装软件:

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

内核x86_64-config文件还要打开以下配置:

# Debug info for symbolization.
CONFIG_DEBUG_INFO_DWARF4=y

# Memory bug detector,这玩意儿会导致运行很慢,所以如果不是测试的不要打开
CONFIG_KASAN=y
CONFIG_KASAN_INLINE=y

2 生成qcow2镜像

sudo apt install debootstrap -y
mkdir syzkaller-image
cd syzkaller-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中的DEBOOTSTRAP_PARAMS="--keyring /usr/share/keyrings/debian-archive-removed-keys.gpg $DEBOOTSTRAP_PARAMS后面的链接修改成https://repo.huaweicloud.com/debian/。但有些网络下也不会加快太多,可以想办法访问国外网络进行下载。

再运行脚本生成bullseye.img:

./create-image.sh

生成bullseye.img后,用以下脚本启动测试一下:

qemu-system-x86_64 \
    -m 2G \
    -smp 16 \
    -kernel /home/sonvhi/chenxiaosong/code/x86_64-linux/build/arch/x86/boot/bzImage \
    -append "console=ttyS0 root=/dev/sda earlyprintk=serial net.ifnames=0" \
    -drive file=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 \

确保能远程登录:

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

测试完后,要把虚拟机关机,因为syzkaller会自己启动虚拟机。如果你已经用上面的脚本启动了虚拟机,再启动syzkaller就会启动失败,而且qcow2镜像也会损坏。

3 运行

到syzkaller源码目录下,创建my.cfg文件如下:

{
    "target": "linux/amd64",
    "http": "0.0.0.0:56741",
    "workdir": "workdir",
    # 这个应该是vmlinux的路径
    "kernel_obj": "/home/sonvhi/chenxiaosong/code/x86_64-linux/build/",
    "image": "/home/sonvhi/chenxiaosong/syzkaller-image/bullseye.img",
    "sshkey": "/home/sonvhi/chenxiaosong/syzkaller-image/bullseye.id_rsa",
    "syzkaller": ".",
    # 只测 chmod 系统调用
    # "enable_syscalls": ["chmod"],
    "procs": 8,
    "type": "qemu",
    "vm": {
        "count": 4,
        "kernel": "/home/sonvhi/chenxiaosong/code/x86_64-linux/build/arch/x86/boot/bzImage",
        "cpu": 2,
        "mem": 2048
    }
}

运行:

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

这时就能通过网页查看测试结果。如果你是在docker中运行syzkaller,想在加一台电脑上访问网页,可以在宿主机中安装nginx,并在nginx配置文件/etc/nginx/sites-enabled/default中添加以下内容,172.17.0.3是docker的ip:

server {
        listen 56741;

        location / {
                proxy_pass http://172.17.0.3:56741/;
        }
}

这时就可以在其他电脑上访问http://192.168.3.224:56741/192.168.3.224是宿主机的ip)。

4 构造一个简单的bug

连续两次chmod调用的mode入参为0时,产生空指针解引用的bug,这个函数的执行路径是chmod() -> do_fchmodat() -> chmod_common()

diff --git a/fs/open.c b/fs/open.c
index 50e45bc7c4d8..ee7962ca777d 100644
--- a/fs/open.c
+++ b/fs/open.c
@@ -637,6 +637,12 @@ int chmod_common(const struct path *path, umode_t mode)
        struct iattr newattrs;
        int error;

+        static umode_t old_mode = 0xffff;
+        if (old_mode == 0 && mode == 0) {
+                path = NULL;
+        }
+        old_mode = mode;
+
        error = mnt_want_write(path->mnt);
        if (error)
                return error;

到syzkaller源码目录下,my.cfg文件修改成如下,增加enable_syscalls,只测试chmod:

{
    "target": "linux/amd64",
    "http": "0.0.0.0:56741",
    "workdir": "workdir",
    # 这个应该是vmlinux的路径
    "kernel_obj": "/home/sonvhi/chenxiaosong/code/x86_64-linux/build/",
    "image": "/home/sonvhi/chenxiaosong/syzkaller-image/bullseye.img",
    "sshkey": "/home/sonvhi/chenxiaosong/syzkaller-image/bullseye.id_rsa",
    "syzkaller": ".",
    # 只测 chmod 系统调用
    "enable_syscalls": ["chmod"],
    "procs": 8,
    "type": "qemu",
    "vm": {
        "count": 4,
        "kernel": "/home/sonvhi/chenxiaosong/code/x86_64-linux/build/arch/x86/boot/bzImage",
        "cpu": 2,
        "mem": 2048
    }
}

运行:

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

5 复现

可以在syzbot中找发现的bug,有crash的日志和复现程序(syz和C),把bin/linux_amd64/复制到要测试的虚拟机中,按以下步骤复现。

echo 1 > /proc/sys/kernel/panic_on_oops # 注意不能用 vim 编辑
cat /proc/sys/kernel/panic_on_oops # 确认是否生效
./linux_amd64/syz-execprog -executor=./linux_amd64/syz-executor -repeat=0 -procs=16 -cover=0 crash-log
./linux_amd64/syz-execprog -executor=./linux_amd64/syz-executor -repeat=0 -procs=16 -cover=0 file-with-a-single-program