1 // Copyright 2018 The Chromium OS Authors. All rights reserved. 2 // Use of this source code is governed by a BSD-style license that can be 3 // found in the LICENSE file. 4 5 #![no_main] 6 7 use block_util::{async_io::DiskFile, raw_sync::RawFileDiskSync}; 8 use libfuzzer_sys::fuzz_target; 9 use seccompiler::SeccompAction; 10 use std::ffi; 11 use std::fs::File; 12 use std::io::{self, Cursor, Read, Seek, SeekFrom}; 13 use std::mem::size_of; 14 use std::os::unix::io::{AsRawFd, FromRawFd, RawFd}; 15 use std::path::PathBuf; 16 use std::sync::Arc; 17 use virtio_devices::{Block, VirtioDevice, VirtioInterrupt, VirtioInterruptType}; 18 use virtio_queue::{Queue, QueueState}; 19 use vm_memory::{bitmap::AtomicBitmap, Bytes, GuestAddress, GuestMemoryAtomic}; 20 use vmm_sys_util::eventfd::{EventFd, EFD_NONBLOCK}; 21 22 type GuestMemoryMmap = vm_memory::GuestMemoryMmap<AtomicBitmap>; 23 24 const MEM_SIZE: u64 = 256 * 1024 * 1024; 25 const DESC_SIZE: u64 = 16; // Bytes in one virtio descriptor. 26 const QUEUE_SIZE: u16 = 16; // Max entries in the queue. 27 const CMD_SIZE: usize = 16; // Bytes in the command. 28 29 fuzz_target!(|bytes| { 30 let size_u64 = size_of::<u64>(); 31 let mem = GuestMemoryMmap::from_ranges(&[(GuestAddress(0), MEM_SIZE as usize)]).unwrap(); 32 33 // The fuzz data is interpreted as: 34 // starting index 8 bytes 35 // command location 8 bytes 36 // command 16 bytes 37 // descriptors circular buffer 16 bytes * 3 38 if bytes.len() < 4 * size_u64 { 39 // Need an index to start. 40 return; 41 } 42 43 let mut data_image = Cursor::new(bytes); 44 45 let first_index = read_u64(&mut data_image); 46 if first_index > MEM_SIZE / DESC_SIZE { 47 return; 48 } 49 let first_offset = first_index * DESC_SIZE; 50 if first_offset as usize + size_u64 > bytes.len() { 51 return; 52 } 53 54 let command_addr = read_u64(&mut data_image); 55 if command_addr > MEM_SIZE - CMD_SIZE as u64 { 56 return; 57 } 58 if mem 59 .write_slice( 60 &bytes[2 * size_u64..(2 * size_u64) + CMD_SIZE], 61 GuestAddress(command_addr as u64), 62 ) 63 .is_err() 64 { 65 return; 66 } 67 68 data_image.seek(SeekFrom::Start(first_offset)).unwrap(); 69 let desc_table = read_u64(&mut data_image); 70 71 if mem 72 .write_slice(&bytes[32..], GuestAddress(desc_table as u64)) 73 .is_err() 74 { 75 return; 76 } 77 78 let guest_memory = GuestMemoryAtomic::new(mem); 79 80 let mut q = Queue::< 81 GuestMemoryAtomic<GuestMemoryMmap>, 82 QueueState, 83 >::new(guest_memory.clone(), QUEUE_SIZE); 84 q.state.ready = true; 85 q.state.size = QUEUE_SIZE / 2; 86 87 let queue_evts: Vec<EventFd> = vec![EventFd::new(0).unwrap()]; 88 let queue_fd = queue_evts[0].as_raw_fd(); 89 let queue_evt = unsafe { EventFd::from_raw_fd(libc::dup(queue_fd)) }; 90 91 let shm = memfd_create(&ffi::CString::new("fuzz").unwrap(), 0).unwrap(); 92 let disk_file: File = unsafe { File::from_raw_fd(shm) }; 93 let qcow_disk = Box::new(RawFileDiskSync::new(disk_file)) as Box<dyn DiskFile>; 94 95 let mut block = Block::new( 96 "tmp".to_owned(), 97 qcow_disk, 98 PathBuf::from(""), 99 false, 100 false, 101 2, 102 256, 103 SeccompAction::Allow, 104 None, 105 EventFd::new(EFD_NONBLOCK).unwrap(), 106 ) 107 .unwrap(); 108 109 block 110 .activate( 111 guest_memory, 112 Arc::new(NoopVirtioInterrupt {}), 113 vec![q], 114 queue_evts, 115 ) 116 .ok(); 117 118 queue_evt.write(77).unwrap(); // Rings the doorbell, any byte will do. 119 }); 120 121 fn read_u64<T: Read>(readable: &mut T) -> u64 { 122 let mut buf = [0u8; size_of::<u64>()]; 123 readable.read_exact(&mut buf[..]).unwrap(); 124 u64::from_le_bytes(buf) 125 } 126 127 fn memfd_create(name: &ffi::CStr, flags: u32) -> Result<RawFd, io::Error> { 128 let res = unsafe { libc::syscall(libc::SYS_memfd_create, name.as_ptr(), flags) }; 129 130 if res < 0 { 131 Err(io::Error::last_os_error()) 132 } else { 133 Ok(res as RawFd) 134 } 135 } 136 137 pub struct NoopVirtioInterrupt {} 138 139 impl VirtioInterrupt for NoopVirtioInterrupt { 140 fn trigger( 141 &self, 142 _int_type: VirtioInterruptType, 143 ) -> std::result::Result<(), std::io::Error> { 144 Ok(()) 145 } 146 } 147