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