xref: /cloud-hypervisor/fuzz/fuzz_targets/block.rs (revision f67b3f79ea19c9a66e04074cbbf5d292f6529e43)
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