xref: /cloud-hypervisor/fuzz/fuzz_targets/balloon.rs (revision 80b2c98a68d4c68f372f849e8d26f7cae5867000)
1 // Copyright © 2022 Intel Corporation
2 //
3 // SPDX-License-Identifier: Apache-2.0
4 
5 #![no_main]
6 
7 use std::os::unix::io::{AsRawFd, FromRawFd};
8 use std::sync::Arc;
9 
10 use libfuzzer_sys::fuzz_target;
11 use seccompiler::SeccompAction;
12 use virtio_devices::{VirtioDevice, VirtioInterrupt, VirtioInterruptType};
13 use virtio_queue::{Queue, QueueT};
14 use vm_memory::bitmap::AtomicBitmap;
15 use vm_memory::{Bytes, GuestAddress, GuestMemoryAtomic};
16 use vmm_sys_util::eventfd::{EventFd, EFD_NONBLOCK};
17 
18 type GuestMemoryMmap = vm_memory::GuestMemoryMmap<AtomicBitmap>;
19 
20 const QUEUE_DATA_SIZE: usize = 4;
21 const MEM_SIZE: usize = 512 * 1024;
22 const BALLOON_SIZE: u64 = 512 * 1024;
23 // Number of queues
24 const QUEUE_NUM: usize = 3;
25 // Max entries in the queue.
26 const QUEUE_SIZE: u16 = 64;
27 // Descriptor table alignment
28 const DESC_TABLE_ALIGN_SIZE: u64 = 16;
29 // Available ring alignment
30 const AVAIL_RING_ALIGN_SIZE: u64 = 2;
31 // Used ring alignment
32 const USED_RING_ALIGN_SIZE: u64 = 4;
33 // Descriptor table size
34 const DESC_TABLE_SIZE: u64 = 16_u64 * QUEUE_SIZE as u64;
35 // Available ring size
36 const AVAIL_RING_SIZE: u64 = 6_u64 + 2 * QUEUE_SIZE as u64;
37 // Used ring size
38 const USED_RING_SIZE: u64 = 6_u64 + 8 * QUEUE_SIZE as u64;
39 
40 fuzz_target!(|bytes| {
41     if bytes.len() < QUEUE_DATA_SIZE * QUEUE_NUM
42         || bytes.len() > (QUEUE_DATA_SIZE * QUEUE_NUM + MEM_SIZE)
43     {
44         return;
45     }
46 
47     let mut balloon = virtio_devices::Balloon::new(
48         "fuzzer_balloon".to_owned(),
49         BALLOON_SIZE,
50         true,
51         true,
52         SeccompAction::Allow,
53         EventFd::new(EFD_NONBLOCK).unwrap(),
54         None,
55     )
56     .unwrap();
57 
58     let queue_data = &bytes[..QUEUE_DATA_SIZE * QUEUE_NUM];
59     let mem_bytes = &bytes[QUEUE_DATA_SIZE * QUEUE_NUM..];
60 
61     // Setup the guest memory with the input bytes
62     let mem = GuestMemoryMmap::from_ranges(&[(GuestAddress(0), MEM_SIZE)]).unwrap();
63     if mem.write_slice(mem_bytes, GuestAddress(0 as u64)).is_err() {
64         return;
65     }
66     let guest_memory = GuestMemoryAtomic::new(mem);
67 
68     // Setup the virt queues with the input bytes
69     let mut queues = setup_virt_queues(
70         &[
71             &queue_data[..QUEUE_DATA_SIZE].try_into().unwrap(),
72             &queue_data[QUEUE_DATA_SIZE..QUEUE_DATA_SIZE * 2]
73                 .try_into()
74                 .unwrap(),
75             &queue_data[QUEUE_DATA_SIZE * 2..QUEUE_DATA_SIZE * 3]
76                 .try_into()
77                 .unwrap(),
78         ],
79         0,
80     );
81 
82     let inflate_q = queues.remove(0);
83     let inflate_evt = EventFd::new(0).unwrap();
84     let inflate_queue_evt = unsafe { EventFd::from_raw_fd(libc::dup(inflate_evt.as_raw_fd())) };
85     let deflate_q = queues.remove(0);
86     let deflate_evt = EventFd::new(0).unwrap();
87     let deflate_queue_evt = unsafe { EventFd::from_raw_fd(libc::dup(deflate_evt.as_raw_fd())) };
88     let reporting_q = queues.remove(0);
89     let reporting_evt = EventFd::new(0).unwrap();
90     let reporting_queue_evt = unsafe { EventFd::from_raw_fd(libc::dup(reporting_evt.as_raw_fd())) };
91 
92     // Kick the 'queue' events before activate the balloon device
93     inflate_queue_evt.write(1).unwrap();
94     deflate_queue_evt.write(1).unwrap();
95     reporting_queue_evt.write(1).unwrap();
96 
97     balloon
98         .activate(
99             guest_memory,
100             Arc::new(NoopVirtioInterrupt {}),
101             vec![
102                 (0, inflate_q, inflate_evt),
103                 (1, deflate_q, deflate_evt),
104                 (2, reporting_q, reporting_evt),
105             ],
106         )
107         .ok();
108 
109     // Wait for the events to finish and balloon device worker thread to return
110     balloon.wait_for_epoll_threads();
111 });
112 
113 pub struct NoopVirtioInterrupt {}
114 
115 impl VirtioInterrupt for NoopVirtioInterrupt {
116     fn trigger(&self, _int_type: VirtioInterruptType) -> std::result::Result<(), std::io::Error> {
117         Ok(())
118     }
119 }
120 
121 macro_rules! align {
122     ($n:expr, $align:expr) => {{
123         $n.div_ceil($align) * $align
124     }};
125 }
126 
127 fn setup_virt_queues(bytes: &[&[u8; QUEUE_DATA_SIZE]], base_addr: u64) -> Vec<Queue> {
128     let mut queues = Vec::new();
129     let mut base_addr = base_addr;
130     for b in bytes {
131         let mut q = Queue::new(QUEUE_SIZE).unwrap();
132 
133         let desc_table_addr = align!(base_addr, DESC_TABLE_ALIGN_SIZE);
134         let avail_ring_addr = align!(desc_table_addr + DESC_TABLE_SIZE, AVAIL_RING_ALIGN_SIZE);
135         let used_ring_addr = align!(avail_ring_addr + AVAIL_RING_SIZE, USED_RING_ALIGN_SIZE);
136         q.try_set_desc_table_address(GuestAddress(desc_table_addr))
137             .unwrap();
138         q.try_set_avail_ring_address(GuestAddress(avail_ring_addr))
139             .unwrap();
140         q.try_set_used_ring_address(GuestAddress(used_ring_addr))
141             .unwrap();
142 
143         q.set_next_avail(b[0] as u16); // 'u8' is enough given the 'QUEUE_SIZE' is small
144         q.set_next_used(b[1] as u16);
145         q.set_event_idx(b[2] % 2 != 0);
146         q.set_size(b[3] as u16 % QUEUE_SIZE);
147 
148         q.set_ready(true);
149         queues.push(q);
150 
151         base_addr = used_ring_addr + USED_RING_SIZE;
152     }
153 
154     queues
155 }
156