def setup_qemu_values(initial_setup=True): """Sets up and runs a QEMU VM in the background. Returns a process.Popen object. Does not block the calling process, and teardown must be handled by the caller (use .kill()). Fuchsia fuzzers assume a QEMU VM is running; call this routine prior to beginning Fuchsia fuzzing tasks. This initialization routine assumes the following layout for fuchsia_resources_dir: * /qemu-for-fuchsia/* * /.ssh/* * target/x64/fvm.blk * target/x64/fuchsia.zbi * target/x64/multiboot.bin * build/out/default/fuzzers.json * build/out/default/ids.txt * build/out/default.zircon/tools/* * build/zircon/prebuilt/downloads/symbolize * build/buildtools/linux-x64/clang/bin/llvm-symbolizer""" # First download the Fuchsia resources locally. fuchsia_resources_dir = environment.get_value('FUCHSIA_RESOURCES_DIR') if not fuchsia_resources_dir: raise errors.FuchsiaConfigError('Could not find FUCHSIA_RESOURCES_DIR') # Then, save paths for necessary commands later. qemu_path = os.path.join(fuchsia_resources_dir, 'qemu-for-fuchsia', 'bin', 'qemu-system-x86_64') os.chmod(qemu_path, 0o550) kernel_path = os.path.join(fuchsia_resources_dir, 'target', 'x64', 'multiboot.bin') os.chmod(kernel_path, 0o644) pkey_path = os.path.join(fuchsia_resources_dir, '.ssh', 'pkey') os.chmod(pkey_path, 0o400) sharefiles_path = os.path.join(fuchsia_resources_dir, 'qemu-for-fuchsia', 'share', 'qemu') drive_path = os.path.join(fuchsia_resources_dir, 'target', 'x64', 'fvm.blk') os.chmod(drive_path, 0o644) fuchsia_zbi = os.path.join(fuchsia_resources_dir, 'target', 'x64', 'fuchsia.zbi') initrd_path = os.path.join(fuchsia_resources_dir, 'fuchsia-ssh.zbi') # Perform some more initiailization steps. # Only do these the first time you run QEMU after downloading the build. if initial_setup: extend_fvm(fuchsia_resources_dir, drive_path) add_keys_to_zbi(fuchsia_resources_dir, initrd_path, fuchsia_zbi) # Get a free port for the VM, so we can SSH in later. tcp = socket.socket(socket.AF_INET, socket.SOCK_STREAM) tcp.bind(('localhost', 0)) _, port = tcp.getsockname() tcp.close() # Fuzzing jobs that SSH into the QEMU VM need access to this env var. environment.set_value('FUCHSIA_PORTNUM', port) environment.set_value('FUCHSIA_RESOURCES_DIR', fuchsia_resources_dir) # yapf: disable qemu_args = [ '-m', '2048', '-nographic', '-kernel', kernel_path, '-initrd', initrd_path, '-smp', '4', '-drive', 'file=' + drive_path + ',format=raw,if=none,id=blobstore', '-device', 'virtio-blk-pci,drive=blobstore', '-monitor', 'none', '-append', '"kernel.serial=legacy TERM=dumb"', '-machine', 'q35', '-display', 'none', '-netdev', ('user,id=net0,net=192.168.3.0/24,dhcpstart=192.168.3.9,' 'host=192.168.3.2,hostfwd=tcp::') + str(port) + '-:22', '-device', 'e1000,netdev=net0,mac=52:54:00:63:5e:7b', '-L', sharefiles_path ] # yapf: enable # Detecing KVM is tricky, so use an environment variable to determine whether # to turn it on or not. if environment.get_value('FUCHSIA_USE_KVM'): qemu_args.extend(['-cpu', 'host,migratable=no']) qemu_args.append('-enable-kvm') else: # Can't use host CPU since we don't necessarily have KVM on the machine. # Emulate a Haswell CPU with a few feature toggles. This mirrors the most # common configuration for Fuchsia VMs when using in-tree tools. qemu_args.extend(['-cpu', 'Haswell,+smap,-check,-fsgsbase']) # Get the list of fuzzers for ClusterFuzz to choose from. host = Host.from_dir( os.path.join(fuchsia_resources_dir, 'build', 'out', 'default')) Device(host, 'localhost', str(port)) Fuzzer.filter(host.fuzzers, '') # Fuzzing jobs that SSH into the QEMU VM need access to this env var. environment.set_value('FUCHSIA_PKEY_PATH', pkey_path) return qemu_path, qemu_args
def create(self): """Configures a QEMU process which can subsequently be `run`. Assumes that initial_qemu_setup was already called exactly once. """ qemu_vars = _fetch_qemu_vars() # Get a free port for the VM, so we can SSH in later. tcp = socket.socket(socket.AF_INET, socket.SOCK_STREAM) tcp.bind(('localhost', 0)) _, port = tcp.getsockname() tcp.close() # Fuzzing jobs that SSH into the QEMU VM need access to this env var. environment.set_value('FUCHSIA_PORTNUM', port) environment.set_value('FUCHSIA_RESOURCES_DIR', qemu_vars['fuchsia_resources_dir']) # yapf: disable qemu_args = [ '-m', '3072', '-nographic', '-kernel', qemu_vars['kernel_path'], '-initrd', qemu_vars['initrd_path'], '-smp', '4', '-drive', ('file=' + qemu_vars['drive_path'] + ',format=raw,if=none,' 'id=blobstore'), '-device', 'virtio-blk-pci,drive=blobstore', '-monitor', 'none', '-append', 'kernel.serial=legacy TERM=dumb', '-machine', 'q35', '-display', 'none', '-netdev', ('user,id=net0,net=192.168.3.0/24,dhcpstart=192.168.3.9,' 'host=192.168.3.2,hostfwd=tcp::') + str(port) + '-:22', '-device', 'e1000,netdev=net0,mac=52:54:00:63:5e:7b', '-L', qemu_vars['sharefiles_path'] ] # yapf: enable # Detecting KVM is tricky, so use an environment variable to determine # whether to turn it on or not. if environment.get_value('FUCHSIA_USE_KVM'): # In builds before fxrev.dev/375343, a bug prevents booting with newer # versions of KVM. On some of these older builds, # `kernel.x86.disable-spec-mitigations` also doesn't work as # expected, so we work around this by selecting a CPU type where the # speculation mitigation will not applied. if environment.get_value('APP_REVISION') < 20200414210423: qemu_args.extend(['-cpu', 'Opteron_G5,+invtsc']) else: qemu_args.extend(['-cpu', 'host,migratable=no,+invtsc']) qemu_args.append('-enable-kvm') else: # Can't use host CPU since we don't necessarily have KVM on the machine. # Emulate a Haswell CPU with a few feature toggles. This mirrors the most # common configuration for Fuchsia VMs when using in-tree tools. qemu_args.extend(['-cpu', 'Haswell,+smap,-check,-fsgsbase']) # Get the list of fuzzers for ClusterFuzz to choose from. host = Host.from_dir( os.path.join(qemu_vars['fuchsia_resources_dir'], 'build', 'out', 'default')) Device(host, 'localhost', str(port)) Fuzzer.filter(host.fuzzers, '') # Fuzzing jobs that SSH into the QEMU VM need access to this env var. environment.set_value('FUCHSIA_PKEY_PATH', qemu_vars['pkey_path']) logs.log('Ready to run QEMU. Command: ' + qemu_vars['qemu_path'] + ' ' + ' '.join(shlex.quote(arg) for arg in qemu_args)) self.process_runner = new_process.ProcessRunner( qemu_vars['qemu_path'], qemu_args)
def create(self): """Configures a QEMU process which can subsequently be `run`. Assumes that initial_qemu_setup was already called exactly once. """ qemu_vars = _fetch_qemu_vars() # Get a free port for the VM, so we can SSH in later. tcp = socket.socket(socket.AF_INET, socket.SOCK_STREAM) tcp.bind(("localhost", 0)) _, port = tcp.getsockname() tcp.close() # Fuzzing jobs that SSH into the QEMU VM need access to this env var. environment.set_value("FUCHSIA_PORTNUM", port) environment.set_value("FUCHSIA_RESOURCES_DIR", qemu_vars["fuchsia_resources_dir"]) # yapf: disable qemu_args = [ '-m', '3072', '-nographic', '-kernel', qemu_vars['kernel_path'], '-initrd', qemu_vars['initrd_path'], '-smp', '4', '-drive', ('file=' + qemu_vars['drive_path'] + ',format=raw,if=none,' 'id=blobstore'), '-device', 'virtio-blk-pci,drive=blobstore', '-monitor', 'none', '-append', 'kernel.serial=legacy TERM=dumb', '-machine', 'q35', '-display', 'none', '-netdev', ('user,id=net0,net=192.168.3.0/24,dhcpstart=192.168.3.9,' 'host=192.168.3.2,hostfwd=tcp::') + str(port) + '-:22', '-device', 'e1000,netdev=net0,mac=52:54:00:63:5e:7b', '-L', qemu_vars['sharefiles_path'] ] # yapf: enable # Detecing KVM is tricky, so use an environment variable to determine # whether to turn it on or not. if environment.get_value("FUCHSIA_USE_KVM"): qemu_args.extend(["-cpu", "host,migratable=no"]) qemu_args.append("-enable-kvm") else: # Can't use host CPU since we don't necessarily have KVM on the machine. # Emulate a Haswell CPU with a few feature toggles. This mirrors the most # common configuration for Fuchsia VMs when using in-tree tools. qemu_args.extend(["-cpu", "Haswell,+smap,-check,-fsgsbase"]) # Get the list of fuzzers for ClusterFuzz to choose from. host = Host.from_dir( os.path.join(qemu_vars["fuchsia_resources_dir"], "build", "out", "default")) Device(host, "localhost", str(port)) Fuzzer.filter(host.fuzzers, "") # Fuzzing jobs that SSH into the QEMU VM need access to this env var. environment.set_value("FUCHSIA_PKEY_PATH", qemu_vars["pkey_path"]) logs.log("Ready to run QEMU. Command: " + qemu_vars["qemu_path"] + " " + str(qemu_args)) self.process_runner = new_process.ProcessRunner( qemu_vars["qemu_path"], qemu_args)