mirror of
https://github.com/cloud-hypervisor/cloud-hypervisor.git
synced 2024-10-01 02:55:45 +00:00
fuzz: Add fuzzer for HTTP API
Fuzz the HTTP API handling code with a minimum HTTP API receiver (e.g. mocking the behavior of our "vmm" thread). Signed-off-by: Bo Chen <chen.bo@intel.com>
This commit is contained in:
parent
eb056d374a
commit
e5155bab62
6
fuzz/Cargo.lock
generated
6
fuzz/Cargo.lock
generated
@ -179,8 +179,11 @@ dependencies = [
|
|||||||
"block_util",
|
"block_util",
|
||||||
"cloud-hypervisor",
|
"cloud-hypervisor",
|
||||||
"devices",
|
"devices",
|
||||||
|
"epoll",
|
||||||
"libc",
|
"libc",
|
||||||
"libfuzzer-sys",
|
"libfuzzer-sys",
|
||||||
|
"micro_http",
|
||||||
|
"once_cell",
|
||||||
"qcow",
|
"qcow",
|
||||||
"seccompiler",
|
"seccompiler",
|
||||||
"vhdx",
|
"vhdx",
|
||||||
@ -189,6 +192,7 @@ dependencies = [
|
|||||||
"vm-device",
|
"vm-device",
|
||||||
"vm-memory",
|
"vm-memory",
|
||||||
"vm-virtio",
|
"vm-virtio",
|
||||||
|
"vmm",
|
||||||
"vmm-sys-util",
|
"vmm-sys-util",
|
||||||
]
|
]
|
||||||
|
|
||||||
@ -444,7 +448,7 @@ dependencies = [
|
|||||||
[[package]]
|
[[package]]
|
||||||
name = "micro_http"
|
name = "micro_http"
|
||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
source = "git+https://github.com/firecracker-microvm/micro-http?branch=main#9517a300370a158a7af0996b7eebf040d171e1a4"
|
source = "git+https://github.com/firecracker-microvm/micro-http?branch=main#863b0370ba7e57f7df5b908ada9e5b44809ccae9"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"libc",
|
"libc",
|
||||||
"vmm-sys-util",
|
"vmm-sys-util",
|
||||||
|
@ -11,13 +11,17 @@ cargo-fuzz = true
|
|||||||
[dependencies]
|
[dependencies]
|
||||||
block_util = { path = "../block_util" }
|
block_util = { path = "../block_util" }
|
||||||
devices = { path = "../devices" }
|
devices = { path = "../devices" }
|
||||||
|
epoll = "4.3.1"
|
||||||
libc = "0.2.126"
|
libc = "0.2.126"
|
||||||
libfuzzer-sys = "0.4.3"
|
libfuzzer-sys = "0.4.3"
|
||||||
|
micro_http = { git = "https://github.com/firecracker-microvm/micro-http", branch = "main" }
|
||||||
|
once_cell = "1.13.0"
|
||||||
qcow = { path = "../qcow" }
|
qcow = { path = "../qcow" }
|
||||||
seccompiler = "0.2.0"
|
seccompiler = "0.2.0"
|
||||||
vhdx = { path = "../vhdx" }
|
vhdx = { path = "../vhdx" }
|
||||||
virtio-devices = { path = "../virtio-devices" }
|
virtio-devices = { path = "../virtio-devices" }
|
||||||
virtio-queue = "0.5.0"
|
virtio-queue = "0.5.0"
|
||||||
|
vmm = { path = "../vmm" }
|
||||||
vmm-sys-util = "0.10.0"
|
vmm-sys-util = "0.10.0"
|
||||||
vm-memory = "0.8.0"
|
vm-memory = "0.8.0"
|
||||||
vm-device = { path = "../vm-device" }
|
vm-device = { path = "../vm-device" }
|
||||||
@ -47,6 +51,12 @@ path = "fuzz_targets/cmos.rs"
|
|||||||
test = false
|
test = false
|
||||||
doc = false
|
doc = false
|
||||||
|
|
||||||
|
[[bin]]
|
||||||
|
name = "http_api"
|
||||||
|
path = "fuzz_targets/http_api.rs"
|
||||||
|
test = false
|
||||||
|
doc = false
|
||||||
|
|
||||||
[[bin]]
|
[[bin]]
|
||||||
name = "qcow"
|
name = "qcow"
|
||||||
path = "fuzz_targets/qcow.rs"
|
path = "fuzz_targets/qcow.rs"
|
||||||
@ -64,4 +74,3 @@ name = "vhdx"
|
|||||||
path = "fuzz_targets/vhdx.rs"
|
path = "fuzz_targets/vhdx.rs"
|
||||||
test = false
|
test = false
|
||||||
doc = false
|
doc = false
|
||||||
|
|
||||||
|
196
fuzz/fuzz_targets/http_api.rs
Normal file
196
fuzz/fuzz_targets/http_api.rs
Normal file
@ -0,0 +1,196 @@
|
|||||||
|
// 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::sync::mpsc::{channel, Receiver};
|
||||||
|
use std::thread;
|
||||||
|
use vmm::api::{http::*, ApiRequest, ApiResponsePayload};
|
||||||
|
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(|| {
|
||||||
|
let mut keys: Vec<&String> = HTTP_ROUTES.routes.keys().collect();
|
||||||
|
keys.sort();
|
||||||
|
keys.iter()
|
||||||
|
.map(|k| HTTP_ROUTES.routes.get(*k).unwrap())
|
||||||
|
.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()
|
||||||
|
}
|
||||||
|
|
||||||
|
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();
|
||||||
|
match api_request {
|
||||||
|
ApiRequest::VmCreate(_, sender) => {
|
||||||
|
sender.send(Ok(ApiResponsePayload::Empty)).unwrap();
|
||||||
|
}
|
||||||
|
ApiRequest::VmDelete(sender) => {
|
||||||
|
sender.send(Ok(ApiResponsePayload::Empty)).unwrap();
|
||||||
|
}
|
||||||
|
ApiRequest::VmBoot(sender) => {
|
||||||
|
sender.send(Ok(ApiResponsePayload::Empty)).unwrap();
|
||||||
|
}
|
||||||
|
ApiRequest::VmShutdown(sender) => {
|
||||||
|
sender.send(Ok(ApiResponsePayload::Empty)).unwrap();
|
||||||
|
}
|
||||||
|
ApiRequest::VmReboot(sender) => {
|
||||||
|
sender.send(Ok(ApiResponsePayload::Empty)).unwrap();
|
||||||
|
}
|
||||||
|
ApiRequest::VmInfo(sender) => {
|
||||||
|
sender.send(Ok(ApiResponsePayload::Empty)).unwrap();
|
||||||
|
}
|
||||||
|
ApiRequest::VmmPing(sender) => {
|
||||||
|
sender.send(Ok(ApiResponsePayload::Empty)).unwrap();
|
||||||
|
}
|
||||||
|
ApiRequest::VmPause(sender) => {
|
||||||
|
sender.send(Ok(ApiResponsePayload::Empty)).unwrap();
|
||||||
|
}
|
||||||
|
ApiRequest::VmResume(sender) => {
|
||||||
|
sender.send(Ok(ApiResponsePayload::Empty)).unwrap();
|
||||||
|
}
|
||||||
|
ApiRequest::VmSnapshot(_, sender) => {
|
||||||
|
sender.send(Ok(ApiResponsePayload::Empty)).unwrap();
|
||||||
|
}
|
||||||
|
ApiRequest::VmRestore(_, sender) => {
|
||||||
|
sender.send(Ok(ApiResponsePayload::Empty)).unwrap();
|
||||||
|
}
|
||||||
|
ApiRequest::VmmShutdown(sender) => {
|
||||||
|
sender.send(Ok(ApiResponsePayload::Empty)).unwrap();
|
||||||
|
}
|
||||||
|
ApiRequest::VmResize(_, sender) => {
|
||||||
|
sender.send(Ok(ApiResponsePayload::Empty)).unwrap();
|
||||||
|
}
|
||||||
|
ApiRequest::VmResizeZone(_, sender) => {
|
||||||
|
sender.send(Ok(ApiResponsePayload::Empty)).unwrap();
|
||||||
|
}
|
||||||
|
ApiRequest::VmAddDevice(_, sender) => {
|
||||||
|
sender.send(Ok(ApiResponsePayload::Empty)).unwrap();
|
||||||
|
}
|
||||||
|
ApiRequest::VmAddUserDevice(_, sender) => {
|
||||||
|
sender.send(Ok(ApiResponsePayload::Empty)).unwrap();
|
||||||
|
}
|
||||||
|
ApiRequest::VmRemoveDevice(_, sender) => {
|
||||||
|
sender.send(Ok(ApiResponsePayload::Empty)).unwrap();
|
||||||
|
}
|
||||||
|
ApiRequest::VmAddDisk(_, sender) => {
|
||||||
|
sender.send(Ok(ApiResponsePayload::Empty)).unwrap();
|
||||||
|
}
|
||||||
|
ApiRequest::VmAddFs(_, sender) => {
|
||||||
|
sender.send(Ok(ApiResponsePayload::Empty)).unwrap();
|
||||||
|
}
|
||||||
|
ApiRequest::VmAddPmem(_, sender) => {
|
||||||
|
sender.send(Ok(ApiResponsePayload::Empty)).unwrap();
|
||||||
|
}
|
||||||
|
ApiRequest::VmAddNet(_, sender) => {
|
||||||
|
sender.send(Ok(ApiResponsePayload::Empty)).unwrap();
|
||||||
|
}
|
||||||
|
ApiRequest::VmAddVdpa(_, sender) => {
|
||||||
|
sender.send(Ok(ApiResponsePayload::Empty)).unwrap();
|
||||||
|
}
|
||||||
|
ApiRequest::VmAddVsock(_, sender) => {
|
||||||
|
sender.send(Ok(ApiResponsePayload::Empty)).unwrap();
|
||||||
|
}
|
||||||
|
ApiRequest::VmCounters(sender) => {
|
||||||
|
sender.send(Ok(ApiResponsePayload::Empty)).unwrap();
|
||||||
|
}
|
||||||
|
ApiRequest::VmReceiveMigration(_, sender) => {
|
||||||
|
sender.send(Ok(ApiResponsePayload::Empty)).unwrap();
|
||||||
|
}
|
||||||
|
ApiRequest::VmSendMigration(_, sender) => {
|
||||||
|
sender.send(Ok(ApiResponsePayload::Empty)).unwrap();
|
||||||
|
}
|
||||||
|
ApiRequest::VmPowerButton(sender) => {
|
||||||
|
sender.send(Ok(ApiResponsePayload::Empty)).unwrap();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
_ => {
|
||||||
|
panic!("Unexpected Epoll event");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in New Issue
Block a user