1#
2# SPDX-License-Identifier: MIT
3#
4
5from oeqa.selftest.case import OESelftestTestCase
6from oeqa.utils.commands import runCmd, bitbake, get_bb_var, get_bb_vars
7from oeqa.utils.network import get_free_port
8
9class TestSyzkaller(OESelftestTestCase):
10    def setUpSyzkallerConfig(self, os_arch, qemu_postfix):
11        syz_target_sysroot = get_bb_var('PKGD', 'syzkaller')
12        syz_target = os.path.join(syz_target_sysroot, 'usr')
13
14        qemu_native_bin = os.path.join(self.syz_native_sysroot, 'usr/bin/qemu-system-' + qemu_postfix)
15        kernel_cmdline = "ip=dhcp rootfs=/dev/sda dummy_hcd.num=%s" % (self.dummy_hcd_num)
16        kernel_objdir = self.deploy_dir_image
17        port = get_free_port()
18
19        if not os.path.exists(self.syz_workdir):
20            os.mkdir(self.syz_workdir)
21
22        with open(self.syz_cfg, 'w') as f:
23            f.write(
24"""
25{
26	"target": "%s",
27	"http": "127.0.0.1:%s",
28	"workdir": "%s",
29	"kernel_obj": "%s",
30	"kernel_src": "%s",
31	"image": "%s",
32	"syzkaller": "%s",
33	"type": "qemu",
34	"reproduce" : false,
35	"sandbox": "none",
36	"vm": {
37		"count": %s,
38		"kernel": "%s",
39		"cmdline": "%s",
40		"cpu": %s,
41		"mem": %s,
42		"qemu": "%s",
43		"qemu_args": "-device virtio-scsi-pci,id=scsi -device scsi-hd,drive=rootfs -enable-kvm -cpu host,migratable=off",
44		"image_device": "drive index=0,id=rootfs,if=none,media=disk,file="
45	}
46}
47"""
48% (os_arch, port, self.syz_workdir, kernel_objdir, self.kernel_src,
49   self.rootfs, syz_target, self.syz_qemu_vms, self.kernel, kernel_cmdline,
50   self.syz_qemu_cpus, self.syz_qemu_mem, qemu_native_bin))
51
52    def test_syzkallerFuzzingQemux86_64(self):
53        self.image = 'core-image-minimal'
54        self.machine = 'qemux86-64'
55        self.fstype = "ext4"
56
57        self.write_config(
58"""
59MACHINE = "%s"
60IMAGE_FSTYPES = "%s"
61KERNEL_IMAGETYPES += "vmlinux"
62EXTRA_IMAGE_FEATURES += " ssh-server-openssh"
63IMAGE_ROOTFS_EXTRA_SPACE = "512000"
64KERNEL_EXTRA_FEATURES += " \
65    cfg/debug/syzkaller/debug-syzkaller.scc \
66"
67IMAGE_INSTALL:append = " syzkaller"
68"""
69% (self.machine, self.fstype))
70
71        build_vars = ['TOPDIR', 'DEPLOY_DIR_IMAGE', 'STAGING_KERNEL_DIR']
72        syz_fuzz_vars = ['SYZ_WORKDIR', 'SYZ_FUZZTIME', 'SYZ_QEMU_MEM', 'SYZ_QEMU_CPUS', 'SYZ_QEMU_VM_COUNT']
73        syz_aux_vars = ['SYZ_DUMMY_HCD_NUM']
74
75        needed_vars = build_vars + syz_fuzz_vars + syz_aux_vars
76        bb_vars = get_bb_vars(needed_vars)
77
78        for var in syz_fuzz_vars:
79                if not bb_vars[var]:
80                    self.skipTest(
81"""
82%s variable not set.
83Please configure %s fuzzing parameters to run this test.
84
85Example local.conf config:
86SYZ_WORKDIR="<path>"  # syzkaller workdir location (must be persistent across os-selftest runs)
87SYZ_FUZZTIME="30"     # fuzzing time in minutes
88SYZ_QEMU_VM_COUNT="1" # number of qemu VMs to be used for fuzzing
89SYZ_QEMU_MEM="2048"'  # memory used by each qemu VM
90SYZ_QEMU_CPUS="2"'    # number of cpus used by each qemu VM
91"""
92% (var, ', '.join(syz_fuzz_vars)))
93
94        self.topdir = bb_vars['TOPDIR']
95        self.deploy_dir_image = bb_vars['DEPLOY_DIR_IMAGE']
96        self.kernel_src = bb_vars['STAGING_KERNEL_DIR']
97
98        """
99        SYZ_WORKDIR must be set to an absolute path where syzkaller will store
100        the corpus database, config, runtime and crash data generated during
101        fuzzing. It must be persistent between oe-selftest runs, so the fuzzer
102        does not start over again on each run.
103        """
104        self.syz_workdir = bb_vars['SYZ_WORKDIR']
105        self.syz_fuzztime = int(bb_vars['SYZ_FUZZTIME']) * 60
106        self.syz_qemu_mem = int(bb_vars['SYZ_QEMU_MEM'])
107        self.syz_qemu_cpus = int(bb_vars['SYZ_QEMU_CPUS'])
108        self.syz_qemu_vms = int(bb_vars['SYZ_QEMU_VM_COUNT'])
109        self.dummy_hcd_num = int(bb_vars['SYZ_DUMMY_HCD_NUM'] or 8)
110
111        self.syz_cfg = os.path.join(self.syz_workdir, 'syzkaller.cfg')
112        self.kernel = os.path.join(self.deploy_dir_image, 'bzImage')
113        self.rootfs = os.path.join(self.deploy_dir_image, '%s-%s.%s' % (self.image, self.machine, self.fstype))
114
115        self.syz_native_sysroot = get_bb_var('RECIPE_SYSROOT_NATIVE', 'syzkaller-native')
116
117        self.setUpSyzkallerConfig("linux/amd64", "x86_64")
118
119        bitbake(self.image)
120        bitbake('syzkaller')
121        bitbake('syzkaller-native -c addto_recipe_sysroot')
122
123        cmd = "syz-manager -config %s" % self.syz_cfg
124        runCmd(cmd, native_sysroot = self.syz_native_sysroot, timeout=self.syz_fuzztime, output_log=self.logger, ignore_status=True, shell=True)
125