2022-07-29 00:07:32 +00:00
|
|
|
// Copyright © 2022 Intel Corporation
|
|
|
|
//
|
|
|
|
// SPDX-License-Identifier: Apache-2.0
|
|
|
|
|
|
|
|
#![no_main]
|
|
|
|
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};
|
|
|
|
use std::thread;
|
2024-09-28 09:01:35 +00:00
|
|
|
|
|
|
|
use libfuzzer_sys::fuzz_target;
|
|
|
|
use micro_http::Request;
|
|
|
|
use once_cell::sync::Lazy;
|
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;
|
2024-09-29 15:22:27 +00:00
|
|
|
use vmm::api::http::*;
|
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 vmm::api::{
|
2024-09-29 15:22:27 +00:00
|
|
|
ApiRequest, RequestHandler, VmInfoResponse, VmReceiveMigrationData, VmSendMigrationData,
|
|
|
|
VmmPingResponse,
|
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 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 {
|
2024-09-18 12:06:22 +00:00
|
|
|
fn vm_create(&mut self, _: Box<VmConfig>) -> Result<(), VmError> {
|
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
|
|
|
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(())
|
|
|
|
}
|
|
|
|
|
2024-05-07 11:00:03 +00:00
|
|
|
#[cfg(target_arch = "x86_64")]
|
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
|
|
|
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 {
|
2024-09-18 12:06:22 +00:00
|
|
|
config: Box::new(VmConfig {
|
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
|
|
|
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,
|
devices: Add pvmemcontrol device
Pvmemcontrol provides a way for the guest to control its physical memory
properties, and enables optimizations and security features. For
example, the guest can provide information to the host where parts of a
hugepage may be unbacked, or sensitive data may not be swapped out, etc.
Pvmemcontrol allows guests to manipulate its gPTE entries in the SLAT,
and also some other properties of the memory map the back's host memory.
This is achieved by using the KVM_CAP_SYNC_MMU capability. When this
capability is available, the changes in the backing of the memory region
on the host are automatically reflected into the guest. For example, an
mmap() or madvise() that affects the region will be made visible
immediately.
There are two components of the implementation: the guest Linux driver
and Virtual Machine Monitor (VMM) device. A guest-allocated shared
buffer is negotiated per-cpu through a few PCI MMIO registers, the VMM
device assigns a unique command for each per-cpu buffer. The guest
writes its pvmemcontrol request in the per-cpu buffer, then writes the
corresponding command into the command register, calling into the VMM
device to perform the pvmemcontrol request.
The synchronous per-cpu shared buffer approach avoids the kick and busy
waiting that the guest would have to do with virtio virtqueue transport.
The Cloud Hypervisor component can be enabled with --pvmemcontrol.
Co-developed-by: Stanko Novakovic <stanko@google.com>
Co-developed-by: Pasha Tatashin <tatashin@google.com>
Signed-off-by: Yuanchu Xie <yuanchu@google.com>
2023-10-25 22:46:47 +00:00
|
|
|
#[cfg(feature = "pvmemcontrol")]
|
|
|
|
pvmemcontrol: 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
|
|
|
iommu: false,
|
|
|
|
#[cfg(target_arch = "x86_64")]
|
|
|
|
sgx_epc: None,
|
|
|
|
numa: None,
|
|
|
|
watchdog: false,
|
|
|
|
gdb: false,
|
2024-04-14 19:50:45 +00:00
|
|
|
pci_segments: 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
|
|
|
platform: None,
|
|
|
|
tpm: None,
|
|
|
|
preserved_fds: None,
|
2024-07-30 14:58:50 +00:00
|
|
|
landlock_enable: false,
|
2024-08-01 17:38:20 +00:00
|
|
|
landlock_rules: None,
|
2024-09-18 12:06:22 +00:00
|
|
|
}),
|
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
|
|
|
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");
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|