1 // Copyright © 2022 Intel Corporation 2 // 3 // SPDX-License-Identifier: Apache-2.0 4 5 #![no_main] 6 7 use std::os::unix::io::{AsRawFd, FromRawFd}; 8 use std::sync::Arc; 9 10 use libfuzzer_sys::fuzz_target; 11 use seccompiler::SeccompAction; 12 use virtio_devices::{VirtioDevice, VirtioInterrupt, VirtioInterruptType}; 13 use virtio_queue::{Queue, QueueT}; 14 use vm_memory::bitmap::AtomicBitmap; 15 use vm_memory::{Bytes, GuestAddress, GuestMemoryAtomic}; 16 use vmm_sys_util::eventfd::{EventFd, EFD_NONBLOCK}; 17 18 type GuestMemoryMmap = vm_memory::GuestMemoryMmap<AtomicBitmap>; 19 20 const QUEUE_DATA_SIZE: usize = 4; 21 const MEM_SIZE: usize = 512 * 1024; 22 const BALLOON_SIZE: u64 = 512 * 1024; 23 // Number of queues 24 const QUEUE_NUM: usize = 3; 25 // Max entries in the queue. 26 const QUEUE_SIZE: u16 = 64; 27 // Descriptor table alignment 28 const DESC_TABLE_ALIGN_SIZE: u64 = 16; 29 // Available ring alignment 30 const AVAIL_RING_ALIGN_SIZE: u64 = 2; 31 // Used ring alignment 32 const USED_RING_ALIGN_SIZE: u64 = 4; 33 // Descriptor table size 34 const DESC_TABLE_SIZE: u64 = 16_u64 * QUEUE_SIZE as u64; 35 // Available ring size 36 const AVAIL_RING_SIZE: u64 = 6_u64 + 2 * QUEUE_SIZE as u64; 37 // Used ring size 38 const USED_RING_SIZE: u64 = 6_u64 + 8 * QUEUE_SIZE as u64; 39 40 fuzz_target!(|bytes| { 41 if bytes.len() < QUEUE_DATA_SIZE * QUEUE_NUM 42 || bytes.len() > (QUEUE_DATA_SIZE * QUEUE_NUM + MEM_SIZE) 43 { 44 return; 45 } 46 47 let mut balloon = virtio_devices::Balloon::new( 48 "fuzzer_balloon".to_owned(), 49 BALLOON_SIZE, 50 true, 51 true, 52 SeccompAction::Allow, 53 EventFd::new(EFD_NONBLOCK).unwrap(), 54 None, 55 ) 56 .unwrap(); 57 58 let queue_data = &bytes[..QUEUE_DATA_SIZE * QUEUE_NUM]; 59 let mem_bytes = &bytes[QUEUE_DATA_SIZE * QUEUE_NUM..]; 60 61 // Setup the guest memory with the input bytes 62 let mem = GuestMemoryMmap::from_ranges(&[(GuestAddress(0), MEM_SIZE)]).unwrap(); 63 if mem.write_slice(mem_bytes, GuestAddress(0 as u64)).is_err() { 64 return; 65 } 66 let guest_memory = GuestMemoryAtomic::new(mem); 67 68 // Setup the virt queues with the input bytes 69 let mut queues = setup_virt_queues( 70 &[ 71 &queue_data[..QUEUE_DATA_SIZE].try_into().unwrap(), 72 &queue_data[QUEUE_DATA_SIZE..QUEUE_DATA_SIZE * 2] 73 .try_into() 74 .unwrap(), 75 &queue_data[QUEUE_DATA_SIZE * 2..QUEUE_DATA_SIZE * 3] 76 .try_into() 77 .unwrap(), 78 ], 79 0, 80 ); 81 82 let inflate_q = queues.remove(0); 83 let inflate_evt = EventFd::new(0).unwrap(); 84 let inflate_queue_evt = unsafe { EventFd::from_raw_fd(libc::dup(inflate_evt.as_raw_fd())) }; 85 let deflate_q = queues.remove(0); 86 let deflate_evt = EventFd::new(0).unwrap(); 87 let deflate_queue_evt = unsafe { EventFd::from_raw_fd(libc::dup(deflate_evt.as_raw_fd())) }; 88 let reporting_q = queues.remove(0); 89 let reporting_evt = EventFd::new(0).unwrap(); 90 let reporting_queue_evt = unsafe { EventFd::from_raw_fd(libc::dup(reporting_evt.as_raw_fd())) }; 91 92 // Kick the 'queue' events before activate the balloon device 93 inflate_queue_evt.write(1).unwrap(); 94 deflate_queue_evt.write(1).unwrap(); 95 reporting_queue_evt.write(1).unwrap(); 96 97 balloon 98 .activate( 99 guest_memory, 100 Arc::new(NoopVirtioInterrupt {}), 101 vec![ 102 (0, inflate_q, inflate_evt), 103 (1, deflate_q, deflate_evt), 104 (2, reporting_q, reporting_evt), 105 ], 106 ) 107 .ok(); 108 109 // Wait for the events to finish and balloon device worker thread to return 110 balloon.wait_for_epoll_threads(); 111 }); 112 113 pub struct NoopVirtioInterrupt {} 114 115 impl VirtioInterrupt for NoopVirtioInterrupt { 116 fn trigger(&self, _int_type: VirtioInterruptType) -> std::result::Result<(), std::io::Error> { 117 Ok(()) 118 } 119 } 120 121 macro_rules! align { 122 ($n:expr, $align:expr) => {{ 123 $n.div_ceil($align) * $align 124 }}; 125 } 126 127 fn setup_virt_queues(bytes: &[&[u8; QUEUE_DATA_SIZE]], base_addr: u64) -> Vec<Queue> { 128 let mut queues = Vec::new(); 129 let mut base_addr = base_addr; 130 for b in bytes { 131 let mut q = Queue::new(QUEUE_SIZE).unwrap(); 132 133 let desc_table_addr = align!(base_addr, DESC_TABLE_ALIGN_SIZE); 134 let avail_ring_addr = align!(desc_table_addr + DESC_TABLE_SIZE, AVAIL_RING_ALIGN_SIZE); 135 let used_ring_addr = align!(avail_ring_addr + AVAIL_RING_SIZE, USED_RING_ALIGN_SIZE); 136 q.try_set_desc_table_address(GuestAddress(desc_table_addr)) 137 .unwrap(); 138 q.try_set_avail_ring_address(GuestAddress(avail_ring_addr)) 139 .unwrap(); 140 q.try_set_used_ring_address(GuestAddress(used_ring_addr)) 141 .unwrap(); 142 143 q.set_next_avail(b[0] as u16); // 'u8' is enough given the 'QUEUE_SIZE' is small 144 q.set_next_used(b[1] as u16); 145 q.set_event_idx(b[2] % 2 != 0); 146 q.set_size(b[3] as u16 % QUEUE_SIZE); 147 148 q.set_ready(true); 149 queues.push(q); 150 151 base_addr = used_ring_addr + USED_RING_SIZE; 152 } 153 154 queues 155 } 156