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