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