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