meta-openembedded/meta-oe/lib/oeqa/selftest/cases/syzkaller.py
Ovidiu Panait cfac82c560 syzkaller: add recipe and selftest for syzkaller fuzzing
Syzkaller is a coverage-guided fuzzer that is widely used to find bugs in
the Linux kernel:
https://github.com/google/syzkaller

Add the recipe and a selftest for running the fuzzer in a qemux86-64
kvm environment. The following steps can be used to start the test:
"""
cat >> conf/local.conf <<EOF
SYZ_WORKDIR="<path>"
SYZ_FUZZTIME="30"
SYZ_QEMU_VM_COUNT="2"
SYZ_QEMU_MEM="2048"
SYZ_QEMU_CPUS="2"
EOF

oe-selftest -r syzkaller
...
loading corpus...
serving http on http://127.0.0.1:49605
serving rpc on tcp://[::]:46475
booting test machines...
wait for the connection from test machine...
vm-0: crash: KCSAN: data-race in poll_schedule_timeout.constprop.NUM / pollwake
vm-1: crash: KCSAN: data-race in mutex_spin_on_owner
machine check:
syscalls                : 2227/4223
code coverage           : enabled
comparison tracing      : enabled
extra coverage          : enabled
delay kcov mmap         : mmap returned an invalid pointer
setuid sandbox          : enabled
namespace sandbox       : enabled
Android sandbox         : /sys/fs/selinux/policy does not exist
fault injection         : enabled
leak checking           : enabled
net packet injection    : enabled
net device setup        : enabled
concurrency sanitizer   : enabled
devlink PCI setup       : PCI device 0000:00:10.0 is not available
USB emulation           : enabled
hci packet injection    : enabled
wifi device emulation   : enabled
802.15.4 emulation      : enabled
corpus                  : 0 (deleted 0 broken)
seeds                   : 0/0
VMs 2, executed 1, cover 0, signal 0/0, crashes 2, repro 0
vm-1: crash: KCSAN: data-race in mutex_spin_on_owner
"""

This will fuzz the yocto kernel for 30 minutes using 2 qemu VMs, each VM
getting 2048MB of memory and 2 CPUs.

The path in SYZ_WORKDIR must be an absolute path that is persistent across
oe-selftest runs, so that fuzzing does not start all over again on each
invocation. Syzkaller will save the corpus database in that directory and will
use the database to keep track of the interfaces already fuzzed.

After the test is done, <workdir>/crashes directory will contain the report
files for all the bugs found.

Signed-off-by: Ovidiu Panait <ovidiu.panait@windriver.com>
Signed-off-by: Khem Raj <raj.khem@gmail.com>
2022-10-21 09:57:59 -07:00

125 lines
4.4 KiB
Python

