vmm: add seccomp filter for serial-manager thread

The serial-manager thread was the only VMM-managed thread without a
seccomp filter. Add a Thread::SerialManager variant and whitelist the
31 syscalls needed for its epoll-based I/O loop (read, write, socket
ops, signal handling, memory allocation, glibc internals).

The filter is computed in start_thread() and applied before the epoll
loop, matching the pattern used by other VMM threads.

Signed-off-by: Wei Liu <liuwe@microsoft.com>
Assisted-by: Pi-agent:Claude-Opus-4.7
This commit is contained in:
Wei Liu
2026-05-14 19:19:34 +00:00
parent 73146be06b
commit 3837c87f1f
3 changed files with 53 additions and 1 deletions

View File

@@ -2534,6 +2534,7 @@ impl DeviceManager {
self.exit_evt
.try_clone()
.map_err(DeviceManagerError::EventFd)?,
&self.seccomp_action,
)
.map_err(DeviceManagerError::SpawnSerialManager)?;
Some(Arc::new(serial_manager))

View File

@@ -39,6 +39,7 @@ pub enum Thread {
Vcpu,
Vmm,
PtyForeground,
SerialManager,
}
/// Shorthand for chaining `SeccompCondition`s with the `and` operator in a `SeccompRule`.
@@ -1031,6 +1032,33 @@ fn event_monitor_thread_rules() -> Result<Vec<(i64, Vec<SeccompRule>)>, BackendE
])
}
fn serial_manager_thread_rules() -> Result<Vec<(i64, Vec<SeccompRule>)>, BackendError> {
Ok(vec![
(libc::SYS_accept4, vec![]),
(libc::SYS_clock_nanosleep, vec![]),
(libc::SYS_clock_gettime, vec![]),
(libc::SYS_close, vec![]),
(libc::SYS_epoll_ctl, vec![]),
(libc::SYS_epoll_pwait, vec![]),
#[cfg(target_arch = "x86_64")]
(libc::SYS_epoll_wait, vec![]),
(libc::SYS_exit, vec![]),
(libc::SYS_fcntl, vec![]),
(libc::SYS_futex, vec![]),
(libc::SYS_madvise, vec![]),
(libc::SYS_mmap, vec![]),
(libc::SYS_munmap, vec![]),
(libc::SYS_nanosleep, vec![]),
(libc::SYS_read, vec![]),
(libc::SYS_recvfrom, vec![]),
(libc::SYS_rt_sigprocmask, vec![]),
(libc::SYS_rt_sigreturn, vec![]),
(libc::SYS_shutdown, vec![]),
(libc::SYS_sigaltstack, vec![]),
(libc::SYS_write, vec![]),
])
}
fn get_seccomp_rules(
thread_type: Thread,
hypervisor_type: Option<HypervisorType>,
@@ -1040,6 +1068,7 @@ fn get_seccomp_rules(
#[cfg(feature = "dbus_api")]
Thread::DBusApi => Ok(dbus_api_thread_rules()?),
Thread::EventMonitor => Ok(event_monitor_thread_rules()?),
Thread::SerialManager => Ok(serial_manager_thread_rules()?),
Thread::SignalHandler => Ok(signal_handler_thread_rules()?),
Thread::Vcpu => Ok(vcpu_thread_rules(
hypervisor_type.expect("hypervisor_type is required for Vcpu threads"),

View File

@@ -21,11 +21,13 @@ use devices::legacy::Pl011;
use devices::legacy::Serial;
use libc::EFD_NONBLOCK;
use log::{error, info, warn};
use seccompiler::{SeccompAction, apply_filter};
use serial_buffer::SerialBuffer;
use thiserror::Error;
use vmm_sys_util::eventfd::EventFd;
use crate::console_devices::ConsoleTransport;
use crate::seccomp_filters::{Thread, get_seccomp_filter};
#[derive(Debug, Error)]
pub enum Error {
@@ -84,6 +86,14 @@ pub enum Error {
/// Cannot duplicate file descriptor
#[error("Error duplicating file descriptor")]
DupFd(#[source] io::Error),
/// Cannot create seccomp filter.
#[error("Error creating seccomp filter")]
CreateSeccompFilter(seccompiler::Error),
/// Cannot apply seccomp filter.
#[error("Error applying seccomp filter")]
ApplySeccompFilter(seccompiler::Error),
}
pub type Result<T> = result::Result<T, Error>;
@@ -250,13 +260,20 @@ impl SerialManager {
Ok(())
}
pub fn start_thread(&mut self, exit_evt: EventFd) -> Result<()> {
pub fn start_thread(
&mut self,
exit_evt: EventFd,
seccomp_action: &SeccompAction,
) -> Result<()> {
// Don't allow this to be run if the handle exists
if self.handle.is_some() {
warn!("Tried to start multiple SerialManager threads, ignoring");
return Ok(());
}
let seccomp_filter = get_seccomp_filter(seccomp_action, Thread::SerialManager, None)
.map_err(Error::CreateSeccompFilter)?;
let epoll_fd = self.epoll_fd.try_clone().map_err(Error::Epoll)?;
let transport = self.transport.clone();
let serial = self.serial.clone();
@@ -275,6 +292,11 @@ impl SerialManager {
.name("serial-manager".to_string())
.spawn(move || {
std::panic::catch_unwind(AssertUnwindSafe(move || {
// Apply seccomp filter for serial manager thread.
if !seccomp_filter.is_empty() {
apply_filter(&seccomp_filter).map_err(Error::ApplySeccompFilter)?;
}
let mut events =
[epoll::Event::new(epoll::Events::empty(), 0); EPOLL_EVENTS_LEN];