2022-07-29 00:07:32 +00:00
|
|
|
// 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;
|
vmm: use trait objects for API actions
Uses of the old ApiRequest enum conflated two different concerns:
identifying an API request endpoint, and storing data for an API
request. This led to ApiRequest values being passed around with junk
data just to communicate a request type, which forced all API request
body types to implement Default, which in some cases doesn't make any
sense — what's the "default" path for a vhost-user socket? The
nonsensical Default values have led to tests relying on being able to
use nonsensical data, which is an impediment to adding better
validation for these types.
Rather than having API request types be represented by an enum, which
has to carry associated body data everywhere it's used, it makes more
sense to represent API request types as trait objects. These can have
an associated type for the type of the request body, and this makes it
possible to pass API request types and data around as siblings in a
type-safe way without forcing them into a single value even where it
doesn't make sense. Trait objects also give us dynamic dispatch,
which lets us get rid of several large match blocks.
To keep it possible to fuzz the HTTP API, all the Vmm methods called
by the HTTP API are pulled out into a trait, so the fuzzer can provide
its own stub implementation of the VMM.
Signed-off-by: Alyssa Ross <hi@alyssa.is>
2024-01-05 14:08:53 +00:00
|
|
|
use std::path::PathBuf;
|
2022-07-29 00:07:32 +00:00
|
|
|
use std::sync::mpsc::{channel, Receiver};
|
vmm: use trait objects for API actions
Uses of the old ApiRequest enum conflated two different concerns:
identifying an API request endpoint, and storing data for an API
request. This led to ApiRequest values being passed around with junk
data just to communicate a request type, which forced all API request
body types to implement Default, which in some cases doesn't make any
sense — what's the "default" path for a vhost-user socket? The
nonsensical Default values have led to tests relying on being able to
use nonsensical data, which is an impediment to adding better
validation for these types.
Rather than having API request types be represented by an enum, which
has to carry associated body data everywhere it's used, it makes more
sense to represent API request types as trait objects. These can have
an associated type for the type of the request body, and this makes it
possible to pass API request types and data around as siblings in a
type-safe way without forcing them into a single value even where it
doesn't make sense. Trait objects also give us dynamic dispatch,
which lets us get rid of several large match blocks.
To keep it possible to fuzz the HTTP API, all the Vmm methods called
by the HTTP API are pulled out into a trait, so the fuzzer can provide
its own stub implementation of the VMM.
Signed-off-by: Alyssa Ross <hi@alyssa.is>
2024-01-05 14:08:53 +00:00
|
|
|
use std::sync::{Arc, Mutex};
|
2022-07-29 00:07:32 +00:00
|
|
|
use std::thread;
|
vmm: use trait objects for API actions
Uses of the old ApiRequest enum conflated two different concerns:
identifying an API request endpoint, and storing data for an API
request. This led to ApiRequest values being passed around with junk
data just to communicate a request type, which forced all API request
body types to implement Default, which in some cases doesn't make any
sense — what's the "default" path for a vhost-user socket? The
nonsensical Default values have led to tests relying on being able to
use nonsensical data, which is an impediment to adding better
validation for these types.
Rather than having API request types be represented by an enum, which
has to carry associated body data everywhere it's used, it makes more
sense to represent API request types as trait objects. These can have
an associated type for the type of the request body, and this makes it
possible to pass API request types and data around as siblings in a
type-safe way without forcing them into a single value even where it
doesn't make sense. Trait objects also give us dynamic dispatch,
which lets us get rid of several large match blocks.
To keep it possible to fuzz the HTTP API, all the Vmm methods called
by the HTTP API are pulled out into a trait, so the fuzzer can provide
its own stub implementation of the VMM.
Signed-off-by: Alyssa Ross <hi@alyssa.is>
2024-01-05 14:08:53 +00:00
|
|
|
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::*;
|
2022-07-29 00:07:32 +00:00
|
|
|
use vmm::{EpollContext, EpollDispatch};
|
|
|
|
use vmm_sys_util::eventfd::EventFd;
|
|
|
|
|
|
|
|
// Need to be ordered for test case reproducibility
|
2022-07-29 19:52:45 +00:00
|
|
|
static ROUTES: Lazy<Vec<&Box<dyn EndpointHandler + Sync + Send>>> =
|
|
|
|
Lazy::new(|| HTTP_ROUTES.routes.values().collect());
|
2022-07-29 00:07:32 +00:00
|
|
|
|
|
|
|
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()
|
|
|
|
}
|
|
|
|
|
vmm: use trait objects for API actions
Uses of the old ApiRequest enum conflated two different concerns:
identifying an API request endpoint, and storing data for an API
request. This led to ApiRequest values being passed around with junk
data just to communicate a request type, which forced all API request
body types to implement Default, which in some cases doesn't make any
sense — what's the "default" path for a vhost-user socket? The
nonsensical Default values have led to tests relying on being able to
use nonsensical data, which is an impediment to adding better
validation for these types.
Rather than having API request types be represented by an enum, which
has to carry associated body data everywhere it's used, it makes more
sense to represent API request types as trait objects. These can have
an associated type for the type of the request body, and this makes it
possible to pass API request types and data around as siblings in a
type-safe way without forcing them into a single value even where it
doesn't make sense. Trait objects also give us dynamic dispatch,
which lets us get rid of several large match blocks.
To keep it possible to fuzz the HTTP API, all the Vmm methods called
by the HTTP API are pulled out into a trait, so the fuzzer can provide
its own stub implementation of the VMM.
Signed-off-by: Alyssa Ross <hi@alyssa.is>
2024-01-05 14:08:53 +00:00
|
|
|
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")),
|
2024-01-17 23:36:38 +00:00
|
|
|
firmware: None,
|
|
|
|
cmdline: None,
|
|
|
|
initramfs: None,
|
|
|
|
#[cfg(feature = "igvm")]
|
|
|
|
igvm: None,
|
vmm: use trait objects for API actions
Uses of the old ApiRequest enum conflated two different concerns:
identifying an API request endpoint, and storing data for an API
request. This led to ApiRequest values being passed around with junk
data just to communicate a request type, which forced all API request
body types to implement Default, which in some cases doesn't make any
sense — what's the "default" path for a vhost-user socket? The
nonsensical Default values have led to tests relying on being able to
use nonsensical data, which is an impediment to adding better
validation for these types.
Rather than having API request types be represented by an enum, which
has to carry associated body data everywhere it's used, it makes more
sense to represent API request types as trait objects. These can have
an associated type for the type of the request body, and this makes it
possible to pass API request types and data around as siblings in a
type-safe way without forcing them into a single value even where it
doesn't make sense. Trait objects also give us dynamic dispatch,
which lets us get rid of several large match blocks.
To keep it possible to fuzz the HTTP API, all the Vmm methods called
by the HTTP API are pulled out into a trait, so the fuzzer can provide
its own stub implementation of the VMM.
Signed-off-by: Alyssa Ross <hi@alyssa.is>
2024-01-05 14:08:53 +00:00
|
|
|
}),
|
|
|
|
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,
|
|
|
|
},
|
2024-01-17 21:12:43 +00:00
|
|
|
#[cfg(target_arch = "x86_64")]
|
|
|
|
debug_console: DebugConsoleConfig::default(),
|
vmm: use trait objects for API actions
Uses of the old ApiRequest enum conflated two different concerns:
identifying an API request endpoint, and storing data for an API
request. This led to ApiRequest values being passed around with junk
data just to communicate a request type, which forced all API request
body types to implement Default, which in some cases doesn't make any
sense — what's the "default" path for a vhost-user socket? The
nonsensical Default values have led to tests relying on being able to
use nonsensical data, which is an impediment to adding better
validation for these types.
Rather than having API request types be represented by an enum, which
has to carry associated body data everywhere it's used, it makes more
sense to represent API request types as trait objects. These can have
an associated type for the type of the request body, and this makes it
possible to pass API request types and data around as siblings in a
type-safe way without forcing them into a single value even where it
doesn't make sense. Trait objects also give us dynamic dispatch,
which lets us get rid of several large match blocks.
To keep it possible to fuzz the HTTP API, all the Vmm methods called
by the HTTP API are pulled out into a trait, so the fuzzer can provide
its own stub implementation of the VMM.
Signed-off-by: Alyssa Ross <hi@alyssa.is>
2024-01-05 14:08:53 +00:00
|
|
|
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(())
|
|
|
|
}
|
2023-11-26 10:59:54 +00:00
|
|
|
|
|
|
|
fn vm_nmi(&mut self) -> Result<(), VmError> {
|
|
|
|
Ok(())
|
|
|
|
}
|
vmm: use trait objects for API actions
Uses of the old ApiRequest enum conflated two different concerns:
identifying an API request endpoint, and storing data for an API
request. This led to ApiRequest values being passed around with junk
data just to communicate a request type, which forced all API request
body types to implement Default, which in some cases doesn't make any
sense — what's the "default" path for a vhost-user socket? The
nonsensical Default values have led to tests relying on being able to
use nonsensical data, which is an impediment to adding better
validation for these types.
Rather than having API request types be represented by an enum, which
has to carry associated body data everywhere it's used, it makes more
sense to represent API request types as trait objects. These can have
an associated type for the type of the request body, and this makes it
possible to pass API request types and data around as siblings in a
type-safe way without forcing them into a single value even where it
doesn't make sense. Trait objects also give us dynamic dispatch,
which lets us get rid of several large match blocks.
To keep it possible to fuzz the HTTP API, all the Vmm methods called
by the HTTP API are pulled out into a trait, so the fuzzer can provide
its own stub implementation of the VMM.
Signed-off-by: Alyssa Ross <hi@alyssa.is>
2024-01-05 14:08:53 +00:00
|
|
|
}
|
|
|
|
|
2022-07-29 00:07:32 +00:00
|
|
|
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();
|
vmm: use trait objects for API actions
Uses of the old ApiRequest enum conflated two different concerns:
identifying an API request endpoint, and storing data for an API
request. This led to ApiRequest values being passed around with junk
data just to communicate a request type, which forced all API request
body types to implement Default, which in some cases doesn't make any
sense — what's the "default" path for a vhost-user socket? The
nonsensical Default values have led to tests relying on being able to
use nonsensical data, which is an impediment to adding better
validation for these types.
Rather than having API request types be represented by an enum, which
has to carry associated body data everywhere it's used, it makes more
sense to represent API request types as trait objects. These can have
an associated type for the type of the request body, and this makes it
possible to pass API request types and data around as siblings in a
type-safe way without forcing them into a single value even where it
doesn't make sense. Trait objects also give us dynamic dispatch,
which lets us get rid of several large match blocks.
To keep it possible to fuzz the HTTP API, all the Vmm methods called
by the HTTP API are pulled out into a trait, so the fuzzer can provide
its own stub implementation of the VMM.
Signed-off-by: Alyssa Ross <hi@alyssa.is>
2024-01-05 14:08:53 +00:00
|
|
|
api_request(&mut StubApiRequestHandler).unwrap();
|
2022-07-29 00:07:32 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
_ => {
|
|
|
|
panic!("Unexpected Epoll event");
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|