diff --git a/fuzz/Cargo.lock b/fuzz/Cargo.lock index 642209cc3..524a91f9d 100644 --- a/fuzz/Cargo.lock +++ b/fuzz/Cargo.lock @@ -89,7 +89,7 @@ dependencies = [ "linux-loader", "log", "serde", - "thiserror", + "thiserror 2.0.9", "uuid", "vm-fdt", "vm-memory", @@ -126,7 +126,7 @@ dependencies = [ "remain", "serde", "smallvec", - "thiserror", + "thiserror 2.0.9", "uuid", "virtio-bindings", "virtio-queue", @@ -300,7 +300,7 @@ dependencies = [ "num_enum", "pci", "serde", - "thiserror", + "thiserror 2.0.9", "tpm", "vm-allocator", "vm-device", @@ -452,7 +452,7 @@ dependencies = [ "mshv-bindings", "serde", "serde_with", - "thiserror", + "thiserror 2.0.9", "vfio-ioctls", "vm-memory", "vmm-sys-util", @@ -544,7 +544,7 @@ checksum = "18738c5d4c7fae6727a96adb94722ef7ce82f3eafea0a11777e258a93816537e" dependencies = [ "enumflags2", "libc", - "thiserror", + "thiserror 1.0.64", ] [[package]] @@ -657,7 +657,7 @@ dependencies = [ "net_gen", "rate_limiter", "serde", - "thiserror", + "thiserror 2.0.9", "virtio-bindings", "virtio-queue", "vm-memory", @@ -721,7 +721,7 @@ dependencies = [ "libc", "log", "serde", - "thiserror", + "thiserror 2.0.9", "vfio-bindings", "vfio-ioctls", "vfio_user", @@ -752,9 +752,9 @@ dependencies = [ [[package]] name = "proc-macro2" -version = "1.0.86" +version = "1.0.92" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5e719e8df665df0d1c8fbfd238015744736151d4445ec0836b8e628aae103b77" +checksum = "37d3544b3f2748c54e147655edb5025752e2303145b5aefb3c3ea2c78b973bb0" dependencies = [ "unicode-ident", ] @@ -805,7 +805,7 @@ dependencies = [ "epoll", "libc", "log", - "thiserror", + "thiserror 2.0.9", "vmm-sys-util", ] @@ -948,9 +948,9 @@ checksum = "7da8b5736845d9f2fcb837ea5d9e2628564b3b043a70948a3f0b778838c5fb4f" [[package]] name = "syn" -version = "2.0.77" +version = "2.0.95" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9f35bcdf61fd8e7be6caf75f429fdca8beb3ed76584befb503b1569faee373ed" +checksum = "46f71c0377baf4ef1cc3e3402ded576dccc315800fbc62dfc7fe04b009773b4a" dependencies = [ "proc-macro2", "quote", @@ -963,7 +963,16 @@ version = "1.0.64" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d50af8abc119fb8bb6dbabcfa89656f46f84aa0ac7688088608076ad2b459a84" dependencies = [ - "thiserror-impl", + "thiserror-impl 1.0.64", +] + +[[package]] +name = "thiserror" +version = "2.0.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f072643fd0190df67a8bab670c20ef5d8737177d6ac6b2e9a236cb096206b2cc" +dependencies = [ + "thiserror-impl 2.0.9", ] [[package]] @@ -977,6 +986,17 @@ dependencies = [ "syn", ] +[[package]] +name = "thiserror-impl" +version = "2.0.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7b50fa271071aae2e6ee85f842e2e28ba8cd2c5fb67f11fcb1fd70b276f9e7d4" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + [[package]] name = "toml_datetime" version = "0.6.8" @@ -1003,7 +1023,7 @@ dependencies = [ "libc", "log", "net_gen", - "thiserror", + "thiserror 2.0.9", "vmm-sys-util", ] @@ -1070,7 +1090,7 @@ dependencies = [ "kvm-ioctls", "libc", "log", - "thiserror", + "thiserror 1.0.64", "vfio-bindings", "vm-memory", "vmm-sys-util", @@ -1087,7 +1107,7 @@ dependencies = [ "serde", "serde_derive", "serde_json", - "thiserror", + "thiserror 1.0.64", "vfio-bindings", "vm-memory", "vmm-sys-util", @@ -1132,7 +1152,7 @@ dependencies = [ "serde_json", "serde_with", "serial_buffer", - "thiserror", + "thiserror 2.0.9", "vhost", "virtio-bindings", "virtio-queue", @@ -1172,7 +1192,7 @@ dependencies = [ "anyhow", "hypervisor", "serde", - "thiserror", + "thiserror 2.0.9", "vfio-ioctls", "vm-memory", "vmm-sys-util", @@ -1191,7 +1211,7 @@ checksum = "f1720e7240cdc739f935456eb77f370d7e9b2a3909204da1e2b47bef1137a013" dependencies = [ "arc-swap", "libc", - "thiserror", + "thiserror 1.0.64", "winapi", ] @@ -1202,7 +1222,7 @@ dependencies = [ "anyhow", "serde", "serde_json", - "thiserror", + "thiserror 2.0.9", "vm-memory", ] @@ -1249,7 +1269,7 @@ dependencies = [ "serde_json", "serial_buffer", "signal-hook", - "thiserror", + "thiserror 2.0.9", "tracer", "uuid", "vfio-ioctls", diff --git a/fuzz/Cargo.toml b/fuzz/Cargo.toml index 09aae4c92..5c75c1837 100644 --- a/fuzz/Cargo.toml +++ b/fuzz/Cargo.toml @@ -130,6 +130,12 @@ name = "vhdx" path = "fuzz_targets/vhdx.rs" test = false +[[bin]] +doc = false +name = "vsock" +path = "fuzz_targets/vsock.rs" +test = false + [[bin]] doc = false name = "watchdog" diff --git a/fuzz/fuzz_targets/vsock.rs b/fuzz/fuzz_targets/vsock.rs new file mode 100644 index 000000000..24763de26 --- /dev/null +++ b/fuzz/fuzz_targets/vsock.rs @@ -0,0 +1,148 @@ +// Copyright © 2025 Microsoft +// +// SPDX-License-Identifier: Apache-2.0 + +#![no_main] + +use std::os::unix::io::{AsRawFd, FromRawFd}; +use std::sync::Arc; + +use libfuzzer_sys::{fuzz_target, Corpus}; +use seccompiler::SeccompAction; +use virtio_devices::vsock::tests::TestBackend; +use virtio_devices::{VirtioDevice, VirtioInterrupt, VirtioInterruptType}; +use virtio_queue::{Queue, QueueT}; +use vm_memory::bitmap::AtomicBitmap; +use vm_memory::{Bytes, GuestAddress, GuestMemoryAtomic}; +use vmm_sys_util::eventfd::{EventFd, EFD_NONBLOCK}; +use vmm_sys_util::tempdir::TempDir; + +type GuestMemoryMmap = vm_memory::GuestMemoryMmap; + +macro_rules! align { + ($n:expr, $align:expr) => {{ + $n.div_ceil($align) * $align + }}; +} + +const QUEUE_DATA_SIZE: usize = 4; +const MEM_SIZE: usize = 1 * 1024 * 1024; + +// Max entries in the queue. +const QUEUE_SIZE: u16 = 256; +// Descriptor table alignment +const DESC_TABLE_ALIGN_SIZE: u64 = 16; +// Available ring alignment +const AVAIL_RING_ALIGN_SIZE: u64 = 2; +// Used ring alignment +const USED_RING_ALIGN_SIZE: u64 = 4; +// Descriptor table size +const DESC_TABLE_SIZE: u64 = 16_u64 * QUEUE_SIZE as u64; +// Available ring size +const AVAIL_RING_SIZE: u64 = 6_u64 + 2 * QUEUE_SIZE as u64; +// Used ring size +const USED_RING_SIZE: u64 = 6_u64 + 8 * QUEUE_SIZE as u64; + +// Guest memory gap +const GUEST_MEM_GAP: u64 = 1 * 1024 * 1024; +// Guest physical address for descriptor table. +const DESC_TABLE_ADDR: u64 = align!(MEM_SIZE as u64 + GUEST_MEM_GAP, DESC_TABLE_ALIGN_SIZE); +// Guest physical address for available ring +const AVAIL_RING_ADDR: u64 = align!(DESC_TABLE_ADDR + DESC_TABLE_SIZE, AVAIL_RING_ALIGN_SIZE); +// Guest physical address for used ring +const USED_RING_ADDR: u64 = align!(AVAIL_RING_ADDR + AVAIL_RING_SIZE, USED_RING_ALIGN_SIZE); +// Virtio-queue size in bytes +const QUEUE_BYTES_SIZE: usize = (USED_RING_ADDR + USED_RING_SIZE - DESC_TABLE_ADDR) as usize; + +fuzz_target!(|bytes: &[u8]| -> Corpus { + if bytes.len() < (QUEUE_DATA_SIZE + QUEUE_BYTES_SIZE) + || bytes.len() > (QUEUE_DATA_SIZE + QUEUE_BYTES_SIZE + MEM_SIZE) + { + return Corpus::Reject; + } + + let queue_data = &bytes[..QUEUE_DATA_SIZE]; + let queue_bytes = &bytes[QUEUE_DATA_SIZE..QUEUE_DATA_SIZE + QUEUE_BYTES_SIZE]; + let mem_bytes = &bytes[QUEUE_DATA_SIZE + QUEUE_BYTES_SIZE..]; + + let q = setup_virt_queue(queue_data.try_into().unwrap()); + + let tmp_dir = TempDir::new_with_prefix("/tmp/fuzz_virtio_vsock").unwrap(); + let vsock_path = tmp_dir.as_path().join("vsock.sock"); + + let backend = TestBackend::new(); + + // Setup the guest memory with the input bytes + let mem = GuestMemoryMmap::from_ranges(&[ + (GuestAddress(0), MEM_SIZE), + (GuestAddress(DESC_TABLE_ADDR), QUEUE_BYTES_SIZE), + ]) + .unwrap(); + if mem + .write_slice(queue_bytes, GuestAddress(DESC_TABLE_ADDR)) + .is_err() + { + return Corpus::Reject; + } + if mem.write_slice(mem_bytes, GuestAddress(0 as u64)).is_err() { + return Corpus::Reject; + } + let guest_memory = GuestMemoryAtomic::new(mem); + + let evt = EventFd::new(0).unwrap(); + let queue_evt = unsafe { EventFd::from_raw_fd(libc::dup(evt.as_raw_fd())) }; + + // Kick the 'queue' event before activate the rng device + queue_evt.write(1).unwrap(); + + let mut vsock = virtio_devices::Vsock::new( + "fuzzer_vsock".to_owned(), + 0, + vsock_path, + backend, + false, + SeccompAction::Allow, + EventFd::new(EFD_NONBLOCK).unwrap(), + None, + ) + .unwrap(); + + vsock + .activate( + guest_memory, + Arc::new(NoopVirtioInterrupt {}), + vec![(0, q, evt)], + ) + .ok(); + + // Wait for the events to finish and vsock device worker thread to return + vsock.wait_for_epoll_threads(); + + Corpus::Keep +}); + +pub struct NoopVirtioInterrupt {} + +impl VirtioInterrupt for NoopVirtioInterrupt { + fn trigger(&self, _int_type: VirtioInterruptType) -> std::result::Result<(), std::io::Error> { + Ok(()) + } +} + +fn setup_virt_queue(bytes: &[u8; QUEUE_DATA_SIZE]) -> Queue { + let mut q = Queue::new(QUEUE_SIZE).unwrap(); + q.set_next_avail(bytes[0] as u16); // 'u8' is enough given the 'QUEUE_SIZE' is small + q.set_next_used(bytes[1] as u16); + q.set_event_idx(bytes[2] % 2 != 0); + q.set_size(bytes[3] as u16 % QUEUE_SIZE); + + q.try_set_desc_table_address(GuestAddress(DESC_TABLE_ADDR)) + .unwrap(); + q.try_set_avail_ring_address(GuestAddress(AVAIL_RING_ADDR)) + .unwrap(); + q.try_set_used_ring_address(GuestAddress(USED_RING_ADDR)) + .unwrap(); + q.set_ready(true); + + q +} diff --git a/virtio-devices/src/vsock/device.rs b/virtio-devices/src/vsock/device.rs index a2b47d5b7..f8c024833 100644 --- a/virtio-devices/src/vsock/device.rs +++ b/virtio-devices/src/vsock/device.rs @@ -378,6 +378,11 @@ where acked_features: self.common.acked_features, } } + + #[cfg(fuzzing)] + pub fn wait_for_epoll_threads(&mut self) { + self.common.wait_for_epoll_threads(); + } } impl Drop for Vsock