fuzz: explicitly keep or reject fuzzer corpus

When the main fuzzer function returns (), it is equivalent to
returning Corpus::Keep.

In some of the return paths, we want to reject the input so that the
libfuzzer won't spend more time mutating them.

The should make fuzzing more efficient. No functional change intended.

Signed-off-by: Wei Liu <liuwe@microsoft.com>
This commit is contained in:
Wei Liu 2024-12-30 23:15:22 +00:00 committed by Wei Liu
parent ef88b2778e
commit 6fd5b0f696
13 changed files with 79 additions and 53 deletions

View File

@ -7,7 +7,7 @@
use std::os::unix::io::{AsRawFd, FromRawFd};
use std::sync::Arc;
use libfuzzer_sys::fuzz_target;
use libfuzzer_sys::{fuzz_target, Corpus};
use seccompiler::SeccompAction;
use virtio_devices::{VirtioDevice, VirtioInterrupt, VirtioInterruptType};
use virtio_queue::{Queue, QueueT};
@ -37,11 +37,11 @@ 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;
fuzz_target!(|bytes| {
fuzz_target!(|bytes: &[u8]| -> Corpus {
if bytes.len() < QUEUE_DATA_SIZE * QUEUE_NUM
|| bytes.len() > (QUEUE_DATA_SIZE * QUEUE_NUM + MEM_SIZE)
{
return;
return Corpus::Reject;
}
let mut balloon = virtio_devices::Balloon::new(
@ -61,7 +61,7 @@ fuzz_target!(|bytes| {
// Setup the guest memory with the input bytes
let mem = GuestMemoryMmap::from_ranges(&[(GuestAddress(0), MEM_SIZE)]).unwrap();
if mem.write_slice(mem_bytes, GuestAddress(0 as u64)).is_err() {
return;
return Corpus::Reject;
}
let guest_memory = GuestMemoryAtomic::new(mem);
@ -108,6 +108,8 @@ fuzz_target!(|bytes| {
// Wait for the events to finish and balloon device worker thread to return
balloon.wait_for_epoll_threads();
Corpus::Keep
});
pub struct NoopVirtioInterrupt {}

View File

@ -17,7 +17,7 @@ use std::{ffi, io};
use block::async_io::DiskFile;
use block::raw_sync::RawFileDiskSync;
use libfuzzer_sys::fuzz_target;
use libfuzzer_sys::{fuzz_target, Corpus};
use seccompiler::SeccompAction;
use virtio_devices::{Block, VirtioDevice, VirtioInterrupt, VirtioInterruptType};
use virtio_queue::{Queue, QueueT};
@ -40,9 +40,9 @@ const AVAIL_RING_SIZE: u64 = 6_u64 + 2 * QUEUE_SIZE as u64;
// Guest physical address for used ring (requires to 4-bytes aligned)
const USED_RING_ADDR: u64 = (AVAIL_RING_ADDR + AVAIL_RING_SIZE + 3) & !3_u64;
fuzz_target!(|bytes| {
fuzz_target!(|bytes: &[u8]| -> Corpus {
if bytes.len() < QUEUE_DATA_SIZE || bytes.len() > (QUEUE_DATA_SIZE + MEM_SIZE) {
return;
return Corpus::Reject;
}
let queue_data = &bytes[..QUEUE_DATA_SIZE];
@ -76,7 +76,7 @@ fuzz_target!(|bytes| {
// Setup the guest memory with the input bytes
let mem = GuestMemoryMmap::from_ranges(&[(GuestAddress(0), MEM_SIZE)]).unwrap();
if mem.write_slice(mem_bytes, GuestAddress(0 as u64)).is_err() {
return;
return Corpus::Reject;
}
let guest_memory = GuestMemoryAtomic::new(mem);
@ -96,6 +96,8 @@ fuzz_target!(|bytes| {
// Wait for the events to finish and block device worker thread to return
block.wait_for_epoll_threads();
Corpus::Keep
});
fn memfd_create(name: &ffi::CStr, flags: u32) -> Result<RawFd, io::Error> {

View File

@ -5,14 +5,14 @@
#![no_main]
use devices::legacy::Cmos;
use libc::EFD_NONBLOCK;
use libfuzzer_sys::fuzz_target;
use libfuzzer_sys::{fuzz_target, Corpus};
use vm_device::BusDevice;
use vmm_sys_util::eventfd::EventFd;
fuzz_target!(|bytes| {
fuzz_target!(|bytes: &[u8]| -> Corpus {
// Need at least 16 bytes for the test
if bytes.len() < 16 {
return;
return Corpus::Reject;
}
let mut below_4g = [0u8; 8];
@ -46,4 +46,6 @@ fuzz_target!(|bytes| {
cmos.write(0, offset, &data);
}
}
Corpus::Keep
});

View File

@ -9,7 +9,7 @@ use std::io::Write;
use std::os::unix::io::{AsRawFd, FromRawFd, RawFd};
use std::sync::Arc;
use libfuzzer_sys::fuzz_target;
use libfuzzer_sys::{fuzz_target, Corpus};
use seccompiler::SeccompAction;
use virtio_devices::{VirtioDevice, VirtioInterrupt, VirtioInterruptType};
use virtio_queue::{Queue, QueueT};
@ -54,12 +54,12 @@ const QUEUE_BYTES_SIZE: usize = align!(
DESC_TABLE_ALIGN_SIZE
) as usize;
fuzz_target!(|bytes| {
fuzz_target!(|bytes: &[u8]| -> Corpus {
if bytes.len() < (QUEUE_DATA_SIZE + QUEUE_BYTES_SIZE) * QUEUE_NUM + CONSOLE_INPUT_SIZE
|| bytes.len()
> (QUEUE_DATA_SIZE + QUEUE_BYTES_SIZE) * QUEUE_NUM + CONSOLE_INPUT_SIZE + MEM_SIZE
{
return;
return Corpus::Reject;
}
let (pipe_rx, mut pipe_tx) = create_pipe().unwrap();
@ -108,10 +108,10 @@ fuzz_target!(|bytes| {
.write_slice(queue_bytes, GuestAddress(BASE_VIRT_QUEUE_ADDR))
.is_err()
{
return;
return Corpus::Reject;
}
if mem.write_slice(mem_bytes, GuestAddress(0 as u64)).is_err() {
return;
return Corpus::Reject;
}
let guest_memory = GuestMemoryAtomic::new(mem);
@ -137,6 +137,8 @@ fuzz_target!(|bytes| {
// Wait for the events to finish and console device worker thread to return
console.wait_for_epoll_threads();
Corpus::Keep
});
pub struct NoopVirtioInterrupt {}

View File

@ -8,7 +8,7 @@ use std::path::PathBuf;
use std::sync::mpsc::{channel, Receiver};
use std::thread;
use libfuzzer_sys::fuzz_target;
use libfuzzer_sys::{fuzz_target, Corpus};
use micro_http::Request;
use once_cell::sync::Lazy;
use vm_migration::MigratableError;
@ -27,9 +27,9 @@ use vmm_sys_util::eventfd::EventFd;
static ROUTES: Lazy<Vec<&Box<dyn EndpointHandler + Sync + Send>>> =
Lazy::new(|| HTTP_ROUTES.routes.values().collect());
fuzz_target!(|bytes| {
fuzz_target!(|bytes: &[u8]| -> Corpus {
if bytes.len() < 2 {
return;
return Corpus::Reject;
}
let route = ROUTES[bytes[0] as usize % ROUTES.len()];
@ -53,6 +53,8 @@ fuzz_target!(|bytes| {
exit_evt.write(1).ok();
http_receiver_thread.join().unwrap();
};
Corpus::Keep
});
fn generate_request(bytes: &[u8]) -> Option<Request> {

View File

@ -7,7 +7,7 @@
use std::os::unix::io::{AsRawFd, FromRawFd};
use std::sync::Arc;
use libfuzzer_sys::fuzz_target;
use libfuzzer_sys::{fuzz_target, Corpus};
use seccompiler::SeccompAction;
use virtio_devices::{VirtioDevice, VirtioInterrupt, VirtioInterruptType};
use virtio_queue::{Queue, QueueT};
@ -54,11 +54,11 @@ const USED_RING_ADDR: u64 = align!(AVAIL_RING_ADDR + AVAIL_RING_SIZE, USED_RING_
// Virtio-queue size in bytes
const QUEUE_BYTES_SIZE: usize = (USED_RING_ADDR + USED_RING_SIZE - DESC_TABLE_ADDR) as usize;
fuzz_target!(|bytes| {
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;
return Corpus::Reject;
}
let (mut iommu, _) = virtio_devices::Iommu::new(
@ -91,10 +91,10 @@ fuzz_target!(|bytes| {
.write_slice(queue_bytes, GuestAddress(DESC_TABLE_ADDR))
.is_err()
{
return;
return Corpus::Reject;
}
if mem.write_slice(mem_bytes, GuestAddress(0 as u64)).is_err() {
return;
return Corpus::Reject;
}
let guest_memory = GuestMemoryAtomic::new(mem);
@ -118,6 +118,8 @@ fuzz_target!(|bytes| {
// Wait for the events to finish and vIOMMU device worker thread to return
iommu.wait_for_epoll_threads();
Corpus::Keep
});
pub struct NoopVirtioInterrupt {}

View File

@ -8,7 +8,7 @@
#![no_main]
use libfuzzer_sys::fuzz_target;
use libfuzzer_sys::{fuzz_target, Corpus};
use vm_memory::bitmap::AtomicBitmap;
use vm_memory::GuestAddress;
@ -18,7 +18,7 @@ const MEM_SIZE: usize = 256 * 1024 * 1024;
// From 'arch::x86_64::layout::CMDLINE_START'
const CMDLINE_START: GuestAddress = GuestAddress(0x20000);
fuzz_target!(|bytes| {
fuzz_target!(|bytes: &[u8]| -> Corpus {
let payload_config = vmm::vm_config::PayloadConfig {
firmware: None,
kernel: None,
@ -29,9 +29,11 @@ fuzz_target!(|bytes| {
};
let kernel_cmdline = match vmm::vm::Vm::generate_cmdline(&payload_config) {
Ok(cmdline) => cmdline,
_ => return,
_ => return Corpus::Reject,
};
let guest_memory = GuestMemoryMmap::from_ranges(&[(GuestAddress(0), MEM_SIZE)]).unwrap();
linux_loader::loader::load_cmdline(&guest_memory, CMDLINE_START, &kernel_cmdline).ok();
Corpus::Keep
});

View File

@ -7,7 +7,7 @@
use std::os::unix::io::{AsRawFd, FromRawFd};
use std::sync::{Arc, Mutex};
use libfuzzer_sys::fuzz_target;
use libfuzzer_sys::{fuzz_target, Corpus};
use seccompiler::SeccompAction;
use virtio_devices::{BlocksState, Mem, VirtioDevice, VirtioInterrupt, VirtioInterruptType};
use virtio_queue::{Queue, QueueT};
@ -57,11 +57,11 @@ const USED_RING_ADDR: u64 = align!(AVAIL_RING_ADDR + AVAIL_RING_SIZE, USED_RING_
// Virtio-queue size in bytes
const QUEUE_BYTES_SIZE: usize = (USED_RING_ADDR + USED_RING_SIZE - DESC_TABLE_ADDR) as usize;
fuzz_target!(|bytes| {
fuzz_target!(|bytes: &[u8]| -> Corpus {
if bytes.len() < VIRTIO_MEM_DATA_SIZE + QUEUE_DATA_SIZE + QUEUE_BYTES_SIZE
|| bytes.len() > (VIRTIO_MEM_DATA_SIZE + QUEUE_DATA_SIZE + QUEUE_BYTES_SIZE + MEM_SIZE)
{
return;
return Corpus::Reject;
}
let virtio_mem_data = &bytes[..VIRTIO_MEM_DATA_SIZE];
@ -86,7 +86,7 @@ fuzz_target!(|bytes| {
.write_slice(queue_bytes, GuestAddress(DESC_TABLE_ADDR))
.is_err()
{
return;
return Corpus::Reject;
}
// Add the memory region for the virtio-mem device
let mem = mem.insert_region(virtio_mem_region).unwrap();
@ -94,7 +94,7 @@ fuzz_target!(|bytes| {
.write_slice(mem_bytes, GuestAddress(VIRTIO_MEM_REGION_ADDRESS))
.is_err()
{
return;
return Corpus::Reject;
}
let guest_memory = GuestMemoryAtomic::new(mem);
@ -114,6 +114,8 @@ fuzz_target!(|bytes| {
// Wait for the events to finish and virtio-mem device worker thread to return
virtio_mem.wait_for_epoll_threads();
return Corpus::Keep;
});
pub struct NoopVirtioInterrupt {}

View File

@ -9,7 +9,7 @@ use std::io::{Read, Write};
use std::os::unix::io::{AsRawFd, FromRawFd};
use std::sync::Arc;
use libfuzzer_sys::fuzz_target;
use libfuzzer_sys::{fuzz_target, Corpus};
use seccompiler::SeccompAction;
use virtio_devices::{VirtioDevice, VirtioInterrupt, VirtioInterruptType};
use virtio_queue::{Queue, QueueT};
@ -55,12 +55,12 @@ const QUEUE_BYTES_SIZE: usize = align!(
DESC_TABLE_ALIGN_SIZE
) as usize;
fuzz_target!(|bytes| {
fuzz_target!(|bytes: &[u8]| -> Corpus {
if bytes.len() < TAP_INPUT_SIZE + (QUEUE_DATA_SIZE + QUEUE_BYTES_SIZE) * QUEUE_NUM
|| bytes.len()
> TAP_INPUT_SIZE + (QUEUE_DATA_SIZE + QUEUE_BYTES_SIZE) * QUEUE_NUM + MEM_SIZE
{
return;
return Corpus::Reject;
}
let (dummy_tap_frontend, dummy_tap_backend) = create_socketpair().unwrap();
@ -111,10 +111,10 @@ fuzz_target!(|bytes| {
.write_slice(queue_bytes, GuestAddress(BASE_VIRT_QUEUE_ADDR))
.is_err()
{
return;
return Corpus::Reject;
}
if mem.write_slice(mem_bytes, GuestAddress(0 as u64)).is_err() {
return;
return Corpus::Reject;
}
let guest_memory = GuestMemoryAtomic::new(mem);
@ -155,6 +155,8 @@ fuzz_target!(|bytes| {
// Terminate the thread for the dummy tap backend
exit_evt.write(1).ok();
tap_backend_thread.join().unwrap();
return Corpus::Keep;
});
pub struct NoopVirtioInterrupt {}

View File

@ -10,7 +10,7 @@ use std::sync::Arc;
use std::{ffi, io};
use libc::{MAP_NORESERVE, MAP_PRIVATE, PROT_READ, PROT_WRITE};
use libfuzzer_sys::fuzz_target;
use libfuzzer_sys::{fuzz_target, Corpus};
use seccompiler::SeccompAction;
use virtio_devices::{Pmem, UserspaceMapping, VirtioDevice, VirtioInterrupt, VirtioInterruptType};
use virtio_queue::{Queue, QueueT};
@ -35,9 +35,9 @@ const AVAIL_RING_SIZE: u64 = 6_u64 + 2 * QUEUE_SIZE as u64;
// Guest physical address for used ring (requires to 4-bytes aligned)
const USED_RING_ADDR: u64 = (AVAIL_RING_ADDR + AVAIL_RING_SIZE + 3) & !3_u64;
fuzz_target!(|bytes| {
fuzz_target!(|bytes: &[u8]| -> Corpus {
if bytes.len() < QUEUE_DATA_SIZE || bytes.len() > (QUEUE_DATA_SIZE + MEM_SIZE) {
return;
return Corpus::Reject;
}
let mut pmem = create_dummy_pmem();
@ -50,7 +50,7 @@ fuzz_target!(|bytes| {
// Setup the guest memory with the input bytes
let mem = GuestMemoryMmap::from_ranges(&[(GuestAddress(0), MEM_SIZE)]).unwrap();
if mem.write_slice(mem_bytes, GuestAddress(0 as u64)).is_err() {
return;
return Corpus::Reject;
}
let guest_memory = GuestMemoryAtomic::new(mem);
@ -69,6 +69,8 @@ fuzz_target!(|bytes| {
// Wait for the events to finish and pmem device worker thread to return
pmem.wait_for_epoll_threads();
Corpus::Keep
});
fn memfd_create_with_size(name: &ffi::CStr, flags: u32, size: usize) -> Result<RawFd, io::Error> {

View File

@ -12,14 +12,14 @@ use std::mem::size_of;
use std::os::unix::io::{FromRawFd, RawFd};
use block::qcow::{QcowFile, RawFile};
use libfuzzer_sys::fuzz_target;
use libfuzzer_sys::{fuzz_target, Corpus};
// Take the first 64 bits of data as an address and the next 64 bits as data to
// store there. The rest of the data is used as a qcow image.
fuzz_target!(|bytes| {
fuzz_target!(|bytes: &[u8]| -> Corpus {
if bytes.len() < 16 {
// Need an address and data, each are 8 bytes.
return;
return Corpus::Reject;
}
let mut disk_image = Cursor::new(bytes);
let addr = read_u64(&mut disk_image);
@ -33,6 +33,8 @@ fuzz_target!(|bytes| {
let _ = qcow.write_all(&value.to_le_bytes());
}
}
Corpus::Keep
});
fn read_u64<T: Read>(readable: &mut T) -> u64 {

View File

@ -7,7 +7,7 @@
use std::os::unix::io::{AsRawFd, FromRawFd};
use std::sync::Arc;
use libfuzzer_sys::fuzz_target;
use libfuzzer_sys::{fuzz_target, Corpus};
use seccompiler::SeccompAction;
use virtio_devices::{VirtioDevice, VirtioInterrupt, VirtioInterruptType};
use virtio_queue::{Queue, QueueT};
@ -52,11 +52,11 @@ const USED_RING_ADDR: u64 = align!(AVAIL_RING_ADDR + AVAIL_RING_SIZE, USED_RING_
// Virtio-queue size in bytes
const QUEUE_BYTES_SIZE: usize = (USED_RING_ADDR + USED_RING_SIZE - DESC_TABLE_ADDR) as usize;
fuzz_target!(|bytes| {
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;
return Corpus::Reject;
}
let mut rng = virtio_devices::Rng::new(
@ -86,10 +86,10 @@ fuzz_target!(|bytes| {
.write_slice(queue_bytes, GuestAddress(DESC_TABLE_ADDR))
.is_err()
{
return;
return Corpus::Reject;
}
if mem.write_slice(mem_bytes, GuestAddress(0 as u64)).is_err() {
return;
return Corpus::Reject;
}
let guest_memory = GuestMemoryAtomic::new(mem);
@ -108,6 +108,8 @@ fuzz_target!(|bytes| {
// Wait for the events to finish and rng device worker thread to return
rng.wait_for_epoll_threads();
Corpus::Keep
});
pub struct NoopVirtioInterrupt {}

View File

@ -7,7 +7,7 @@
use std::os::unix::io::{AsRawFd, FromRawFd};
use std::sync::Arc;
use libfuzzer_sys::fuzz_target;
use libfuzzer_sys::{fuzz_target, Corpus};
use seccompiler::SeccompAction;
use virtio_devices::{VirtioDevice, VirtioInterrupt, VirtioInterruptType};
use virtio_queue::{Queue, QueueT};
@ -30,9 +30,9 @@ const AVAIL_RING_SIZE: u64 = 6_u64 + 2 * QUEUE_SIZE as u64;
// Guest physical address for used ring (requires to 4-bytes aligned)
const USED_RING_ADDR: u64 = (AVAIL_RING_ADDR + AVAIL_RING_SIZE + 3) & !3_u64;
fuzz_target!(|bytes| {
fuzz_target!(|bytes: &[u8]| -> Corpus {
if bytes.len() < QUEUE_DATA_SIZE || bytes.len() > (QUEUE_DATA_SIZE + MEM_SIZE) {
return;
return Corpus::Reject;
}
let mut watchdog = virtio_devices::Watchdog::new(
@ -53,7 +53,7 @@ fuzz_target!(|bytes| {
// Setup the guest memory with the input bytes
let mem = GuestMemoryMmap::from_ranges(&[(GuestAddress(0), MEM_SIZE)]).unwrap();
if mem.write_slice(mem_bytes, GuestAddress(0 as u64)).is_err() {
return;
return Corpus::Reject;
}
let guest_memory = GuestMemoryAtomic::new(mem);
@ -73,6 +73,8 @@ fuzz_target!(|bytes| {
// Wait for the events to finish and watchdog device worker thread to return
watchdog.wait_for_epoll_threads();
Corpus::Keep
});
pub struct NoopVirtioInterrupt {}