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