vmm: Add seccomp filter for migrate-send-postcopy thread

Applying seccomp filtering to the migration postcopy thread running on
the source VM during migration.

Signed-off-by: Sebastien Boeuf <sboeuf@meta.com>
Assisted-by: Claude:claude-opus-4-8
This commit is contained in:
Sebastien Boeuf
2026-06-23 09:52:42 -07:00
parent 80958acdab
commit cc98a232e6
3 changed files with 60 additions and 2 deletions

View File

@@ -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<GuestMemoryMmap>,
) -> 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<u8> = 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 {

View File

@@ -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<dyn hypervisor::Hypervisor>,
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<MigrationWorkerHandle, MigrationWorkerSpawnError> {
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()

View File

@@ -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<Vec<(i64, Vec<SeccompRule>)>, Backend
])
}
// The filter containing the white listed syscall rules required by the
// migration postcopy thread.
fn migrate_send_postcopy_thread_rules() -> Result<Vec<(i64, Vec<SeccompRule>)>, 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<HypervisorType>,
@@ -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()?),
}
}