Coverage Report

Created: 2026-01-08 06:20

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/cloud-hypervisor/fuzz/fuzz_targets/balloon.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::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::{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: &[u8]| -> Corpus {
41
    if bytes.len() < QUEUE_DATA_SIZE * QUEUE_NUM
42
        || bytes.len() > (QUEUE_DATA_SIZE * QUEUE_NUM + MEM_SIZE)
43
    {
44
        return Corpus::Reject;
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 Corpus::Reject;
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
    Corpus::Keep
113
});
114
115
pub struct NoopVirtioInterrupt {}
116
117
impl VirtioInterrupt for NoopVirtioInterrupt {
118
59
    fn trigger(&self, _int_type: VirtioInterruptType) -> std::result::Result<(), std::io::Error> {
119
59
        Ok(())
120
59
    }
121
}
122
123
macro_rules! align {
124
    ($n:expr, $align:expr) => {{
125
        $n.div_ceil($align) * $align
126
    }};
127
}
128
129
544
fn setup_virt_queues(bytes: &[&[u8; QUEUE_DATA_SIZE]], base_addr: u64) -> Vec<Queue> {
130
544
    let mut queues = Vec::new();
131
544
    let mut base_addr = base_addr;
132
1.63k
    for b in bytes {
133
1.63k
        let mut q = Queue::new(QUEUE_SIZE).unwrap();
134
1.63k
135
1.63k
        let desc_table_addr = align!(base_addr, DESC_TABLE_ALIGN_SIZE);
136
1.63k
        let avail_ring_addr = align!(desc_table_addr + DESC_TABLE_SIZE, AVAIL_RING_ALIGN_SIZE);
137
1.63k
        let used_ring_addr = align!(avail_ring_addr + AVAIL_RING_SIZE, USED_RING_ALIGN_SIZE);
138
1.63k
        q.try_set_desc_table_address(GuestAddress(desc_table_addr))
139
1.63k
            .unwrap();
140
1.63k
        q.try_set_avail_ring_address(GuestAddress(avail_ring_addr))
141
1.63k
            .unwrap();
142
1.63k
        q.try_set_used_ring_address(GuestAddress(used_ring_addr))
143
1.63k
            .unwrap();
144
1.63k
145
1.63k
        q.set_next_avail(b[0] as u16); // 'u8' is enough given the 'QUEUE_SIZE' is small
146
1.63k
        q.set_next_used(b[1] as u16);
147
1.63k
        q.set_event_idx(b[2] % 2 != 0);
148
1.63k
        q.set_size(b[3] as u16 % QUEUE_SIZE);
149
1.63k
150
1.63k
        q.set_ready(true);
151
1.63k
        queues.push(q);
152
1.63k
153
1.63k
        base_addr = used_ring_addr + USED_RING_SIZE;
154
1.63k
    }
155
156
544
    queues
157
544
}