xref: /cloud-hypervisor/fuzz/fuzz_targets/http_api.rs (revision 3ce0fef7fd546467398c914dbc74d8542e45cf6f)
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                 devices: None,
177                 user_devices: None,
178                 vdpa: None,
179                 vsock: None,
180                 pvpanic: false,
181                 iommu: false,
182                 #[cfg(target_arch = "x86_64")]
183                 sgx_epc: None,
184                 numa: None,
185                 watchdog: false,
186                 #[cfg(feature = "guest_debug")]
187                 gdb: false,
188                 platform: None,
189                 tpm: None,
190                 preserved_fds: None,
191             })),
192             state: VmState::Running,
193             memory_actual_size: 0,
194             device_tree: None,
195         })
196     }
197 
198     fn vmm_ping(&self) -> VmmPingResponse {
199         VmmPingResponse {
200             build_version: String::new(),
201             version: String::new(),
202             pid: 0,
203             features: Vec::new(),
204         }
205     }
206 
207     fn vm_delete(&mut self) -> Result<(), VmError> {
208         Ok(())
209     }
210 
211     fn vmm_shutdown(&mut self) -> Result<(), VmError> {
212         Ok(())
213     }
214 
215     fn vm_resize(&mut self, _: Option<u8>, _: Option<u64>, _: Option<u64>) -> Result<(), VmError> {
216         Ok(())
217     }
218 
219     fn vm_resize_zone(&mut self, _: String, _: u64) -> Result<(), VmError> {
220         Ok(())
221     }
222 
223     fn vm_add_device(&mut self, _: DeviceConfig) -> Result<Option<Vec<u8>>, VmError> {
224         Ok(None)
225     }
226 
227     fn vm_add_user_device(&mut self, _: UserDeviceConfig) -> Result<Option<Vec<u8>>, VmError> {
228         Ok(None)
229     }
230 
231     fn vm_remove_device(&mut self, _: String) -> Result<(), VmError> {
232         Ok(())
233     }
234 
235     fn vm_add_disk(&mut self, _: DiskConfig) -> Result<Option<Vec<u8>>, VmError> {
236         Ok(None)
237     }
238 
239     fn vm_add_fs(&mut self, _: FsConfig) -> Result<Option<Vec<u8>>, VmError> {
240         Ok(None)
241     }
242 
243     fn vm_add_pmem(&mut self, _: PmemConfig) -> Result<Option<Vec<u8>>, VmError> {
244         Ok(None)
245     }
246 
247     fn vm_add_net(&mut self, _: NetConfig) -> Result<Option<Vec<u8>>, VmError> {
248         Ok(None)
249     }
250 
251     fn vm_add_vdpa(&mut self, _: VdpaConfig) -> Result<Option<Vec<u8>>, VmError> {
252         Ok(None)
253     }
254 
255     fn vm_add_vsock(&mut self, _: VsockConfig) -> Result<Option<Vec<u8>>, VmError> {
256         Ok(None)
257     }
258 
259     fn vm_counters(&mut self) -> Result<Option<Vec<u8>>, VmError> {
260         Ok(None)
261     }
262 
263     fn vm_power_button(&mut self) -> Result<(), VmError> {
264         Ok(())
265     }
266 
267     fn vm_receive_migration(&mut self, _: VmReceiveMigrationData) -> Result<(), MigratableError> {
268         Ok(())
269     }
270 
271     fn vm_send_migration(&mut self, _: VmSendMigrationData) -> Result<(), MigratableError> {
272         Ok(())
273     }
274 }
275 
276 fn http_receiver_stub(exit_evt: EventFd, api_evt: EventFd, api_receiver: Receiver<ApiRequest>) {
277     let mut epoll = EpollContext::new().unwrap();
278     epoll.add_event(&exit_evt, EpollDispatch::Exit).unwrap();
279     epoll.add_event(&api_evt, EpollDispatch::Api).unwrap();
280 
281     let epoll_fd = epoll.as_raw_fd();
282     let mut events = vec![epoll::Event::new(epoll::Events::empty(), 0); 2];
283     let num_events;
284     loop {
285         num_events = match epoll::wait(epoll_fd, -1, &mut events[..]) {
286             Ok(num_events) => num_events,
287             Err(e) => match e.raw_os_error() {
288                 Some(libc::EAGAIN) | Some(libc::EINTR) => continue,
289                 _ => panic!("Unexpected epoll::wait error!"),
290             },
291         };
292 
293         break;
294     }
295 
296     for event in events.iter().take(num_events) {
297         let dispatch_event: EpollDispatch = event.data.into();
298         match dispatch_event {
299             EpollDispatch::Exit => {
300                 break;
301             }
302             EpollDispatch::Api => {
303                 for _ in 0..api_evt.read().unwrap() {
304                     let api_request = api_receiver.recv().unwrap();
305                     api_request(&mut StubApiRequestHandler).unwrap();
306                 }
307             }
308             _ => {
309                 panic!("Unexpected Epoll event");
310             }
311         }
312     }
313 }
314