#
# SPDX-License-Identifier: MIT
#
from oeqa.selftest.case import OESelftestTestCase
from oeqa.utils.commands import runCmd, bitbake, get_bb_var, get_bb_vars
from oeqa.utils.network import get_free_port
class TestSyzkaller(OESelftestTestCase):
def setUpSyzkallerConfig(self, os_arch, qemu_postfix):
syz_target_sysroot = get_bb_var('PKGD', 'syzkaller')
syz_target = os.path.join(syz_target_sysroot, 'usr')
qemu_native_bin = os.path.join(self.syz_native_sysroot, 'usr/bin/qemu-system-' + qemu_postfix)
kernel_cmdline = "ip=dhcp rootfs=/dev/sda dummy_hcd.num=%s" % (self.dummy_hcd_num)
kernel_objdir = self.deploy_dir_image
port = get_free_port()
if not os.path.exists(self.syz_workdir):
os.mkdir(self.syz_workdir)
with open(self.syz_cfg, 'w') as f:
f.write(
"""
{
"target": "%s",
"http": "127.0.0.1:%s",
"workdir": "%s",
"kernel_obj": "%s",
"kernel_src": "%s",
"image": "%s",
"syzkaller": "%s",
"type": "qemu",
"reproduce" : false,
"sandbox": "none",
"vm": {
"count": %s,
"kernel": "%s",
"cmdline": "%s",
"cpu": %s,
"mem": %s,
"qemu": "%s",
"qemu_args": "-device virtio-scsi-pci,id=scsi -device scsi-hd,drive=rootfs -enable-kvm -cpu host,migratable=off",
"image_device": "drive index=0,id=rootfs,if=none,media=disk,file="
}
}
"""
% (os_arch, port, self.syz_workdir, kernel_objdir, self.kernel_src,
self.rootfs, syz_target, self.syz_qemu_vms, self.kernel, kernel_cmdline,
self.syz_qemu_cpus, self.syz_qemu_mem, qemu_native_bin))
def test_syzkallerFuzzingQemux86_64(self):
self.image = 'core-image-minimal'
self.machine = 'qemux86-64'
self.fstype = "ext4"
self.write_config(
"""
MACHINE = "%s"
IMAGE_FSTYPES = "%s"
KERNEL_IMAGETYPES += "vmlinux"
EXTRA_IMAGE_FEATURES += " ssh-server-openssh"
IMAGE_ROOTFS_EXTRA_SPACE = "512000"
KERNEL_EXTRA_FEATURES += " \
cfg/debug/syzkaller/debug-syzkaller.scc \
"
IMAGE_INSTALL:append = " syzkaller"
"""
% (self.machine, self.fstype))
build_vars = ['TOPDIR', 'DEPLOY_DIR_IMAGE', 'STAGING_KERNEL_DIR']
syz_fuzz_vars = ['SYZ_WORKDIR', 'SYZ_FUZZTIME', 'SYZ_QEMU_MEM', 'SYZ_QEMU_CPUS', 'SYZ_QEMU_VM_COUNT']
syz_aux_vars = ['SYZ_DUMMY_HCD_NUM']
needed_vars = build_vars + syz_fuzz_vars + syz_aux_vars
bb_vars = get_bb_vars(needed_vars)
for var in syz_fuzz_vars:
if not bb_vars[var]:
self.skipTest(
"""
%s variable not set.
Please configure %s fuzzing parameters to run this test.
Example local.conf config:
SYZ_WORKDIR="<path>" # syzkaller workdir location (must be persistent across os-selftest runs)
SYZ_FUZZTIME="30" # fuzzing time in minutes
SYZ_QEMU_VM_COUNT="1" # number of qemu VMs to be used for fuzzing
SYZ_QEMU_MEM="2048"' # memory used by each qemu VM
SYZ_QEMU_CPUS="2"' # number of cpus used by each qemu VM
"""
% (var, ', '.join(syz_fuzz_vars)))
self.topdir = bb_vars['TOPDIR']
self.deploy_dir_image = bb_vars['DEPLOY_DIR_IMAGE']
self.kernel_src = bb_vars['STAGING_KERNEL_DIR']
"""
SYZ_WORKDIR must be set to an absolute path where syzkaller will store
the corpus database, config, runtime and crash data generated during
fuzzing. It must be persistent between oe-selftest runs, so the fuzzer
does not start over again on each run.
"""
self.syz_workdir = bb_vars['SYZ_WORKDIR']
self.syz_fuzztime = int(bb_vars['SYZ_FUZZTIME']) * 60
self.syz_qemu_mem = int(bb_vars['SYZ_QEMU_MEM'])
self.syz_qemu_cpus = int(bb_vars['SYZ_QEMU_CPUS'])
self.syz_qemu_vms = int(bb_vars['SYZ_QEMU_VM_COUNT'])
self.dummy_hcd_num = int(bb_vars['SYZ_DUMMY_HCD_NUM'] or 8)
self.syz_cfg = os.path.join(self.syz_workdir, 'syzkaller.cfg')
self.kernel = os.path.join(self.deploy_dir_image, 'bzImage')
self.rootfs = os.path.join(self.deploy_dir_image, '%s-%s.%s' % (self.image, self.machine, self.fstype))
self.syz_native_sysroot = get_bb_var('RECIPE_SYSROOT_NATIVE', 'syzkaller-native')
self.setUpSyzkallerConfig("linux/amd64", "x86_64")
bitbake(self.image)
bitbake('syzkaller')
bitbake('syzkaller-native -c addto_recipe_sysroot')
cmd = "syz-manager -config %s" % self.syz_cfg
runCmd(cmd, native_sysroot = self.syz_native_sysroot, timeout=self.syz_fuzztime, output_log=self.logger, ignore_status=True, shell=True)