From 742d6858f72b5cd8fd9bc7d0a19e6b6877a4b0f6 Mon Sep 17 00:00:00 2001 From: Bo Chen Date: Tue, 30 Aug 2022 17:41:19 -0700 Subject: [PATCH] fuzz: block: Setup the virt queue based on the fuzzed input bytes Instead of always fuzzing virt-queues with default values (mostly 0s), the fuzzer now initializes the virt-queue based on the fuzzed input bytes, such as the tail position of the available ring, queue size selected by driver, descriptor table address, available ring address, used ring address, etc. In this way, the fuzzer can explore the virtio-block code path with various virt-queue setup. Signed-off-by: Bo Chen --- fuzz/fuzz_targets/block.rs | 58 +++++++++++++++++++++++++++----------- 1 file changed, 42 insertions(+), 16 deletions(-) diff --git a/fuzz/fuzz_targets/block.rs b/fuzz/fuzz_targets/block.rs index d3c56fe1c..375c6d40d 100644 --- a/fuzz/fuzz_targets/block.rs +++ b/fuzz/fuzz_targets/block.rs @@ -24,32 +24,22 @@ use vmm_sys_util::eventfd::{EventFd, EFD_NONBLOCK}; type GuestMemoryMmap = vm_memory::GuestMemoryMmap; -const MEM_SIZE: u64 = 256 * 1024 * 1024; +const QUEUE_DATA_SIZE: usize = 28; +const MEM_SIZE: usize = 256 * 1024 * 1024; const QUEUE_SIZE: u16 = 16; // Max entries in the queue. fuzz_target!(|bytes| { - if bytes.len() as u64 > MEM_SIZE { + if bytes.len() < QUEUE_DATA_SIZE || bytes.len() > (QUEUE_DATA_SIZE + MEM_SIZE) { return; } - let mem = GuestMemoryMmap::from_ranges(&[(GuestAddress(0), MEM_SIZE as usize)]).unwrap(); - if mem.write_slice(bytes, GuestAddress(0 as u64)).is_err() { - return; - } - - let guest_memory = GuestMemoryAtomic::new(mem); - - let mut q = Queue::new(QUEUE_SIZE).unwrap(); - q.set_ready(true); - q.set_size(QUEUE_SIZE / 2); - - let evt = EventFd::new(0).unwrap(); - let queue_evt = unsafe { EventFd::from_raw_fd(libc::dup(evt.as_raw_fd())) }; + let queue_data = &bytes[..QUEUE_DATA_SIZE]; + let mem_bytes = &bytes[QUEUE_DATA_SIZE..]; + // Create a virtio-block device backed by a synchronous raw file let shm = memfd_create(&ffi::CString::new("fuzz").unwrap(), 0).unwrap(); let disk_file: File = unsafe { File::from_raw_fd(shm) }; let qcow_disk = Box::new(RawFileDiskSync::new(disk_file)) as Box; - let mut block = Block::new( "tmp".to_owned(), qcow_disk, @@ -64,6 +54,19 @@ fuzz_target!(|bytes| { ) .unwrap(); + // Setup the virt queue with the input bytes + let q = setup_virt_queue(queue_data.try_into().unwrap()); + + // Setup the guest memory with the input bytes + let mem = GuestMemoryMmap::from_ranges(&[(GuestAddress(0), MEM_SIZE)]).unwrap(); + if mem.write_slice(mem_bytes, GuestAddress(0 as u64)).is_err() { + return; + } + let guest_memory = GuestMemoryAtomic::new(mem); + + let evt = EventFd::new(0).unwrap(); + let queue_evt = unsafe { EventFd::from_raw_fd(libc::dup(evt.as_raw_fd())) }; + // Kick the 'queue' event before activate the block device queue_evt.write(1).unwrap(); @@ -96,3 +99,26 @@ impl VirtioInterrupt for NoopVirtioInterrupt { Ok(()) } } + +fn setup_virt_queue(bytes: &[u8; QUEUE_DATA_SIZE]) -> Queue { + let mut q = Queue::new(QUEUE_SIZE).unwrap(); + q.set_next_avail(bytes[0] as u16); // 'u8' is enough given the 'QUEUE_SIZE' is small + q.set_next_used(bytes[1] as u16); + q.set_event_idx(bytes[2] % 2 != 0); + q.set_size(bytes[3] as u16 % QUEUE_SIZE); + q.set_desc_table_address( + Some(u32::from_le_bytes(bytes[4..8].try_into().unwrap())), + Some(u32::from_le_bytes(bytes[8..12].try_into().unwrap())), + ); + q.set_avail_ring_address( + Some(u32::from_le_bytes(bytes[12..16].try_into().unwrap())), + Some(u32::from_le_bytes(bytes[16..20].try_into().unwrap())), + ); + q.set_used_ring_address( + Some(u32::from_le_bytes(bytes[20..24].try_into().unwrap())), + Some(u32::from_le_bytes(bytes[24..28].try_into().unwrap())), + ); + q.set_ready(true); + + q +}