/src/cloud-hypervisor/fuzz/fuzz_targets/pmem.rs
Line | Count | Source |
1 | | // Copyright © 2022 Intel Corporation |
2 | | // |
3 | | // SPDX-License-Identifier: Apache-2.0 |
4 | | |
5 | | #![no_main] |
6 | | |
7 | | use std::fs::File; |
8 | | use std::os::unix::io::{AsRawFd, FromRawFd, RawFd}; |
9 | | use std::sync::Arc; |
10 | | use std::{ffi, io}; |
11 | | |
12 | | use libc::{MAP_NORESERVE, MAP_PRIVATE, PROT_READ, PROT_WRITE}; |
13 | | use libfuzzer_sys::{fuzz_target, Corpus}; |
14 | | use seccompiler::SeccompAction; |
15 | | use virtio_devices::{Pmem, VirtioDevice, VirtioInterrupt, VirtioInterruptType}; |
16 | | use virtio_queue::{Queue, QueueT}; |
17 | | use vm_device::UserspaceMapping; |
18 | | use vm_memory::bitmap::AtomicBitmap; |
19 | | use vm_memory::guest_memory::FileOffset; |
20 | | use vm_memory::{Bytes, GuestAddress, GuestMemoryAtomic, MmapRegion}; |
21 | | use vmm_sys_util::eventfd::{EventFd, EFD_NONBLOCK}; |
22 | | |
23 | | type GuestMemoryMmap = vm_memory::GuestMemoryMmap<AtomicBitmap>; |
24 | | |
25 | | const QUEUE_DATA_SIZE: usize = 4; |
26 | | const MEM_SIZE: usize = 256 * 1024 * 1024; |
27 | | const PMEM_FILE_SIZE: usize = 128 * 1024 * 1024; |
28 | | // Max entries in the queue. |
29 | | const QUEUE_SIZE: u16 = 256; |
30 | | // Guest physical address for descriptor table. |
31 | | const DESC_TABLE_ADDR: u64 = 0; |
32 | | const DESC_TABLE_SIZE: u64 = 16_u64 * QUEUE_SIZE as u64; |
33 | | // Guest physical address for available ring |
34 | | const AVAIL_RING_ADDR: u64 = DESC_TABLE_ADDR + DESC_TABLE_SIZE; |
35 | | const AVAIL_RING_SIZE: u64 = 6_u64 + 2 * QUEUE_SIZE as u64; |
36 | | // Guest physical address for used ring (requires to 4-bytes aligned) |
37 | | const USED_RING_ADDR: u64 = (AVAIL_RING_ADDR + AVAIL_RING_SIZE + 3) & !3_u64; |
38 | | |
39 | | fuzz_target!(|bytes: &[u8]| -> Corpus { |
40 | | if bytes.len() < QUEUE_DATA_SIZE || bytes.len() > (QUEUE_DATA_SIZE + MEM_SIZE) { |
41 | | return Corpus::Reject; |
42 | | } |
43 | | |
44 | | let mut pmem = create_dummy_pmem(); |
45 | | let queue_data = &bytes[..QUEUE_DATA_SIZE]; |
46 | | let mem_bytes = &bytes[QUEUE_DATA_SIZE..]; |
47 | | |
48 | | // Setup the virt queue with the input bytes |
49 | | let q = setup_virt_queue(queue_data.try_into().unwrap()); |
50 | | |
51 | | // Setup the guest memory with the input bytes |
52 | | let mem = GuestMemoryMmap::from_ranges(&[(GuestAddress(0), MEM_SIZE)]).unwrap(); |
53 | | if mem.write_slice(mem_bytes, GuestAddress(0 as u64)).is_err() { |
54 | | return Corpus::Reject; |
55 | | } |
56 | | let guest_memory = GuestMemoryAtomic::new(mem); |
57 | | |
58 | | let evt = EventFd::new(0).unwrap(); |
59 | | let queue_evt = unsafe { EventFd::from_raw_fd(libc::dup(evt.as_raw_fd())) }; |
60 | | |
61 | | // Kick the 'queue' event before activate the pmem device |
62 | | queue_evt.write(1).unwrap(); |
63 | | |
64 | | pmem.activate( |
65 | | guest_memory, |
66 | | Arc::new(NoopVirtioInterrupt {}), |
67 | | vec![(0, q, evt)], |
68 | | ) |
69 | | .ok(); |
70 | | |
71 | | // Wait for the events to finish and pmem device worker thread to return |
72 | | pmem.wait_for_epoll_threads(); |
73 | | |
74 | | Corpus::Keep |
75 | | }); |
76 | | |
77 | 770 | fn memfd_create_with_size(name: &ffi::CStr, flags: u32, size: usize) -> Result<RawFd, io::Error> { |
78 | 770 | let fd = unsafe { libc::syscall(libc::SYS_memfd_create, name.as_ptr(), flags) }; |
79 | 770 | if fd < 0 { |
80 | 0 | return Err(io::Error::last_os_error()); |
81 | 770 | } |
82 | | |
83 | 770 | let res = unsafe { libc::syscall(libc::SYS_ftruncate, fd, size) }; |
84 | 770 | if res < 0 { |
85 | 0 | return Err(io::Error::last_os_error()); |
86 | 770 | } |
87 | | |
88 | 770 | Ok(fd as RawFd) |
89 | 770 | } |
90 | | |
91 | | pub struct NoopVirtioInterrupt {} |
92 | | |
93 | | impl VirtioInterrupt for NoopVirtioInterrupt { |
94 | 556 | fn trigger(&self, _int_type: VirtioInterruptType) -> std::result::Result<(), std::io::Error> { |
95 | 556 | Ok(()) |
96 | 556 | } |
97 | | } |
98 | | |
99 | | // Create a dummy virtio-pmem device for fuzzing purpose only |
100 | 770 | fn create_dummy_pmem() -> Pmem { |
101 | 770 | let shm = |
102 | 770 | memfd_create_with_size(&ffi::CString::new("fuzz").unwrap(), 0, PMEM_FILE_SIZE).unwrap(); |
103 | 770 | let file: File = unsafe { File::from_raw_fd(shm) }; |
104 | | |
105 | | // The fuzzer is focusing on the virtio-pmem code that processes guest inputs (e.g. virt queues), |
106 | | // so dummy mappings (both mmap and user mapping) does the job and is faster (using smaller amount of memory). |
107 | 770 | let dummy_mapping_size = 1; |
108 | 770 | let cloned_file = file.try_clone().unwrap(); |
109 | 770 | let dummy_mmap_region = MmapRegion::build( |
110 | 770 | Some(FileOffset::new(cloned_file, 0)), |
111 | 770 | dummy_mapping_size, |
112 | 770 | PROT_READ | PROT_WRITE, |
113 | 770 | MAP_NORESERVE | MAP_PRIVATE, |
114 | | ) |
115 | 770 | .unwrap(); |
116 | 770 | let guest_addr = GuestAddress(0); |
117 | 770 | let dummy_user_mapping = UserspaceMapping { |
118 | 770 | mem_slot: 0, |
119 | 770 | addr: guest_addr, |
120 | 770 | mapping: Arc::new(dummy_mmap_region), |
121 | 770 | mergeable: false, |
122 | 770 | }; |
123 | | |
124 | 770 | Pmem::new( |
125 | 770 | "tmp".to_owned(), |
126 | 770 | file, |
127 | 770 | guest_addr, |
128 | 770 | dummy_user_mapping, |
129 | | false, |
130 | 770 | SeccompAction::Allow, |
131 | 770 | EventFd::new(EFD_NONBLOCK).unwrap(), |
132 | 770 | None, |
133 | | ) |
134 | 770 | .unwrap() |
135 | 770 | } |
136 | | |
137 | 770 | fn setup_virt_queue(bytes: &[u8; QUEUE_DATA_SIZE]) -> Queue { |
138 | 770 | let mut q = Queue::new(QUEUE_SIZE).unwrap(); |
139 | 770 | q.set_next_avail(bytes[0] as u16); // 'u8' is enough given the 'QUEUE_SIZE' is small |
140 | 770 | q.set_next_used(bytes[1] as u16); |
141 | 770 | q.set_event_idx(bytes[2] % 2 != 0); |
142 | 770 | q.set_size(bytes[3] as u16 % QUEUE_SIZE); |
143 | | |
144 | 770 | q.try_set_desc_table_address(GuestAddress(DESC_TABLE_ADDR)) |
145 | 770 | .unwrap(); |
146 | 770 | q.try_set_avail_ring_address(GuestAddress(AVAIL_RING_ADDR)) |
147 | 770 | .unwrap(); |
148 | 770 | q.try_set_used_ring_address(GuestAddress(USED_RING_ADDR)) |
149 | 770 | .unwrap(); |
150 | 770 | q.set_ready(true); |
151 | | |
152 | 770 | q |
153 | 770 | } |