mirror of
https://github.com/cloud-hypervisor/cloud-hypervisor.git
synced 2024-08-23 08:21:15 +00:00
320 lines
9.2 KiB
Rust
320 lines
9.2 KiB
Rust
// Copyright © 2022 Intel Corporation
|
|
//
|
|
// SPDX-License-Identifier: Apache-2.0
|
|
|
|
#![no_main]
|
|
use libfuzzer_sys::fuzz_target;
|
|
use micro_http::Request;
|
|
use once_cell::sync::Lazy;
|
|
use std::os::unix::io::AsRawFd;
|
|
use std::path::PathBuf;
|
|
use std::sync::mpsc::{channel, Receiver};
|
|
use std::sync::{Arc, Mutex};
|
|
use std::thread;
|
|
use vm_migration::MigratableError;
|
|
use vmm::api::{
|
|
http::*, ApiRequest, RequestHandler, VmInfoResponse, VmReceiveMigrationData,
|
|
VmSendMigrationData, VmmPingResponse,
|
|
};
|
|
use vmm::config::RestoreConfig;
|
|
use vmm::vm::{Error as VmError, VmState};
|
|
use vmm::vm_config::*;
|
|
use vmm::{EpollContext, EpollDispatch};
|
|
use vmm_sys_util::eventfd::EventFd;
|
|
|
|
// Need to be ordered for test case reproducibility
|
|
static ROUTES: Lazy<Vec<&Box<dyn EndpointHandler + Sync + Send>>> =
|
|
Lazy::new(|| HTTP_ROUTES.routes.values().collect());
|
|
|
|
fuzz_target!(|bytes| {
|
|
if bytes.len() < 2 {
|
|
return;
|
|
}
|
|
|
|
let route = ROUTES[bytes[0] as usize % ROUTES.len()];
|
|
if let Some(request) = generate_request(&bytes[1..]) {
|
|
let exit_evt = EventFd::new(libc::EFD_NONBLOCK).unwrap();
|
|
let api_evt = EventFd::new(libc::EFD_NONBLOCK).unwrap();
|
|
let (api_sender, api_receiver) = channel();
|
|
|
|
let http_receiver_thread = {
|
|
let exit_evt = exit_evt.try_clone().unwrap();
|
|
let api_evt = api_evt.try_clone().unwrap();
|
|
thread::Builder::new()
|
|
.name("http_receiver".to_string())
|
|
.spawn(move || {
|
|
http_receiver_stub(exit_evt, api_evt, api_receiver);
|
|
})
|
|
.unwrap()
|
|
};
|
|
|
|
route.handle_request(&request, api_evt, api_sender);
|
|
exit_evt.write(1).ok();
|
|
http_receiver_thread.join().unwrap();
|
|
};
|
|
});
|
|
|
|
fn generate_request(bytes: &[u8]) -> Option<Request> {
|
|
let req_method = match bytes[0] % 5 {
|
|
0 => "GET",
|
|
1 => "PUT",
|
|
2 => "PATCH",
|
|
3 => "POST",
|
|
_ => "INVALID",
|
|
};
|
|
let request_line = format!("{} http://localhost/home HTTP/1.1\r\n", req_method);
|
|
|
|
let req_body = &bytes[1..];
|
|
let request = if req_body.len() > 0 {
|
|
[
|
|
format!("{}Content-Length: {}\r\n", request_line, req_body.len()).as_bytes(),
|
|
req_body,
|
|
]
|
|
.concat()
|
|
} else {
|
|
format!("{}\r\n", request_line).as_bytes().to_vec()
|
|
};
|
|
|
|
Request::try_from(&request, None).ok()
|
|
}
|
|
|
|
struct StubApiRequestHandler;
|
|
|
|
impl RequestHandler for StubApiRequestHandler {
|
|
fn vm_create(&mut self, _: Arc<Mutex<VmConfig>>) -> Result<(), VmError> {
|
|
Ok(())
|
|
}
|
|
|
|
fn vm_boot(&mut self) -> Result<(), VmError> {
|
|
Ok(())
|
|
}
|
|
|
|
fn vm_pause(&mut self) -> Result<(), VmError> {
|
|
Ok(())
|
|
}
|
|
|
|
fn vm_resume(&mut self) -> Result<(), VmError> {
|
|
Ok(())
|
|
}
|
|
|
|
fn vm_snapshot(&mut self, _: &str) -> Result<(), VmError> {
|
|
Ok(())
|
|
}
|
|
|
|
fn vm_restore(&mut self, _: RestoreConfig) -> Result<(), VmError> {
|
|
Ok(())
|
|
}
|
|
|
|
#[cfg(all(target_arch = "x86_64", feature = "guest_debug"))]
|
|
fn vm_coredump(&mut self, _: &str) -> Result<(), VmError> {
|
|
Ok(())
|
|
}
|
|
|
|
fn vm_shutdown(&mut self) -> Result<(), VmError> {
|
|
Ok(())
|
|
}
|
|
|
|
fn vm_reboot(&mut self) -> Result<(), VmError> {
|
|
Ok(())
|
|
}
|
|
|
|
fn vm_info(&self) -> Result<VmInfoResponse, VmError> {
|
|
Ok(VmInfoResponse {
|
|
config: Arc::new(Mutex::new(VmConfig {
|
|
cpus: CpusConfig {
|
|
boot_vcpus: 1,
|
|
max_vcpus: 1,
|
|
topology: None,
|
|
kvm_hyperv: false,
|
|
max_phys_bits: 46,
|
|
affinity: None,
|
|
features: CpuFeatures::default(),
|
|
},
|
|
memory: MemoryConfig {
|
|
size: 536_870_912,
|
|
mergeable: false,
|
|
hotplug_method: HotplugMethod::Acpi,
|
|
hotplug_size: None,
|
|
hotplugged_size: None,
|
|
shared: false,
|
|
hugepages: false,
|
|
hugepage_size: None,
|
|
prefault: false,
|
|
zones: None,
|
|
thp: true,
|
|
},
|
|
payload: Some(PayloadConfig {
|
|
kernel: Some(PathBuf::from("/path/to/kernel")),
|
|
firmware: None,
|
|
cmdline: None,
|
|
initramfs: None,
|
|
#[cfg(feature = "igvm")]
|
|
igvm: None,
|
|
}),
|
|
rate_limit_groups: None,
|
|
disks: None,
|
|
net: None,
|
|
rng: RngConfig {
|
|
src: PathBuf::from("/dev/urandom"),
|
|
iommu: false,
|
|
},
|
|
balloon: None,
|
|
fs: None,
|
|
pmem: None,
|
|
serial: ConsoleConfig {
|
|
file: None,
|
|
mode: ConsoleOutputMode::Null,
|
|
iommu: false,
|
|
socket: None,
|
|
},
|
|
console: ConsoleConfig {
|
|
file: None,
|
|
mode: ConsoleOutputMode::Tty,
|
|
iommu: false,
|
|
socket: None,
|
|
},
|
|
#[cfg(target_arch = "x86_64")]
|
|
debug_console: DebugConsoleConfig::default(),
|
|
devices: None,
|
|
user_devices: None,
|
|
vdpa: None,
|
|
vsock: None,
|
|
pvpanic: false,
|
|
iommu: false,
|
|
#[cfg(target_arch = "x86_64")]
|
|
sgx_epc: None,
|
|
numa: None,
|
|
watchdog: false,
|
|
#[cfg(feature = "guest_debug")]
|
|
gdb: false,
|
|
platform: None,
|
|
tpm: None,
|
|
preserved_fds: None,
|
|
})),
|
|
state: VmState::Running,
|
|
memory_actual_size: 0,
|
|
device_tree: None,
|
|
})
|
|
}
|
|
|
|
fn vmm_ping(&self) -> VmmPingResponse {
|
|
VmmPingResponse {
|
|
build_version: String::new(),
|
|
version: String::new(),
|
|
pid: 0,
|
|
features: Vec::new(),
|
|
}
|
|
}
|
|
|
|
fn vm_delete(&mut self) -> Result<(), VmError> {
|
|
Ok(())
|
|
}
|
|
|
|
fn vmm_shutdown(&mut self) -> Result<(), VmError> {
|
|
Ok(())
|
|
}
|
|
|
|
fn vm_resize(&mut self, _: Option<u8>, _: Option<u64>, _: Option<u64>) -> Result<(), VmError> {
|
|
Ok(())
|
|
}
|
|
|
|
fn vm_resize_zone(&mut self, _: String, _: u64) -> Result<(), VmError> {
|
|
Ok(())
|
|
}
|
|
|
|
fn vm_add_device(&mut self, _: DeviceConfig) -> Result<Option<Vec<u8>>, VmError> {
|
|
Ok(None)
|
|
}
|
|
|
|
fn vm_add_user_device(&mut self, _: UserDeviceConfig) -> Result<Option<Vec<u8>>, VmError> {
|
|
Ok(None)
|
|
}
|
|
|
|
fn vm_remove_device(&mut self, _: String) -> Result<(), VmError> {
|
|
Ok(())
|
|
}
|
|
|
|
fn vm_add_disk(&mut self, _: DiskConfig) -> Result<Option<Vec<u8>>, VmError> {
|
|
Ok(None)
|
|
}
|
|
|
|
fn vm_add_fs(&mut self, _: FsConfig) -> Result<Option<Vec<u8>>, VmError> {
|
|
Ok(None)
|
|
}
|
|
|
|
fn vm_add_pmem(&mut self, _: PmemConfig) -> Result<Option<Vec<u8>>, VmError> {
|
|
Ok(None)
|
|
}
|
|
|
|
fn vm_add_net(&mut self, _: NetConfig) -> Result<Option<Vec<u8>>, VmError> {
|
|
Ok(None)
|
|
}
|
|
|
|
fn vm_add_vdpa(&mut self, _: VdpaConfig) -> Result<Option<Vec<u8>>, VmError> {
|
|
Ok(None)
|
|
}
|
|
|
|
fn vm_add_vsock(&mut self, _: VsockConfig) -> Result<Option<Vec<u8>>, VmError> {
|
|
Ok(None)
|
|
}
|
|
|
|
fn vm_counters(&mut self) -> Result<Option<Vec<u8>>, VmError> {
|
|
Ok(None)
|
|
}
|
|
|
|
fn vm_power_button(&mut self) -> Result<(), VmError> {
|
|
Ok(())
|
|
}
|
|
|
|
fn vm_receive_migration(&mut self, _: VmReceiveMigrationData) -> Result<(), MigratableError> {
|
|
Ok(())
|
|
}
|
|
|
|
fn vm_send_migration(&mut self, _: VmSendMigrationData) -> Result<(), MigratableError> {
|
|
Ok(())
|
|
}
|
|
|
|
fn vm_nmi(&mut self) -> Result<(), VmError> {
|
|
Ok(())
|
|
}
|
|
}
|
|
|
|
fn http_receiver_stub(exit_evt: EventFd, api_evt: EventFd, api_receiver: Receiver<ApiRequest>) {
|
|
let mut epoll = EpollContext::new().unwrap();
|
|
epoll.add_event(&exit_evt, EpollDispatch::Exit).unwrap();
|
|
epoll.add_event(&api_evt, EpollDispatch::Api).unwrap();
|
|
|
|
let epoll_fd = epoll.as_raw_fd();
|
|
let mut events = vec![epoll::Event::new(epoll::Events::empty(), 0); 2];
|
|
let num_events;
|
|
loop {
|
|
num_events = match epoll::wait(epoll_fd, -1, &mut events[..]) {
|
|
Ok(num_events) => num_events,
|
|
Err(e) => match e.raw_os_error() {
|
|
Some(libc::EAGAIN) | Some(libc::EINTR) => continue,
|
|
_ => panic!("Unexpected epoll::wait error!"),
|
|
},
|
|
};
|
|
|
|
break;
|
|
}
|
|
|
|
for event in events.iter().take(num_events) {
|
|
let dispatch_event: EpollDispatch = event.data.into();
|
|
match dispatch_event {
|
|
EpollDispatch::Exit => {
|
|
break;
|
|
}
|
|
EpollDispatch::Api => {
|
|
for _ in 0..api_evt.read().unwrap() {
|
|
let api_request = api_receiver.recv().unwrap();
|
|
api_request(&mut StubApiRequestHandler).unwrap();
|
|
}
|
|
}
|
|
_ => {
|
|
panic!("Unexpected Epoll event");
|
|
}
|
|
}
|
|
}
|
|
}
|