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