diff --git a/vmm/src/lib.rs b/vmm/src/lib.rs index b227949ae..7a27b6925 100644 --- a/vmm/src/lib.rs +++ b/vmm/src/lib.rs @@ -31,7 +31,7 @@ use libc::{EFD_NONBLOCK, SIGINT, SIGTERM, TCSANOW, tcsetattr, termios}; use log::{debug, error, info, trace, warn}; use memory_manager::MemoryManagerSnapshotData; use pci::PciBdf; -use seccompiler::{SeccompAction, apply_filter}; +use seccompiler::{BpfProgram, SeccompAction, apply_filter}; use serde::ser::{SerializeStruct, Serializer}; use serde::{Deserialize, Serialize}; use signal_hook::iterator::{Handle, Signals}; @@ -1531,6 +1531,7 @@ impl Vmm { hypervisor: &dyn hypervisor::Hypervisor, send_data_migration: &VmSendMigrationData, initial_vm_state: VmState, + seccomp_action: &SeccompAction, ) -> result::Result<(), MigratableError> { // State machine that is updated with more context as we progress. let mut ctx = OngoingMigrationContext::new(); @@ -1667,9 +1668,19 @@ impl Vmm { send_data_migration.tls_dir.as_deref(), )?; let guest_memory = vm.guest_memory(); + // Build the seccomp filter on the parent thread so any failure aborts + // the migration before the serve thread is spawned. + let seccomp_filter = get_seccomp_filter( + seccomp_action, + Thread::MigrateSendPostcopy, + None, + ) + .map_err(|e| { + MigratableError::MigrateSend(anyhow!("creating postcopy serve seccomp filter: {e}")) + })?; let handle = thread::Builder::new() .name("migrate-send-postcopy".to_owned()) - .spawn(move || Self::serve_postcopy(fault_stream, guest_memory)) + .spawn(move || Self::serve_postcopy(seccomp_filter, fault_stream, guest_memory)) .map_err(|e| { MigratableError::MigrateSend(anyhow!("spawning postcopy serve thread: {e}")) })?; @@ -1751,9 +1762,19 @@ impl Vmm { reason = "runs on a dedicated thread and must own its arguments" )] fn serve_postcopy( + seccomp_filter: BpfProgram, mut socket: SocketStream, guest_memory: GuestMemoryAtomic, ) -> result::Result<(), MigratableError> { + // Apply the dedicated seccomp filter for this thread. It is empty when + // seccomp is disabled (SeccompAction::Allow), in which case there is + // nothing to apply. + if !seccomp_filter.is_empty() { + apply_filter(&seccomp_filter).map_err(|e| { + MigratableError::MigrateSend(anyhow!("applying postcopy serve seccomp filter: {e}")) + })?; + } + let mut buf: Vec = Vec::new(); info!("Postcopy: source entering PageFault serve loop"); @@ -3099,6 +3120,7 @@ impl RequestHandler for Vmm { #[cfg(all(feature = "kvm", target_arch = "x86_64"))] self.hypervisor.clone(), initial_vm_state, + self.seccomp_action.clone(), ) { Ok(handle) => { self.vm = VmOwnership::Migration { diff --git a/vmm/src/migration/worker.rs b/vmm/src/migration/worker.rs index 986615aee..16f3a97a8 100644 --- a/vmm/src/migration/worker.rs +++ b/vmm/src/migration/worker.rs @@ -21,6 +21,7 @@ use std::thread::JoinHandle; use event_monitor::event; use log::warn; +use seccompiler::SeccompAction; use vm_migration::MigratableError; use vmm_sys_util::eventfd::EventFd; @@ -75,6 +76,7 @@ pub struct MigrationWorker { #[cfg(all(feature = "kvm", target_arch = "x86_64"))] hypervisor: Arc, initial_vm_state: VmState, + seccomp_action: SeccompAction, } impl MigrationWorker { @@ -90,6 +92,7 @@ impl MigrationWorker { self.hypervisor.as_ref(), &self.config, self.initial_vm_state, + &self.seccomp_action, ) .inspect(|_| event!("vm", "migration-finished")) .inspect_err(|_| event!("vm", "migration-failed")); @@ -116,6 +119,7 @@ impl MigrationWorker { dyn hypervisor::Hypervisor, >, initial_vm_state: VmState, + seccomp_action: SeccompAction, ) -> Result { let (vm_sender, vm_receiver) = std::sync::mpsc::sync_channel(0); let worker = MigrationWorker { @@ -125,6 +129,7 @@ impl MigrationWorker { #[cfg(all(feature = "kvm", target_arch = "x86_64"))] hypervisor, initial_vm_state, + seccomp_action, }; let inner_handle = match thread::Builder::new() diff --git a/vmm/src/seccomp_filters.rs b/vmm/src/seccomp_filters.rs index f00f3a3f3..f9a106ff0 100644 --- a/vmm/src/seccomp_filters.rs +++ b/vmm/src/seccomp_filters.rs @@ -42,6 +42,7 @@ pub enum Thread { Vmm, PtyForeground, SerialManager, + MigrateSendPostcopy, } /// Shorthand for chaining `SeccompCondition`s with the `and` operator in a `SeccompRule`. @@ -1084,6 +1085,35 @@ fn serial_manager_thread_rules() -> Result)>, Backend ]) } +// The filter containing the white listed syscall rules required by the +// migration postcopy thread. +fn migrate_send_postcopy_thread_rules() -> Result)>, BackendError> { + Ok(vec![ + (libc::SYS_brk, vec![]), + (libc::SYS_clock_gettime, vec![]), + (libc::SYS_close, vec![]), + (libc::SYS_exit, vec![]), + (libc::SYS_futex, vec![]), + (libc::SYS_getrandom, vec![]), + (libc::SYS_gettid, vec![]), + (libc::SYS_madvise, vec![]), + (libc::SYS_mmap, vec![]), + (libc::SYS_mprotect, vec![]), + (libc::SYS_munmap, vec![]), + (libc::SYS_read, vec![]), + (libc::SYS_recvfrom, vec![]), + (libc::SYS_recvmsg, vec![]), + (libc::SYS_rt_sigprocmask, vec![]), + (libc::SYS_rt_sigreturn, vec![]), + (libc::SYS_sched_yield, vec![]), + (libc::SYS_sendmsg, vec![]), + (libc::SYS_sendto, vec![]), + (libc::SYS_sigaltstack, vec![]), + (libc::SYS_write, vec![]), + (libc::SYS_writev, vec![]), + ]) +} + fn get_seccomp_rules( thread_type: Thread, hypervisor_type: Option, @@ -1102,6 +1132,7 @@ fn get_seccomp_rules( hypervisor_type.expect("hypervisor_type is required for Vmm threads"), )?), Thread::PtyForeground => Ok(pty_foreground_thread_rules()?), + Thread::MigrateSendPostcopy => Ok(migrate_send_postcopy_thread_rules()?), } }