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