diff --git a/src/firecracker/src/api_server_adapter.rs b/src/firecracker/src/api_server_adapter.rs index 70abff48ce8..efed97f1691 100644 --- a/src/firecracker/src/api_server_adapter.rs +++ b/src/firecracker/src/api_server_adapter.rs @@ -57,12 +57,13 @@ impl ApiServerAdapter { to_api: Sender, vmm: Arc>, event_manager: &mut EventManager, + seccomp_filters: &BpfThreadMap, ) -> Result<(), ApiServerError> { let api_adapter = Arc::new(Mutex::new(Self { api_event_fd, from_api, to_api, - controller: RuntimeApiController::new(vmm.clone()), + controller: RuntimeApiController::new(vmm.clone(), seccomp_filters), request: None, })); event_manager.add_subscriber(api_adapter.clone()); @@ -262,7 +263,14 @@ pub(crate) fn run_with_api( .expect("Poisoned lock") .start(super::metrics::WRITE_METRICS_PERIOD_MS); - ApiServerAdapter::run_microvm(api_event_fd, from_api, to_api, vmm, &mut event_manager) + ApiServerAdapter::run_microvm( + api_event_fd, + from_api, + to_api, + vmm, + &mut event_manager, + seccomp_filters, + ) }); api_kill_switch.write(1).unwrap(); diff --git a/src/vmm/src/builder.rs b/src/vmm/src/builder.rs index 28d38af2b2d..31226d6539d 100644 --- a/src/vmm/src/builder.rs +++ b/src/vmm/src/builder.rs @@ -29,6 +29,7 @@ use crate::device_manager::{ DeviceRestoreArgs, }; use crate::devices::virtio::balloon::Balloon; +use crate::devices::virtio::block::BlockError; use crate::devices::virtio::block::device::Block; use crate::devices::virtio::device::VirtioDevice; use crate::devices::virtio::mem::{VIRTIO_MEM_DEFAULT_SLOT_SIZE_MIB, VirtioMem}; @@ -109,6 +110,8 @@ pub enum StartMicrovmError { NetDeviceNotConfigured, /// Cannot open the block device backing file: {0} OpenBlockDevice(io::Error), + /// Failed to spawn the block worker thread: {0} + SpawnBlockWorker(BlockError), /// Cannot restore microvm state: {0} RestoreMicrovmState(MicrovmStateError), /// Cannot set vm resources: {0} @@ -238,6 +241,7 @@ pub fn build_microvm_for_boot( &mut boot_cmdline, vm_resources.block.devices.iter(), event_manager, + seccomp_filters, )?; attach_net_devices( &mut device_manager, @@ -502,6 +506,7 @@ pub fn build_microvm_from_snapshot( vm_resources, instance_id: &instance_info.id, vcpus_exit_evt: kvm_vm.vcpus_exit_evt(), + seccomp_filters, }; #[allow(unused_mut)] let mut device_manager = @@ -659,6 +664,7 @@ fn attach_block_devices<'a, I: Iterator>> + Debug>( cmdline: &mut LoaderKernelCmdline, blocks: I, event_manager: &mut EventManager, + seccomp_filters: &BpfThreadMap, ) -> Result<(), StartMicrovmError> { for block in blocks { let (id, is_vhost_user) = { @@ -672,6 +678,13 @@ fn attach_block_devices<'a, I: Iterator>> + Debug>( } (locked.id().to_string(), locked.is_vhost_user()) }; + + block + .lock() + .expect("Poisoned lock") + .spawn_worker(seccomp_filters.get("blk_worker").cloned()) + .map_err(StartMicrovmError::SpawnBlockWorker)?; + // The device mutex mustn't be locked here otherwise it will deadlock. device_manager.attach_boot_virtio_device( vm, @@ -778,6 +791,7 @@ pub(crate) mod tests { use crate::devices::virtio::vsock::VSOCK_DEV_ID; use crate::mmds::data_store::{Mmds, MmdsVersion}; use crate::mmds::ns::MmdsNetworkStack; + use crate::seccomp::get_empty_filters; use crate::utils::mib_to_bytes; use crate::vmm_config::balloon::{BALLOON_DEV_ID, BalloonBuilder, BalloonDeviceConfig}; use crate::vmm_config::boot_source::BootSourceConfig; @@ -917,6 +931,7 @@ pub(crate) mod tests { cmdline, block_dev_configs.devices.iter(), event_manager, + &get_empty_filters(), ) .unwrap(); block_files diff --git a/src/vmm/src/device_manager/mod.rs b/src/vmm/src/device_manager/mod.rs index 75a00b53cdd..d48ae07191d 100644 --- a/src/vmm/src/device_manager/mod.rs +++ b/src/vmm/src/device_manager/mod.rs @@ -51,6 +51,7 @@ use crate::logger::{error, info}; use crate::rate_limiter::TokenBucket; use crate::resources::VmResources; use crate::rpc_interface::VmmActionError; +use crate::seccomp::BpfThreadMap; use crate::snapshot::Persist; use crate::utils::open_file_nonblock; use crate::vmm_config::HotplugDeviceConfig; @@ -458,6 +459,7 @@ impl DeviceManager { vm: Arc, config: HotplugDeviceConfig, event_manager: &mut EventManager, + seccomp_filters: &BpfThreadMap, ) -> Result<(), VmmActionError> { let dev_type = config.device_type(); let dev_id = config.device_id().to_string(); @@ -473,7 +475,7 @@ impl DeviceManager { } let device = match config { - HotplugDeviceConfig::Block(cfg) => Self::hotplug_make_block(cfg)?, + HotplugDeviceConfig::Block(cfg) => Self::hotplug_make_block(cfg, seccomp_filters)?, HotplugDeviceConfig::Pmem(cfg) => Self::hotplug_make_pmem(vm.clone(), cfg)?, HotplugDeviceConfig::Net(cfg) => self.hotplug_make_net(cfg)?, }; @@ -488,12 +490,17 @@ impl DeviceManager { fn hotplug_make_block( config: BlockDeviceConfig, + seccomp_filters: &BpfThreadMap, ) -> Result>, VmmActionError> { if config.is_root_device { return Err(DriveError::RootBlockDeviceAlreadyAdded.into()); } - let block = Block::new(config).map_err(DriveError::CreateBlockDevice)?; + let mut block = Block::new(config).map_err(DriveError::CreateBlockDevice)?; + block + .spawn_worker(seccomp_filters.get("blk_worker").cloned()) + .map_err(DriveError::CreateBlockDevice)?; + Ok(Arc::new(Mutex::new(block))) } @@ -673,6 +680,7 @@ pub struct DeviceRestoreArgs<'a> { pub vcpus_exit_evt: &'a EventFd, pub vm_resources: &'a mut VmResources, pub instance_id: &'a str, + pub seccomp_filters: &'a BpfThreadMap, } impl std::fmt::Debug for DeviceRestoreArgs<'_> { @@ -745,6 +753,7 @@ impl<'a> Persist<'a> for DeviceManager { vm_resources: constructor_args.vm_resources, instance_id: constructor_args.instance_id, event_manager: constructor_args.event_manager, + seccomp_filters: constructor_args.seccomp_filters, }; let pci_devices = PciDevices::restore(pci_ctor_args, pci_state) .map_err(DeviceManagerPersistError::PciRestore)?; @@ -757,6 +766,7 @@ impl<'a> Persist<'a> for DeviceManager { event_manager: constructor_args.event_manager, vm_resources: constructor_args.vm_resources, instance_id: constructor_args.instance_id, + seccomp_filters: constructor_args.seccomp_filters, }; let mmio_virtio_devices = MMIOVirtioDevices::restore(mmio_ctor_args, mmio_state) .map_err(DeviceManagerPersistError::MmioRestore)?; @@ -787,6 +797,7 @@ pub(crate) mod tests { use crate::devices::acpi::vmgenid::VmGenId; use crate::devices::virtio::block::CacheType; use crate::rpc_interface::VmmActionError; + use crate::seccomp::get_empty_filters; use crate::vmm_config::HotplugDeviceConfig; use crate::vmm_config::drive::{BlockDeviceConfig, DriveError}; use crate::vmm_config::net::{NetworkInterfaceConfig, NetworkInterfaceError}; @@ -920,12 +931,14 @@ pub(crate) mod tests { #[test] fn test_hotplug_block() { let mut evt_manager = EventManager::new().unwrap(); + let seccomp_filters = get_empty_filters(); let mut vmm = default_vmm_with_pci(); let f = TempFile::new().unwrap(); // Successful case let cfg = HotplugDeviceConfig::Block(make_hotplug_block_cfg("block0", &f, false)); - vmm.hotplug_device(cfg, &mut evt_manager).unwrap(); + vmm.hotplug_device(cfg, &mut evt_manager, &seccomp_filters) + .unwrap(); assert!( pci_devices(&vmm.device_manager) .virtio_devices @@ -935,14 +948,14 @@ pub(crate) mod tests { // Duplicate device ID is rejected let cfg2 = HotplugDeviceConfig::Block(make_hotplug_block_cfg("block0", &f, false)); assert!(matches!( - vmm.hotplug_device(cfg2, &mut evt_manager), + vmm.hotplug_device(cfg2, &mut evt_manager, &seccomp_filters), Err(VmmActionError::DeviceIdInUse) )); // Root block device is rejected let cfg3 = HotplugDeviceConfig::Block(make_hotplug_block_cfg("block1", &f, true)); assert!(matches!( - vmm.hotplug_device(cfg3, &mut evt_manager), + vmm.hotplug_device(cfg3, &mut evt_manager, &seccomp_filters), Err(VmmActionError::DriveConfig( DriveError::RootBlockDeviceAlreadyAdded )) @@ -970,11 +983,12 @@ pub(crate) mod tests { fn test_hotplug_pci_not_enabled() { let mut vmm = default_vmm(); let mut evt_manager = EventManager::new().unwrap(); + let seccomp_filters = get_empty_filters(); let f = TempFile::new().unwrap(); let cfg = HotplugDeviceConfig::Block(make_hotplug_block_cfg("block0", &f, false)); assert!(matches!( - vmm.hotplug_device(cfg, &mut evt_manager), + vmm.hotplug_device(cfg, &mut evt_manager, &seccomp_filters), Err(VmmActionError::PciNotEnabled) )); } @@ -1007,6 +1021,7 @@ pub(crate) mod tests { fn test_hotplug_pmem() { let mut vmm = default_vmm_with_pci(); let mut evt_manager = EventManager::new().unwrap(); + let seccomp_filters = get_empty_filters(); let f = TempFile::new().unwrap(); f.as_file().set_len(0x1000).unwrap(); @@ -1018,7 +1033,8 @@ pub(crate) mod tests { read_only: false, ..Default::default() }); - vmm.hotplug_device(cfg, &mut evt_manager).unwrap(); + vmm.hotplug_device(cfg, &mut evt_manager, &seccomp_filters) + .unwrap(); assert!( pci_devices(&vmm.device_manager) .virtio_devices @@ -1035,7 +1051,7 @@ pub(crate) mod tests { ..Default::default() }); assert!(matches!( - vmm.hotplug_device(cfg2, &mut evt_manager), + vmm.hotplug_device(cfg2, &mut evt_manager, &seccomp_filters), Err(VmmActionError::PmemConfig( PmemConfigError::AddingSecondRootDevice )) @@ -1063,6 +1079,7 @@ pub(crate) mod tests { fn test_hotplug_net() { let mut vmm = default_vmm_with_pci(); let mut evt_manager = EventManager::new().unwrap(); + let seccomp_filters = get_empty_filters(); let mac = "AA:FC:00:00:00:01"; @@ -1075,7 +1092,8 @@ pub(crate) mod tests { rx_rate_limiter: None, tx_rate_limiter: None, }); - vmm.hotplug_device(cfg, &mut evt_manager).unwrap(); + vmm.hotplug_device(cfg, &mut evt_manager, &seccomp_filters) + .unwrap(); assert!( pci_devices(&vmm.device_manager) .virtio_devices @@ -1092,7 +1110,7 @@ pub(crate) mod tests { tx_rate_limiter: None, }); assert!(matches!( - vmm.hotplug_device(cfg2, &mut evt_manager), + vmm.hotplug_device(cfg2, &mut evt_manager, &seccomp_filters), Err(VmmActionError::NetworkConfig( NetworkInterfaceError::GuestMacAddressInUse(_) )) diff --git a/src/vmm/src/device_manager/pci_mngr.rs b/src/vmm/src/device_manager/pci_mngr.rs index 990d2d825d3..a9a5e1aa733 100644 --- a/src/vmm/src/device_manager/pci_mngr.rs +++ b/src/vmm/src/device_manager/pci_mngr.rs @@ -37,6 +37,7 @@ use crate::logger::{debug, warn}; use crate::pci::PciSBDF; use crate::pci::bus::PciRootError; use crate::resources::VmResources; +use crate::seccomp::BpfThreadMap; use crate::snapshot::Persist; use crate::vmm_config::memory_hotplug::MemoryHotplugConfig; use crate::vstate::bus::BusError; @@ -327,6 +328,7 @@ pub struct PciDevicesConstructorArgs<'a> { pub vm_resources: &'a mut VmResources, pub instance_id: &'a str, pub event_manager: &'a mut EventManager, + pub seccomp_filters: &'a BpfThreadMap, } impl<'a> Debug for PciDevicesConstructorArgs<'a> { @@ -518,6 +520,11 @@ impl<'a> Persist<'a> for PciDevices { &block_state.device_state, )?)); + device + .lock() + .expect("Poisoned lock") + .spawn_worker(constructor_args.seccomp_filters.get("blk_worker").cloned())?; + constructor_args .vm_resources .block @@ -685,6 +692,7 @@ mod tests { use crate::devices::virtio::block::CacheType; use crate::mmds::data_store::MmdsVersion; use crate::resources::VmmConfig; + use crate::seccomp::get_empty_filters; use crate::vmm_config::balloon::BalloonDeviceConfig; use crate::vmm_config::entropy::EntropyDeviceConfig; use crate::vmm_config::memory_hotplug::MemoryHotplugConfig; @@ -812,6 +820,7 @@ mod tests { vm_resources, instance_id: "microvm-id", event_manager: &mut event_manager, + seccomp_filters: &get_empty_filters(), }; let _restored_dev_manager = PciDevices::restore(restore_args, pci_state).unwrap(); diff --git a/src/vmm/src/device_manager/persist.rs b/src/vmm/src/device_manager/persist.rs index 2be21aa15ec..6da2fe1ec8d 100644 --- a/src/vmm/src/device_manager/persist.rs +++ b/src/vmm/src/device_manager/persist.rs @@ -38,6 +38,7 @@ use crate::devices::virtio::vsock::persist::{ use crate::devices::virtio::vsock::{Vsock, VsockUnixBackend}; use crate::mmds::data_store::MmdsVersion; use crate::resources::VmResources; +use crate::seccomp::BpfThreadMap; use crate::snapshot::Persist; use crate::vmm_config::memory_hotplug::MemoryHotplugConfig; use crate::vstate::memory::GuestMemoryMmap; @@ -137,6 +138,7 @@ pub struct MMIODevManagerConstructorArgs<'a> { pub event_manager: &'a mut EventManager, pub vm_resources: &'a mut VmResources, pub instance_id: &'a str, + pub seccomp_filters: &'a BpfThreadMap, } pub struct MMIOPlatformDevicesConstructorArgs<'a> { @@ -479,6 +481,11 @@ impl<'a> Persist<'a> for MMIOVirtioDevices { &block_state.device_state, )?)); + device + .lock() + .expect("Poisoned lock") + .spawn_worker(constructor_args.seccomp_filters.get("blk_worker").cloned())?; + constructor_args .vm_resources .block @@ -649,6 +656,7 @@ mod tests { use crate::device_manager; use crate::devices::virtio::block::CacheType; use crate::resources::VmmConfig; + use crate::seccomp::get_empty_filters; use crate::vmm_config::balloon::BalloonDeviceConfig; use crate::vmm_config::entropy::EntropyDeviceConfig; use crate::vmm_config::memory_hotplug::MemoryHotplugConfig; @@ -806,6 +814,7 @@ mod tests { event_manager: &mut event_manager, vm_resources, instance_id: "microvm-id", + seccomp_filters: &get_empty_filters(), }; let _restored_dev_manager = MMIOVirtioDevices::restore(restore_args, mmio_state).unwrap(); diff --git a/src/vmm/src/devices/virtio/block/device.rs b/src/vmm/src/devices/virtio/block/device.rs index 1ab665ceed8..c2b573df03f 100644 --- a/src/vmm/src/devices/virtio/block/device.rs +++ b/src/vmm/src/devices/virtio/block/device.rs @@ -16,6 +16,7 @@ use crate::devices::virtio::queue::{InvalidAvailIdx, QueueConfig, QueueError}; use crate::devices::virtio::transport::VirtioInterrupt; use crate::impl_device_type; use crate::rate_limiter::BucketUpdate; +use crate::seccomp::BpfProgram; use crate::snapshot::Persist; use crate::vmm_config::drive::BlockDeviceConfig; use crate::vstate::memory::GuestMemoryMmap; @@ -114,6 +115,15 @@ impl Block { Self::VhostUser(_) => true, } } + + pub(crate) fn spawn_worker( + &mut self, + _seccomp_filter: Option>, + ) -> Result<(), BlockError> { + match self { + Self::Virtio(_) | Self::VhostUser(_) => Ok(()), + } + } } impl VirtioDevice for Block { diff --git a/src/vmm/src/devices/virtio/block/mod.rs b/src/vmm/src/devices/virtio/block/mod.rs index 4d8c8cdf9c9..0dfbec9beb5 100644 --- a/src/vmm/src/devices/virtio/block/mod.rs +++ b/src/vmm/src/devices/virtio/block/mod.rs @@ -31,6 +31,8 @@ pub enum BlockError { InvalidBlockBackend, /// Can not restore any backend. BackendRestore, + /// Missing block worker seccomp filter. + MissingSeccompFilter, /// Virtio backend error: {0} VirtioBackend(VirtioBlockError), /// Vhost user backend error: {0} diff --git a/src/vmm/src/devices/virtio/block/virtio/device.rs b/src/vmm/src/devices/virtio/block/virtio/device.rs index 247b221e632..62f9eafa7f8 100644 --- a/src/vmm/src/devices/virtio/block/virtio/device.rs +++ b/src/vmm/src/devices/virtio/block/virtio/device.rs @@ -501,8 +501,6 @@ impl VirtioBlock { } /// Spawn a parked worker thread for the next activation. - // Currently unused because threaded mode is not exposed through device configuration yet. - #[allow(dead_code)] pub(crate) fn spawn_worker( &mut self, seccomp_filter: Arc, @@ -516,11 +514,9 @@ impl VirtioBlock { .map_err(VirtioBlockError::EventFd)?; let name = format!("fc_{}", self.config.drive_id); - - *worker_handle = Some( - WorkerHandle::spawn(seccomp_filter, queue_evts, name) - .map_err(VirtioBlockError::ThreadSpawn)?, - ); + let worker = WorkerHandle::spawn(seccomp_filter, queue_evts, name) + .map_err(VirtioBlockError::ThreadSpawn)?; + *worker_handle = Some(worker); } Ok(()) } diff --git a/src/vmm/src/lib.rs b/src/vmm/src/lib.rs index 9b80384b348..0404965093f 100644 --- a/src/vmm/src/lib.rs +++ b/src/vmm/src/lib.rs @@ -150,6 +150,7 @@ use crate::persist::{MicrovmState, MicrovmStateError, VmInfo}; use crate::rate_limiter::BucketUpdate; use crate::resources::VmmConfig; use crate::rpc_interface::VmmActionError; +use crate::seccomp::BpfThreadMap; use crate::vmm_config::HotplugDeviceConfig; use crate::vmm_config::balloon::BalloonDeviceConfig; use crate::vmm_config::boot_source::BootSourceConfig; @@ -708,6 +709,7 @@ impl Vmm { &mut self, config: HotplugDeviceConfig, event_manager: &mut EventManager, + seccomp_filters: &BpfThreadMap, ) -> Result<(), VmmActionError> { log_dev_preview_warning("PCI device hotplug", None); let kvm_vm = self @@ -716,7 +718,7 @@ impl Vmm { .ok_or_else(|| VmmActionError::NotSupported("Operation requires KVM".to_string()))? .clone(); self.device_manager - .hotplug_device(kvm_vm, config, event_manager) + .hotplug_device(kvm_vm, config, event_manager, seccomp_filters) } /// Detaches a device after VM start diff --git a/src/vmm/src/rpc_interface.rs b/src/vmm/src/rpc_interface.rs index aea186995a1..9a4447f23c9 100644 --- a/src/vmm/src/rpc_interface.rs +++ b/src/vmm/src/rpc_interface.rs @@ -690,6 +690,7 @@ impl<'a> PrebootApiController<'a> { #[derive(Debug)] pub struct RuntimeApiController { vmm: Arc>, + seccomp_filters: BpfThreadMap, } impl RuntimeApiController { @@ -765,19 +766,31 @@ impl RuntimeApiController { .vmm .lock() .expect("Poisoned lock") - .hotplug_device(HotplugDeviceConfig::Block(config), event_manager) + .hotplug_device( + HotplugDeviceConfig::Block(config), + event_manager, + &self.seccomp_filters, + ) .map(|()| VmmData::Empty), InsertPmemDevice(config) => self .vmm .lock() .expect("Poisoned lock") - .hotplug_device(HotplugDeviceConfig::Pmem(config), event_manager) + .hotplug_device( + HotplugDeviceConfig::Pmem(config), + event_manager, + &self.seccomp_filters, + ) .map(|()| VmmData::Empty), InsertNetworkDevice(config) => self .vmm .lock() .expect("Poisoned lock") - .hotplug_device(HotplugDeviceConfig::Net(config), event_manager) + .hotplug_device( + HotplugDeviceConfig::Net(config), + event_manager, + &self.seccomp_filters, + ) .map(|()| VmmData::Empty), HotUnplugDevice(device_id) => self .vmm @@ -862,8 +875,11 @@ impl RuntimeApiController { } /// Creates a new `RuntimeApiController`. - pub fn new(vmm: Arc>) -> Self { - Self { vmm } + pub fn new(vmm: Arc>, seccomp_filters: &BpfThreadMap) -> Self { + Self { + vmm, + seccomp_filters: seccomp_filters.clone(), + } } /// Pauses the microVM by pausing the vCPUs. @@ -1253,7 +1269,8 @@ mod tests { fn runtime_request(request: VmmAction) -> Result { let vmm = Arc::new(Mutex::new(default_vmm())); - let mut runtime = RuntimeApiController::new(vmm.clone()); + let seccomp_filters = BpfThreadMap::new(); + let mut runtime = RuntimeApiController::new(vmm.clone(), &seccomp_filters); let mut event_manager = EventManager::new().unwrap(); runtime.handle_request(request, &mut event_manager) } diff --git a/src/vmm/tests/integration_tests.rs b/src/vmm/tests/integration_tests.rs index 7936a0a0898..444c3d919a9 100644 --- a/src/vmm/tests/integration_tests.rs +++ b/src/vmm/tests/integration_tests.rs @@ -104,7 +104,7 @@ fn test_build_microvm() { } fn pause_resume_microvm(vmm: Arc>) { - let mut api_controller = RuntimeApiController::new(vmm.clone()); + let mut api_controller = RuntimeApiController::new(vmm.clone(), &get_empty_filters()); let mut event_manager = EventManager::new().unwrap(); // There's a race between this thread and the vcpu thread, but this thread @@ -228,7 +228,7 @@ fn verify_create_snapshot( ); let vm_info = VmInfo::from(&*vmm.lock().unwrap()); - let mut controller = RuntimeApiController::new(vmm.clone()); + let mut controller = RuntimeApiController::new(vmm.clone(), &get_empty_filters()); let mut event_manager = EventManager::new().unwrap(); // Be sure that the microVM is running.