Coverage Report

Created: 2026-02-14 06:57

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/cloud-hypervisor/fuzz/fuzz_targets/vsock.rs
Line
Count
Source
1
// Copyright © 2025 Microsoft
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, Corpus};
11
use seccompiler::SeccompAction;
12
use virtio_devices::vsock::unit_tests::TestBackend;
13
use virtio_devices::{VirtioDevice, VirtioInterrupt, VirtioInterruptType};
14
use virtio_queue::{Queue, QueueT};
15
use vm_memory::bitmap::AtomicBitmap;
16
use vm_memory::{Bytes, GuestAddress, GuestMemoryAtomic};
17
use vmm_sys_util::eventfd::{EventFd, EFD_NONBLOCK};
18
use vmm_sys_util::tempdir::TempDir;
19
20
type GuestMemoryMmap = vm_memory::GuestMemoryMmap<AtomicBitmap>;
21
22
macro_rules! align {
23
    ($n:expr, $align:expr) => {{
24
        $n.div_ceil($align) * $align
25
    }};
26
}
27
28
const QUEUE_DATA_SIZE: usize = 4;
29
const MEM_SIZE: usize = 1 * 1024 * 1024;
30
31
// Max entries in the queue.
32
const QUEUE_SIZE: u16 = 256;
33
// Descriptor table alignment
34
const DESC_TABLE_ALIGN_SIZE: u64 = 16;
35
// Available ring alignment
36
const AVAIL_RING_ALIGN_SIZE: u64 = 2;
37
// Used ring alignment
38
const USED_RING_ALIGN_SIZE: u64 = 4;
39
// Descriptor table size
40
const DESC_TABLE_SIZE: u64 = 16_u64 * QUEUE_SIZE as u64;
41
// Available ring size
42
const AVAIL_RING_SIZE: u64 = 6_u64 + 2 * QUEUE_SIZE as u64;
43
// Used ring size
44
const USED_RING_SIZE: u64 = 6_u64 + 8 * QUEUE_SIZE as u64;
45
46
// Guest memory gap
47
const GUEST_MEM_GAP: u64 = 1 * 1024 * 1024;
48
// Guest physical address for descriptor table.
49
const DESC_TABLE_ADDR: u64 = align!(MEM_SIZE as u64 + GUEST_MEM_GAP, DESC_TABLE_ALIGN_SIZE);
50
// Guest physical address for available ring
51
const AVAIL_RING_ADDR: u64 = align!(DESC_TABLE_ADDR + DESC_TABLE_SIZE, AVAIL_RING_ALIGN_SIZE);
52
// Guest physical address for used ring
53
const USED_RING_ADDR: u64 = align!(AVAIL_RING_ADDR + AVAIL_RING_SIZE, USED_RING_ALIGN_SIZE);
54
// Virtio-queue size in bytes
55
const QUEUE_BYTES_SIZE: usize = (USED_RING_ADDR + USED_RING_SIZE - DESC_TABLE_ADDR) as usize;
56
57
fuzz_target!(|bytes: &[u8]| -> Corpus {
58
    if bytes.len() < (QUEUE_DATA_SIZE + QUEUE_BYTES_SIZE)
59
        || bytes.len() > (QUEUE_DATA_SIZE + QUEUE_BYTES_SIZE + MEM_SIZE)
60
    {
61
        return Corpus::Reject;
62
    }
63
64
    let queue_data = &bytes[..QUEUE_DATA_SIZE];
65
    let queue_bytes = &bytes[QUEUE_DATA_SIZE..QUEUE_DATA_SIZE + QUEUE_BYTES_SIZE];
66
    let mem_bytes = &bytes[QUEUE_DATA_SIZE + QUEUE_BYTES_SIZE..];
67
68
    let q = setup_virt_queue(queue_data.try_into().unwrap());
69
70
    let tmp_dir = TempDir::new_with_prefix("/tmp/fuzz_virtio_vsock").unwrap();
71
    let vsock_path = tmp_dir.as_path().join("vsock.sock");
72
73
    let backend = TestBackend::new();
74
75
    // Setup the guest memory with the input bytes
76
    let mem = GuestMemoryMmap::from_ranges(&[
77
        (GuestAddress(0), MEM_SIZE),
78
        (GuestAddress(DESC_TABLE_ADDR), QUEUE_BYTES_SIZE),
79
    ])
80
    .unwrap();
81
    if mem
82
        .write_slice(queue_bytes, GuestAddress(DESC_TABLE_ADDR))
83
        .is_err()
84
    {
85
        return Corpus::Reject;
86
    }
87
    if mem.write_slice(mem_bytes, GuestAddress(0 as u64)).is_err() {
88
        return Corpus::Reject;
89
    }
90
    let guest_memory = GuestMemoryAtomic::new(mem);
91
92
    let evt = EventFd::new(0).unwrap();
93
    let queue_evt = unsafe { EventFd::from_raw_fd(libc::dup(evt.as_raw_fd())) };
94
95
    // Kick the 'queue' event before activate the rng device
96
    queue_evt.write(1).unwrap();
97
98
    let mut vsock = virtio_devices::Vsock::new(
99
        "fuzzer_vsock".to_owned(),
100
        0,
101
        vsock_path,
102
        backend,
103
        false,
104
        SeccompAction::Allow,
105
        EventFd::new(EFD_NONBLOCK).unwrap(),
106
        None,
107
    )
108
    .unwrap();
109
110
    vsock
111
        .activate(
112
            guest_memory,
113
            Arc::new(NoopVirtioInterrupt {}),
114
            vec![(0, q, evt)],
115
        )
116
        .ok();
117
118
    // Wait for the events to finish and vsock device worker thread to return
119
    vsock.wait_for_epoll_threads();
120
121
    Corpus::Keep
122
});
123
124
pub struct NoopVirtioInterrupt {}
125
126
impl VirtioInterrupt for NoopVirtioInterrupt {
127
0
    fn trigger(&self, _int_type: VirtioInterruptType) -> std::result::Result<(), std::io::Error> {
128
0
        Ok(())
129
0
    }
130
}
131
132
158
fn setup_virt_queue(bytes: &[u8; QUEUE_DATA_SIZE]) -> Queue {
133
158
    let mut q = Queue::new(QUEUE_SIZE).unwrap();
134
158
    q.set_next_avail(bytes[0] as u16); // 'u8' is enough given the 'QUEUE_SIZE' is small
135
158
    q.set_next_used(bytes[1] as u16);
136
158
    q.set_event_idx(bytes[2] % 2 != 0);
137
158
    q.set_size(bytes[3] as u16 % QUEUE_SIZE);
138
139
158
    q.try_set_desc_table_address(GuestAddress(DESC_TABLE_ADDR))
140
158
        .unwrap();
141
158
    q.try_set_avail_ring_address(GuestAddress(AVAIL_RING_ADDR))
142
158
        .unwrap();
143
158
    q.try_set_used_ring_address(GuestAddress(USED_RING_ADDR))
144
158
        .unwrap();
145
158
    q.set_ready(true);
146
147
158
    q
148
158
}