From 4d878418e5790b40635879b79cb04dfd45cee6a7 Mon Sep 17 00:00:00 2001 From: Rob Bradford Date: Thu, 16 Jul 2020 15:44:36 +0100 Subject: [PATCH] fuzz: Add fuzzing infrastructure and QCOW fuzzer Add the basic infrastructure for fuzzing along with a qcow fuzzer ported from crosvm and adapted to our code. Signed-off-by: Rob Bradford --- docs/fuzzing.md | 35 +++++++++++++++++++++++++++ fuzz/.gitignore | 4 +++ fuzz/Cargo.toml | 28 +++++++++++++++++++++ fuzz/fuzz_targets/qcow.rs | 51 +++++++++++++++++++++++++++++++++++++++ 4 files changed, 118 insertions(+) create mode 100644 docs/fuzzing.md create mode 100644 fuzz/.gitignore create mode 100644 fuzz/Cargo.toml create mode 100644 fuzz/fuzz_targets/qcow.rs diff --git a/docs/fuzzing.md b/docs/fuzzing.md new file mode 100644 index 000000000..0236175f1 --- /dev/null +++ b/docs/fuzzing.md @@ -0,0 +1,35 @@ +# Fuzzing in Cloud Hypervisor + +Cloud Hypervisor uses [cargo-fuzz](https://github.com/rust-fuzz/cargo-fuzz) for fuzzing individual components. + +The fuzzers are are in the `fuzz/fuzz_targets` directory + +## Preparation + +Switch to nightly: + +```` +rustup override set nightly +```` + +Install `cargo fuzz`: + +``` +cargo install cargo-fuzz +``` + +## Running the fuzzers + +e.g. To run the `qcow` fuzzer using all available CPUs: + +``` +cargo fuzz run qcow -j `nproc` +``` + +## Adding a new fuzzer + +``` +cargo fuzz add +``` + +Inspiration for fuzzers can be found in [crosvm](https://chromium.googlesource.com/chromiumos/platform/crosvm/+/refs/heads/master/fuzz/) \ No newline at end of file diff --git a/fuzz/.gitignore b/fuzz/.gitignore new file mode 100644 index 000000000..572e03bdf --- /dev/null +++ b/fuzz/.gitignore @@ -0,0 +1,4 @@ + +target +corpus +artifacts diff --git a/fuzz/Cargo.toml b/fuzz/Cargo.toml new file mode 100644 index 000000000..f3e9acc2f --- /dev/null +++ b/fuzz/Cargo.toml @@ -0,0 +1,28 @@ + +[package] +name = "cloud-hypervisor-fuzz" +version = "0.0.0" +authors = ["Automatically generated"] +publish = false +edition = "2018" + +[package.metadata] +cargo-fuzz = true + +[dependencies] +libc = "0.2.72" +libfuzzer-sys = "0.3" +qcow = { path = "../qcow" } + +[dependencies.cloud-hypervisor] +path = ".." + +# Prevent this from interfering with workspaces +[workspace] +members = ["."] + +[[bin]] +name = "qcow" +path = "fuzz_targets/qcow.rs" +test = false +doc = false diff --git a/fuzz/fuzz_targets/qcow.rs b/fuzz/fuzz_targets/qcow.rs new file mode 100644 index 000000000..ec432cb21 --- /dev/null +++ b/fuzz/fuzz_targets/qcow.rs @@ -0,0 +1,51 @@ +// Copyright 2018 The Chromium OS Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE-BSD-3-Clause file. + +#![no_main] +use libfuzzer_sys::fuzz_target; + +use libc; +use qcow::{QcowFile, RawFile}; +use std::ffi; +use std::fs::File; +use std::io::{self, Cursor, Read, Seek, SeekFrom, Write}; +use std::mem::size_of; +use std::os::unix::io::{FromRawFd, RawFd}; + +// Take the first 64 bits of data as an address and the next 64 bits as data to +// store there. The rest of the data is used as a qcow image. +fuzz_target!(|bytes| { + if bytes.len() < 16 { + // Need an address and data, each are 8 bytes. + return; + } + let mut disk_image = Cursor::new(bytes); + let addr = read_u64(&mut disk_image); + let value = read_u64(&mut disk_image); + let shm = memfd_create(&ffi::CString::new("fuzz").unwrap(), 0).unwrap(); + let mut disk_file: File = unsafe { File::from_raw_fd(shm) }; + disk_file.write_all(&bytes[16..]).unwrap(); + disk_file.seek(SeekFrom::Start(0)).unwrap(); + if let Ok(mut qcow) = QcowFile::from(RawFile::new(disk_file, false)) { + if qcow.seek(SeekFrom::Start(addr)).is_ok() { + let _ = qcow.write_all(&value.to_le_bytes()); + } + } +}); + +fn read_u64(readable: &mut T) -> u64 { + let mut buf = [0u8; size_of::()]; + readable.read_exact(&mut buf[..]).unwrap(); + u64::from_le_bytes(buf) +} + +fn memfd_create(name: &ffi::CStr, flags: u32) -> Result { + let res = unsafe { libc::syscall(libc::SYS_memfd_create, name.as_ptr(), flags) }; + + if res < 0 { + Err(io::Error::last_os_error()) + } else { + Ok(res as RawFd) + } +}