1 // Copyright 2019 The ChromiumOS Authors 2 // Use of this source code is governed by a BSD-style license that can be 3 // found in the LICENSE file. 4 5 #![cfg(not(test))] 6 #![no_main] 7 8 use std::io::Read; 9 use std::io::Write; 10 use std::mem::size_of; 11 12 use base::Event; 13 use crosvm_fuzz::fuzz_target; 14 use crosvm_fuzz::rand::FuzzRng; 15 use devices::virtio::Interrupt; 16 use devices::virtio::QueueConfig; 17 use devices::IrqLevelEvent; 18 use rand::Rng; 19 use rand::RngCore; 20 use vm_memory::GuestAddress; 21 use vm_memory::GuestMemory; 22 23 const MAX_QUEUE_SIZE: u16 = 256; 24 const MEM_SIZE: u64 = 1024 * 1024; 25 26 thread_local! { 27 static GUEST_MEM: GuestMemory = GuestMemory::new(&[(GuestAddress(0), MEM_SIZE)]).unwrap(); 28 } 29 30 // These are taken from the virtio spec and can be used as a reference for the size calculations in 31 // the fuzzer. 32 #[repr(C, packed)] 33 struct virtq_desc { 34 addr: u64, 35 len: u32, 36 flags: u16, 37 next: u16, 38 } 39 40 #[repr(C, packed)] 41 struct virtq_avail { 42 flags: u16, 43 idx: u16, 44 ring: [u16; MAX_QUEUE_SIZE as usize], 45 used_event: u16, 46 } 47 48 #[repr(C, packed)] 49 struct virtq_used_elem { 50 id: u32, 51 len: u32, 52 } 53 54 #[repr(C, packed)] 55 struct virtq_used { 56 flags: u16, 57 idx: u16, 58 ring: [virtq_used_elem; MAX_QUEUE_SIZE as usize], 59 avail_event: u16, 60 } 61 62 fuzz_target!(|data: &[u8]| { 63 let interrupt = Interrupt::new( 64 IrqLevelEvent::new().unwrap(), 65 None, // msix_config 66 0xFFFF, // VIRTIO_MSI_NO_VECTOR 67 #[cfg(target_arch = "x86_64")] 68 None, 69 ); 70 71 let mut q = QueueConfig::new(MAX_QUEUE_SIZE, 0); 72 let mut rng = FuzzRng::new(data); 73 q.set_size(rng.gen()); 74 75 // For each of {desc_table,avail_ring,used_ring} generate a random address that includes enough 76 // space to hold the relevant struct with the largest possible queue size. 77 let max_table_size = MAX_QUEUE_SIZE as u64 * size_of::<virtq_desc>() as u64; 78 q.set_desc_table(GuestAddress(rng.gen_range(0..MEM_SIZE - max_table_size))); 79 q.set_avail_ring(GuestAddress( 80 rng.gen_range(0..MEM_SIZE - size_of::<virtq_avail>() as u64), 81 )); 82 q.set_used_ring(GuestAddress( 83 rng.gen_range(0..MEM_SIZE - size_of::<virtq_used>() as u64), 84 )); 85 q.set_ready(true); 86 87 GUEST_MEM.with(|mem| { 88 let mut q = if let Ok(q) = q.activate(mem, Event::new().unwrap(), interrupt) { 89 q 90 } else { 91 return; 92 }; 93 94 // First zero out all of the memory. 95 let vs = mem 96 .get_slice_at_addr(GuestAddress(0), MEM_SIZE as usize) 97 .unwrap(); 98 vs.write_bytes(0); 99 100 // Fill in the descriptor table. 101 let queue_size = q.size() as usize; 102 let mut buf = vec![0u8; queue_size * size_of::<virtq_desc>()]; 103 104 rng.fill_bytes(&mut buf[..]); 105 mem.write_all_at_addr(&buf[..], q.desc_table()).unwrap(); 106 107 // Fill in the available ring. See the definition of virtq_avail above for the source of 108 // these numbers. 109 let avail_size = 4 + (queue_size * 2) + 2; 110 buf.resize(avail_size, 0); 111 rng.fill_bytes(&mut buf[..]); 112 mem.write_all_at_addr(&buf[..], q.avail_ring()).unwrap(); 113 114 // Fill in the used ring. See the definition of virtq_used above for the source of 115 // these numbers. 116 let used_size = 4 + (queue_size * size_of::<virtq_used_elem>()) + 2; 117 buf.resize(used_size, 0); 118 rng.fill_bytes(&mut buf[..]); 119 mem.write_all_at_addr(&buf[..], q.used_ring()).unwrap(); 120 121 while let Some(mut avail_desc) = q.pop() { 122 // Read the entire readable portion of the buffer. 123 let mut read_buf = vec![0u8; avail_desc.reader.available_bytes()]; 124 avail_desc.reader.read_exact(&mut read_buf).unwrap(); 125 126 // Write the entire writable portion of the buffer. 127 let write_buf = vec![0u8; avail_desc.writer.available_bytes()]; 128 avail_desc.writer.write_all(&write_buf).unwrap(); 129 130 let bytes_written = avail_desc.writer.bytes_written() as u32; 131 q.add_used(avail_desc, bytes_written); 132 } 133 }); 134 }); 135