mirror of
https://github.com/cloud-hypervisor/cloud-hypervisor.git
synced 2025-01-08 13:55:19 +00:00
fuzz: introduce an x86 instruction emulator fuzzer
Signed-off-by: Wei Liu <liuwe@microsoft.com>
This commit is contained in:
parent
fe24a7a24f
commit
7c39f37855
68
fuzz/Cargo.lock
generated
68
fuzz/Cargo.lock
generated
@ -7,7 +7,7 @@ name = "acpi_tables"
|
|||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
source = "git+https://github.com/rust-vmm/acpi_tables?branch=main#849d5950196f66dd10f2b2606d8fe8c7cb39ec24"
|
source = "git+https://github.com/rust-vmm/acpi_tables?branch=main#849d5950196f66dd10f2b2606d8fe8c7cb39ec24"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"zerocopy",
|
"zerocopy 0.7.35",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
@ -67,9 +67,9 @@ checksum = "34ac096ce696dc2fcabef30516bb13c0a68a11d30131d3df6f04711467681b04"
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "arbitrary"
|
name = "arbitrary"
|
||||||
version = "1.3.2"
|
version = "1.4.1"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "7d5a26814d8dcb93b0e5a0ff3c6d80a8843bafb21b39e8e18a6f05471870e110"
|
checksum = "dde20b3d026af13f561bdd0f15edf01fc734f0dafcedbaf42bba506a9517f223"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "arc-swap"
|
name = "arc-swap"
|
||||||
@ -195,13 +195,16 @@ checksum = "1462739cb27611015575c0c11df5df7601141071f07518d56fcc1be504cbec97"
|
|||||||
name = "cloud-hypervisor-fuzz"
|
name = "cloud-hypervisor-fuzz"
|
||||||
version = "0.0.0"
|
version = "0.0.0"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
|
"arbitrary",
|
||||||
"block",
|
"block",
|
||||||
"devices",
|
"devices",
|
||||||
"epoll",
|
"epoll",
|
||||||
|
"hypervisor",
|
||||||
"libc",
|
"libc",
|
||||||
"libfuzzer-sys",
|
"libfuzzer-sys",
|
||||||
"linux-loader",
|
"linux-loader",
|
||||||
"micro_http",
|
"micro_http",
|
||||||
|
"mshv-bindings",
|
||||||
"net_util",
|
"net_util",
|
||||||
"once_cell",
|
"once_cell",
|
||||||
"seccompiler",
|
"seccompiler",
|
||||||
@ -441,10 +444,12 @@ dependencies = [
|
|||||||
"byteorder",
|
"byteorder",
|
||||||
"cfg-if",
|
"cfg-if",
|
||||||
"concat-idents",
|
"concat-idents",
|
||||||
|
"iced-x86",
|
||||||
"kvm-bindings",
|
"kvm-bindings",
|
||||||
"kvm-ioctls",
|
"kvm-ioctls",
|
||||||
"libc",
|
"libc",
|
||||||
"log",
|
"log",
|
||||||
|
"mshv-bindings",
|
||||||
"serde",
|
"serde",
|
||||||
"serde_with",
|
"serde_with",
|
||||||
"thiserror",
|
"thiserror",
|
||||||
@ -453,6 +458,15 @@ dependencies = [
|
|||||||
"vmm-sys-util",
|
"vmm-sys-util",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "iced-x86"
|
||||||
|
version = "1.21.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "7c447cff8c7f384a7d4f741cfcff32f75f3ad02b406432e8d6c878d56b1edf6b"
|
||||||
|
dependencies = [
|
||||||
|
"lazy_static",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "ident_case"
|
name = "ident_case"
|
||||||
version = "1.0.1"
|
version = "1.0.1"
|
||||||
@ -507,7 +521,7 @@ checksum = "fa4933174d0cc4b77b958578cd45784071cc5ae212c2d78fbd755aaaa6dfa71a"
|
|||||||
dependencies = [
|
dependencies = [
|
||||||
"serde",
|
"serde",
|
||||||
"vmm-sys-util",
|
"vmm-sys-util",
|
||||||
"zerocopy",
|
"zerocopy 0.7.35",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
@ -533,6 +547,12 @@ dependencies = [
|
|||||||
"thiserror",
|
"thiserror",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "lazy_static"
|
||||||
|
version = "1.5.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "bbd2bcb4c963f2ddae06a2efc7e9f3591312473c50c6685e1f298068316e66fe"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "libc"
|
name = "libc"
|
||||||
version = "0.2.169"
|
version = "0.2.169"
|
||||||
@ -596,6 +616,20 @@ dependencies = [
|
|||||||
"vmm-sys-util",
|
"vmm-sys-util",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "mshv-bindings"
|
||||||
|
version = "0.3.2"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "1e0cb5031f3243a7459b7c13d960d25420980874eebda816db24ce6077e21d43"
|
||||||
|
dependencies = [
|
||||||
|
"libc",
|
||||||
|
"num_enum",
|
||||||
|
"serde",
|
||||||
|
"serde_derive",
|
||||||
|
"vmm-sys-util",
|
||||||
|
"zerocopy 0.8.14",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "nanorand"
|
name = "nanorand"
|
||||||
version = "0.7.0"
|
version = "0.7.0"
|
||||||
@ -704,7 +738,7 @@ version = "0.2.20"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "77957b295656769bb8ad2b6a6b09d897d94f05c41b069aede1fcdaa675eaea04"
|
checksum = "77957b295656769bb8ad2b6a6b09d897d94f05c41b069aede1fcdaa675eaea04"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"zerocopy",
|
"zerocopy 0.7.35",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
@ -1228,7 +1262,7 @@ dependencies = [
|
|||||||
"vm-migration",
|
"vm-migration",
|
||||||
"vm-virtio",
|
"vm-virtio",
|
||||||
"vmm-sys-util",
|
"vmm-sys-util",
|
||||||
"zerocopy",
|
"zerocopy 0.7.35",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
@ -1415,7 +1449,16 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
|||||||
checksum = "1b9b4fd18abc82b8136838da5d50bae7bdea537c574d8dc1a34ed098d6c166f0"
|
checksum = "1b9b4fd18abc82b8136838da5d50bae7bdea537c574d8dc1a34ed098d6c166f0"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"byteorder",
|
"byteorder",
|
||||||
"zerocopy-derive",
|
"zerocopy-derive 0.7.35",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "zerocopy"
|
||||||
|
version = "0.8.14"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "a367f292d93d4eab890745e75a778da40909cab4d6ff8173693812f79c4a2468"
|
||||||
|
dependencies = [
|
||||||
|
"zerocopy-derive 0.8.14",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
@ -1428,3 +1471,14 @@ dependencies = [
|
|||||||
"quote",
|
"quote",
|
||||||
"syn",
|
"syn",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "zerocopy-derive"
|
||||||
|
version = "0.8.14"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "d3931cb58c62c13adec22e38686b559c86a30565e16ad6e8510a337cedc611e1"
|
||||||
|
dependencies = [
|
||||||
|
"proc-macro2",
|
||||||
|
"quote",
|
||||||
|
"syn",
|
||||||
|
]
|
||||||
|
@ -10,16 +10,20 @@ cargo-fuzz = true
|
|||||||
|
|
||||||
[features]
|
[features]
|
||||||
igvm = []
|
igvm = []
|
||||||
|
mshv_emulator = ["hypervisor/mshv_emulator"]
|
||||||
pvmemcontrol = []
|
pvmemcontrol = []
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
|
arbitrary = "1.4.1"
|
||||||
block = { path = "../block" }
|
block = { path = "../block" }
|
||||||
devices = { path = "../devices" }
|
devices = { path = "../devices" }
|
||||||
epoll = "4.3.3"
|
epoll = "4.3.3"
|
||||||
|
hypervisor = { path = "../hypervisor", features = ["mshv_emulator"] }
|
||||||
libc = "0.2.155"
|
libc = "0.2.155"
|
||||||
libfuzzer-sys = "0.4.7"
|
libfuzzer-sys = "0.4.7"
|
||||||
linux-loader = { version = "0.13.0", features = ["bzimage", "elf", "pe"] }
|
linux-loader = { version = "0.13.0", features = ["bzimage", "elf", "pe"] }
|
||||||
micro_http = { git = "https://github.com/firecracker-microvm/micro-http", branch = "main" }
|
micro_http = { git = "https://github.com/firecracker-microvm/micro-http", branch = "main" }
|
||||||
|
mshv-bindings = "0.3.2"
|
||||||
net_util = { path = "../net_util" }
|
net_util = { path = "../net_util" }
|
||||||
once_cell = "1.19.0"
|
once_cell = "1.19.0"
|
||||||
seccompiler = "0.4.0"
|
seccompiler = "0.4.0"
|
||||||
@ -131,3 +135,10 @@ doc = false
|
|||||||
name = "watchdog"
|
name = "watchdog"
|
||||||
path = "fuzz_targets/watchdog.rs"
|
path = "fuzz_targets/watchdog.rs"
|
||||||
test = false
|
test = false
|
||||||
|
|
||||||
|
[[bin]]
|
||||||
|
doc = false
|
||||||
|
name = "x86emul"
|
||||||
|
path = "fuzz_targets/x86emul.rs"
|
||||||
|
required-features = ["mshv_emulator"]
|
||||||
|
test = false
|
||||||
|
153
fuzz/fuzz_targets/x86emul.rs
Normal file
153
fuzz/fuzz_targets/x86emul.rs
Normal file
@ -0,0 +1,153 @@
|
|||||||
|
// Copyright © 2025 Microsoft Corporation
|
||||||
|
//
|
||||||
|
// SPDX-License-Identifier: Apache-2.0
|
||||||
|
|
||||||
|
#![no_main]
|
||||||
|
|
||||||
|
use hypervisor::arch::emulator::{PlatformEmulator, PlatformError};
|
||||||
|
use hypervisor::arch::x86::emulator::{Emulator, EmulatorCpuState};
|
||||||
|
use hypervisor::arch::x86::{DescriptorTable, SegmentRegister, SpecialRegisters};
|
||||||
|
use hypervisor::StandardRegisters;
|
||||||
|
use libfuzzer_sys::{fuzz_target, Corpus};
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
|
struct EmulatorContext {
|
||||||
|
state: EmulatorCpuState,
|
||||||
|
memory: [u8; 8],
|
||||||
|
}
|
||||||
|
|
||||||
|
impl PlatformEmulator for EmulatorContext {
|
||||||
|
type CpuState = EmulatorCpuState;
|
||||||
|
|
||||||
|
fn read_memory(&self, _gva: u64, data: &mut [u8]) -> Result<(), PlatformError> {
|
||||||
|
data.copy_from_slice(&self.memory[..data.len()]);
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn write_memory(&mut self, _gva: u64, _data: &[u8]) -> Result<(), PlatformError> {
|
||||||
|
// Discard writes
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn cpu_state(&self, _cpu_id: usize) -> Result<Self::CpuState, PlatformError> {
|
||||||
|
Ok(self.state.clone())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn set_cpu_state(&self, _cpu_id: usize, _state: Self::CpuState) -> Result<(), PlatformError> {
|
||||||
|
// Ignore
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn fetch(&self, _ip: u64, _data: &mut [u8]) -> Result<(), PlatformError> {
|
||||||
|
// The fuzzer already provides 16 bytes of data, we don't need to fetch anything
|
||||||
|
panic!("fetch should not be called");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fuzz_target!(|bytes: &[u8]| -> Corpus {
|
||||||
|
let (mut ctx, insn) = match generate_context_and_instruction(bytes) {
|
||||||
|
Ok((ctx, insn)) => (ctx, insn),
|
||||||
|
Err(_) => return Corpus::Reject,
|
||||||
|
};
|
||||||
|
|
||||||
|
let mut e = Emulator::new(&mut ctx);
|
||||||
|
|
||||||
|
if e.emulate_first_insn(0, &insn).is_err() {
|
||||||
|
return Corpus::Reject;
|
||||||
|
}
|
||||||
|
|
||||||
|
Corpus::Keep
|
||||||
|
});
|
||||||
|
|
||||||
|
// Helper functions to generate structures from fuzzer input below
|
||||||
|
|
||||||
|
fn generate_segment_register(
|
||||||
|
u: &mut arbitrary::Unstructured<'_>,
|
||||||
|
) -> arbitrary::Result<SegmentRegister> {
|
||||||
|
Ok(SegmentRegister {
|
||||||
|
base: u.arbitrary()?,
|
||||||
|
limit: u.arbitrary()?,
|
||||||
|
selector: u.arbitrary()?,
|
||||||
|
avl: u.arbitrary()?,
|
||||||
|
dpl: u.arbitrary()?,
|
||||||
|
db: u.arbitrary()?,
|
||||||
|
g: u.arbitrary()?,
|
||||||
|
l: u.arbitrary()?,
|
||||||
|
present: u.arbitrary()?,
|
||||||
|
s: u.arbitrary()?,
|
||||||
|
type_: u.arbitrary()?,
|
||||||
|
unusable: u.arbitrary()?,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
fn generate_descriptor_table(
|
||||||
|
u: &mut arbitrary::Unstructured<'_>,
|
||||||
|
) -> arbitrary::Result<DescriptorTable> {
|
||||||
|
Ok(DescriptorTable {
|
||||||
|
base: u.arbitrary()?,
|
||||||
|
limit: u.arbitrary()?,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
fn generate_context_and_instruction(
|
||||||
|
bytes: &[u8],
|
||||||
|
) -> arbitrary::Result<(EmulatorContext, [u8; 16])> {
|
||||||
|
let mut u = arbitrary::Unstructured::new(bytes);
|
||||||
|
|
||||||
|
let mut regs = mshv_bindings::StandardRegisters {
|
||||||
|
rax: u.arbitrary()?,
|
||||||
|
rbx: u.arbitrary()?,
|
||||||
|
rcx: u.arbitrary()?,
|
||||||
|
rdx: u.arbitrary()?,
|
||||||
|
rsi: u.arbitrary()?,
|
||||||
|
rdi: u.arbitrary()?,
|
||||||
|
rsp: u.arbitrary()?,
|
||||||
|
rbp: u.arbitrary()?,
|
||||||
|
r8: u.arbitrary()?,
|
||||||
|
r9: u.arbitrary()?,
|
||||||
|
r10: u.arbitrary()?,
|
||||||
|
r11: u.arbitrary()?,
|
||||||
|
r12: u.arbitrary()?,
|
||||||
|
r13: u.arbitrary()?,
|
||||||
|
r14: u.arbitrary()?,
|
||||||
|
r15: u.arbitrary()?,
|
||||||
|
rip: u.arbitrary()?,
|
||||||
|
rflags: u.arbitrary()?,
|
||||||
|
};
|
||||||
|
|
||||||
|
// Cap RCX to avoid looping for too long for reps instructions.
|
||||||
|
regs.rcx &= 0xFFFFu64;
|
||||||
|
|
||||||
|
let regs = StandardRegisters::Mshv(regs);
|
||||||
|
|
||||||
|
let sregs = SpecialRegisters {
|
||||||
|
cs: generate_segment_register(&mut u)?,
|
||||||
|
ds: generate_segment_register(&mut u)?,
|
||||||
|
es: generate_segment_register(&mut u)?,
|
||||||
|
fs: generate_segment_register(&mut u)?,
|
||||||
|
gs: generate_segment_register(&mut u)?,
|
||||||
|
ss: generate_segment_register(&mut u)?,
|
||||||
|
tr: generate_segment_register(&mut u)?,
|
||||||
|
ldt: generate_segment_register(&mut u)?,
|
||||||
|
gdt: generate_descriptor_table(&mut u)?,
|
||||||
|
idt: generate_descriptor_table(&mut u)?,
|
||||||
|
cr0: u.arbitrary()?,
|
||||||
|
cr2: u.arbitrary()?,
|
||||||
|
cr3: u.arbitrary()?,
|
||||||
|
cr4: u.arbitrary()?,
|
||||||
|
cr8: u.arbitrary()?,
|
||||||
|
efer: u.arbitrary()?,
|
||||||
|
apic_base: u.arbitrary()?,
|
||||||
|
interrupt_bitmap: u.arbitrary()?,
|
||||||
|
};
|
||||||
|
|
||||||
|
let memory = u.arbitrary::<[u8; 8]>()?;
|
||||||
|
let insn = u.arbitrary::<[u8; 16]>()?;
|
||||||
|
|
||||||
|
let ctx = EmulatorContext {
|
||||||
|
state: EmulatorCpuState { regs, sregs },
|
||||||
|
memory,
|
||||||
|
};
|
||||||
|
|
||||||
|
Ok((ctx, insn))
|
||||||
|
}
|
Loading…
Reference in New Issue
Block a user