From d9f89ef2abc8c156d736a4d8a203861efbf70e63 Mon Sep 17 00:00:00 2001 From: Bo Chen Date: Thu, 23 Apr 2026 23:51:45 +0000 Subject: [PATCH] vmm: http_api: Accept a VFIO device FD via SCM_RIGHTS Signed-off-by: Bo Chen Assisted-by: Claude:Opus-4.7 --- vmm/src/api/http/http_endpoint.rs | 65 +++++++++++++++++++++++++++++-- 1 file changed, 62 insertions(+), 3 deletions(-) diff --git a/vmm/src/api/http/http_endpoint.rs b/vmm/src/api/http/http_endpoint.rs index 286ef71bd..b652dfe99 100644 --- a/vmm/src/api/http/http_endpoint.rs +++ b/vmm/src/api/http/http_endpoint.rs @@ -45,7 +45,7 @@ use crate::api::VmCoredump; use crate::api::http::http_endpoint::fds_helper::{attach_fds_to_cfg, attach_fds_to_cfgs}; use crate::api::http::{EndpointHandler, HttpError, error_response}; use crate::api::{ - AddDisk, ApiAction, ApiError, ApiRequest, NetConfig, VmAddDevice, VmAddFs, + AddDisk, ApiAction, ApiError, ApiRequest, DeviceConfig, NetConfig, VmAddDevice, VmAddFs, VmAddGenericVhostUser, VmAddNet, VmAddPmem, VmAddUserDevice, VmAddVdpa, VmAddVsock, VmBoot, VmConfig, VmCounters, VmDelete, VmNmi, VmPause, VmPowerButton, VmReboot, VmReceiveMigration, VmRemoveDevice, VmResize, VmResizeDisk, VmResizeZone, VmRestore, VmResume, VmSendMigration, @@ -115,10 +115,11 @@ mod fds_helper { mod config_with_fds_impls { use std::os::fd::RawFd; + use std::slice::from_ref; use super::{ConfigWithFDs, ConfigWithVariableFDs}; use crate::config::RestoredNetConfig; - use crate::vm_config::NetConfig; + use crate::vm_config::{DeviceConfig, NetConfig}; impl ConfigWithFDs for NetConfig { fn id(&self) -> Option<&str> { @@ -134,6 +135,24 @@ mod fds_helper { } } + impl ConfigWithFDs for DeviceConfig { + fn id(&self) -> Option<&str> { + self.pci_common.id.as_deref() + } + + fn fds_from_http_body(&self) -> Option<&[RawFd]> { + // A DeviceConfig carries at most one FD. + self.fd.as_ref().map(from_ref) + } + + fn set_fds(&mut self, fds: Option>) { + // The VmAddDevice PutHandler enforces `fds.len() <= 1` + // before calling into this trait, so `pop()` yields the + // single FD when present. + self.fd = fds.and_then(|mut v| v.pop()); + } + } + impl ConfigWithFDs for RestoredNetConfig { fn id(&self) -> Option<&str> { Some(self.id.as_str()) @@ -295,6 +314,19 @@ impl EndpointHandler for VmCreate { } } + if let Some(ref mut devices) = vm_config.devices { + // For the VmCreate call, we do not accept FDs from the socket currently. + // This call sets all FDs to null while doing the same logging as + // similar code paths. + for cfg in devices.iter_mut() { + if let Err(e) = + attach_fds_to_cfg(vec![], cfg).map_err(error_response) + { + return e; + } + } + } + match crate::api::VmCreate .send(api_notifier, api_sender, vm_config) .map_err(HttpError::ApiError) @@ -417,7 +449,6 @@ vm_action_put_handler!(VmResume); vm_action_put_handler!(VmPowerButton); vm_action_put_handler!(VmNmi); -vm_action_put_handler_body!(VmAddDevice); vm_action_put_handler_body!(AddDisk); vm_action_put_handler_body!(VmAddFs); vm_action_put_handler_body!(VmAddGenericVhostUser); @@ -435,6 +466,34 @@ vm_action_put_handler_body!(VmSendMigration); #[cfg(all(target_arch = "x86_64", feature = "guest_debug"))] vm_action_put_handler_body!(VmCoredump); +// Special handling for VFIO devices backed by an externally-opened cdev FD. +// See module description for more info. +impl PutHandler for VmAddDevice { + fn handle_request( + &'static self, + api_notifier: EventFd, + api_sender: Sender, + body: &Option, + files: Vec, + ) -> Result, HttpError> { + if let Some(body) = body { + // A DeviceConfig is backed by at most one FD. + if files.len() > 1 { + return Err(HttpError::BadRequest); + } + let mut device_cfg: DeviceConfig = serde_json::from_slice(body.raw())?; + attach_fds_to_cfg(files, &mut device_cfg)?; + + self.send(api_notifier, api_sender, device_cfg) + .map_err(HttpError::ApiError) + } else { + Err(HttpError::BadRequest) + } + } +} + +impl GetHandler for VmAddDevice {} + // Special handling for virtio-net devices backed by network FDs. // See module description for more info. impl PutHandler for VmAddNet {