xref: /cloud-hypervisor/fuzz/fuzz_targets/http_api.rs (revision f6cd3bd86ded632da437b6dd6077f4237d2f71fe)
1 // Copyright © 2022 Intel Corporation
2 //
3 // SPDX-License-Identifier: Apache-2.0
4 
5 #![no_main]
6 use libfuzzer_sys::fuzz_target;
7 use micro_http::Request;
8 use once_cell::sync::Lazy;
9 use std::os::unix::io::AsRawFd;
10 use std::path::PathBuf;
11 use std::sync::mpsc::{channel, Receiver};
12 use std::sync::{Arc, Mutex};
13 use std::thread;
14 use vm_migration::MigratableError;
15 use vmm::api::{
16     http::*, ApiRequest, RequestHandler, VmInfoResponse, VmReceiveMigrationData,
17     VmSendMigrationData, VmmPingResponse,
18 };
19 use vmm::config::RestoreConfig;
20 use vmm::vm::{Error as VmError, VmState};
21 use vmm::vm_config::*;
22 use vmm::{EpollContext, EpollDispatch};
23 use vmm_sys_util::eventfd::EventFd;
24 
25 // Need to be ordered for test case reproducibility
26 static ROUTES: Lazy<Vec<&Box<dyn EndpointHandler + Sync + Send>>> =
27     Lazy::new(|| HTTP_ROUTES.routes.values().collect());
28 
29 fuzz_target!(|bytes| {
30     if bytes.len() < 2 {
31         return;
32     }
33 
34     let route = ROUTES[bytes[0] as usize % ROUTES.len()];
35     if let Some(request) = generate_request(&bytes[1..]) {
36         let exit_evt = EventFd::new(libc::EFD_NONBLOCK).unwrap();
37         let api_evt = EventFd::new(libc::EFD_NONBLOCK).unwrap();
38         let (api_sender, api_receiver) = channel();
39 
40         let http_receiver_thread = {
41             let exit_evt = exit_evt.try_clone().unwrap();
42             let api_evt = api_evt.try_clone().unwrap();
43             thread::Builder::new()
44                 .name("http_receiver".to_string())
45                 .spawn(move || {
46                     http_receiver_stub(exit_evt, api_evt, api_receiver);
47                 })
48                 .unwrap()
49         };
50 
51         route.handle_request(&request, api_evt, api_sender);
52         exit_evt.write(1).ok();
53         http_receiver_thread.join().unwrap();
54     };
55 });
56 
57 fn generate_request(bytes: &[u8]) -> Option<Request> {
58     let req_method = match bytes[0] % 5 {
59         0 => "GET",
60         1 => "PUT",
61         2 => "PATCH",
62         3 => "POST",
63         _ => "INVALID",
64     };
65     let request_line = format!("{} http://localhost/home HTTP/1.1\r\n", req_method);
66 
67     let req_body = &bytes[1..];
68     let request = if req_body.len() > 0 {
69         [
70             format!("{}Content-Length: {}\r\n", request_line, req_body.len()).as_bytes(),
71             req_body,
72         ]
73         .concat()
74     } else {
75         format!("{}\r\n", request_line).as_bytes().to_vec()
76     };
77 
78     Request::try_from(&request, None).ok()
79 }
80 
81 struct StubApiRequestHandler;
82 
83 impl RequestHandler for StubApiRequestHandler {
84     fn vm_create(&mut self, _: Arc<Mutex<VmConfig>>) -> Result<(), VmError> {
85         Ok(())
86     }
87 
88     fn vm_boot(&mut self) -> Result<(), VmError> {
89         Ok(())
90     }
91 
92     fn vm_pause(&mut self) -> Result<(), VmError> {
93         Ok(())
94     }
95 
96     fn vm_resume(&mut self) -> Result<(), VmError> {
97         Ok(())
98     }
99 
100     fn vm_snapshot(&mut self, _: &str) -> Result<(), VmError> {
101         Ok(())
102     }
103 
104     fn vm_restore(&mut self, _: RestoreConfig) -> Result<(), VmError> {
105         Ok(())
106     }
107 
108     #[cfg(all(target_arch = "x86_64", feature = "guest_debug"))]
109     fn vm_coredump(&mut self, _: &str) -> Result<(), VmError> {
110         Ok(())
111     }
112 
113     fn vm_shutdown(&mut self) -> Result<(), VmError> {
114         Ok(())
115     }
116 
117     fn vm_reboot(&mut self) -> Result<(), VmError> {
118         Ok(())
119     }
120 
121     fn vm_info(&self) -> Result<VmInfoResponse, VmError> {
122         Ok(VmInfoResponse {
123             config: Arc::new(Mutex::new(VmConfig {
124                 cpus: CpusConfig {
125                     boot_vcpus: 1,
126                     max_vcpus: 1,
127                     topology: None,
128                     kvm_hyperv: false,
129                     max_phys_bits: 46,
130                     affinity: None,
131                     features: CpuFeatures::default(),
132                 },
133                 memory: MemoryConfig {
134                     size: 536_870_912,
135                     mergeable: false,
136                     hotplug_method: HotplugMethod::Acpi,
137                     hotplug_size: None,
138                     hotplugged_size: None,
139                     shared: false,
140                     hugepages: false,
141                     hugepage_size: None,
142                     prefault: false,
143                     zones: None,
144                     thp: true,
145                 },
146                 payload: Some(PayloadConfig {
147                     kernel: Some(PathBuf::from("/path/to/kernel")),
148                     firmware: None,
149                     cmdline: None,
150                     initramfs: None,
151                     #[cfg(feature = "igvm")]
152                     igvm: None,
153                 }),
154                 rate_limit_groups: None,
155                 disks: None,
156                 net: None,
157                 rng: RngConfig {
158                     src: PathBuf::from("/dev/urandom"),
159                     iommu: false,
160                 },
161                 balloon: None,
162                 fs: None,
163                 pmem: None,
164                 serial: ConsoleConfig {
165                     file: None,
166                     mode: ConsoleOutputMode::Null,
167                     iommu: false,
168                     socket: None,
169                 },
170                 console: ConsoleConfig {
171                     file: None,
172                     mode: ConsoleOutputMode::Tty,
173                     iommu: false,
174                     socket: None,
175                 },
176                 #[cfg(target_arch = "x86_64")]
177                 debug_console: DebugConsoleConfig::default(),
178                 devices: None,
179                 user_devices: None,
180                 vdpa: None,
181                 vsock: None,
182                 pvpanic: false,
183                 iommu: false,
184                 #[cfg(target_arch = "x86_64")]
185                 sgx_epc: None,
186                 numa: None,
187                 watchdog: false,
188                 #[cfg(feature = "guest_debug")]
189                 gdb: false,
190                 pci_segments: None,
191                 platform: None,
192                 tpm: None,
193                 preserved_fds: None,
194             })),
195             state: VmState::Running,
196             memory_actual_size: 0,
197             device_tree: None,
198         })
199     }
200 
201     fn vmm_ping(&self) -> VmmPingResponse {
202         VmmPingResponse {
203             build_version: String::new(),
204             version: String::new(),
205             pid: 0,
206             features: Vec::new(),
207         }
208     }
209 
210     fn vm_delete(&mut self) -> Result<(), VmError> {
211         Ok(())
212     }
213 
214     fn vmm_shutdown(&mut self) -> Result<(), VmError> {
215         Ok(())
216     }
217 
218     fn vm_resize(&mut self, _: Option<u8>, _: Option<u64>, _: Option<u64>) -> Result<(), VmError> {
219         Ok(())
220     }
221 
222     fn vm_resize_zone(&mut self, _: String, _: u64) -> Result<(), VmError> {
223         Ok(())
224     }
225 
226     fn vm_add_device(&mut self, _: DeviceConfig) -> Result<Option<Vec<u8>>, VmError> {
227         Ok(None)
228     }
229 
230     fn vm_add_user_device(&mut self, _: UserDeviceConfig) -> Result<Option<Vec<u8>>, VmError> {
231         Ok(None)
232     }
233 
234     fn vm_remove_device(&mut self, _: String) -> Result<(), VmError> {
235         Ok(())
236     }
237 
238     fn vm_add_disk(&mut self, _: DiskConfig) -> Result<Option<Vec<u8>>, VmError> {
239         Ok(None)
240     }
241 
242     fn vm_add_fs(&mut self, _: FsConfig) -> Result<Option<Vec<u8>>, VmError> {
243         Ok(None)
244     }
245 
246     fn vm_add_pmem(&mut self, _: PmemConfig) -> Result<Option<Vec<u8>>, VmError> {
247         Ok(None)
248     }
249 
250     fn vm_add_net(&mut self, _: NetConfig) -> Result<Option<Vec<u8>>, VmError> {
251         Ok(None)
252     }
253 
254     fn vm_add_vdpa(&mut self, _: VdpaConfig) -> Result<Option<Vec<u8>>, VmError> {
255         Ok(None)
256     }
257 
258     fn vm_add_vsock(&mut self, _: VsockConfig) -> Result<Option<Vec<u8>>, VmError> {
259         Ok(None)
260     }
261 
262     fn vm_counters(&mut self) -> Result<Option<Vec<u8>>, VmError> {
263         Ok(None)
264     }
265 
266     fn vm_power_button(&mut self) -> Result<(), VmError> {
267         Ok(())
268     }
269 
270     fn vm_receive_migration(&mut self, _: VmReceiveMigrationData) -> Result<(), MigratableError> {
271         Ok(())
272     }
273 
274     fn vm_send_migration(&mut self, _: VmSendMigrationData) -> Result<(), MigratableError> {
275         Ok(())
276     }
277 
278     fn vm_nmi(&mut self) -> Result<(), VmError> {
279         Ok(())
280     }
281 }
282 
283 fn http_receiver_stub(exit_evt: EventFd, api_evt: EventFd, api_receiver: Receiver<ApiRequest>) {
284     let mut epoll = EpollContext::new().unwrap();
285     epoll.add_event(&exit_evt, EpollDispatch::Exit).unwrap();
286     epoll.add_event(&api_evt, EpollDispatch::Api).unwrap();
287 
288     let epoll_fd = epoll.as_raw_fd();
289     let mut events = vec![epoll::Event::new(epoll::Events::empty(), 0); 2];
290     let num_events;
291     loop {
292         num_events = match epoll::wait(epoll_fd, -1, &mut events[..]) {
293             Ok(num_events) => num_events,
294             Err(e) => match e.raw_os_error() {
295                 Some(libc::EAGAIN) | Some(libc::EINTR) => continue,
296                 _ => panic!("Unexpected epoll::wait error!"),
297             },
298         };
299 
300         break;
301     }
302 
303     for event in events.iter().take(num_events) {
304         let dispatch_event: EpollDispatch = event.data.into();
305         match dispatch_event {
306             EpollDispatch::Exit => {
307                 break;
308             }
309             EpollDispatch::Api => {
310                 for _ in 0..api_evt.read().unwrap() {
311                     let api_request = api_receiver.recv().unwrap();
312                     api_request(&mut StubApiRequestHandler).unwrap();
313                 }
314             }
315             _ => {
316                 panic!("Unexpected Epoll event");
317             }
318         }
319     }
320 }
321