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