From feb8d7ae90a8e1c0c57feece6baa57a38b6b7d08 Mon Sep 17 00:00:00 2001 From: Sebastien Boeuf Date: Fri, 20 Mar 2020 17:57:03 +0100 Subject: [PATCH] vmm: Separate seccomp filters between VMM and API threads This separates the filters used between the VMM and API threads, so that we can apply different rules for each thread. Signed-off-by: Sebastien Boeuf --- vmm/src/api/http.rs | 4 +-- vmm/src/lib.rs | 4 +-- vmm/src/seccomp_filters.rs | 60 +++++++++++++++++++++++++++++++++----- 3 files changed, 57 insertions(+), 11 deletions(-) diff --git a/vmm/src/api/http.rs b/vmm/src/api/http.rs index a6e4d8ab7..4006b136e 100644 --- a/vmm/src/api/http.rs +++ b/vmm/src/api/http.rs @@ -7,7 +7,7 @@ use crate::api::http_endpoint::{ VmActionHandler, VmAddDevice, VmCreate, VmInfo, VmRemoveDevice, VmResize, VmmPing, VmmShutdown, }; use crate::api::{ApiRequest, VmAction}; -use crate::seccomp_filters::get_seccomp_filter; +use crate::seccomp_filters::{get_seccomp_filter, Thread}; use crate::{Error, Result}; use micro_http::{HttpServer, MediaType, Request, Response, StatusCode, Version}; use seccomp::{SeccompFilter, SeccompLevel}; @@ -101,7 +101,7 @@ pub fn start_http_thread( // Retrieve seccomp filter for API thread let api_seccomp_filter = - get_seccomp_filter(seccomp_level).map_err(Error::CreateSeccompFilter)?; + get_seccomp_filter(seccomp_level, Thread::Api).map_err(Error::CreateSeccompFilter)?; thread::Builder::new() .name("http-server".to_string()) diff --git a/vmm/src/lib.rs b/vmm/src/lib.rs index 91f3b4dc2..7ebfc41a4 100644 --- a/vmm/src/lib.rs +++ b/vmm/src/lib.rs @@ -17,7 +17,7 @@ extern crate vmm_sys_util; use crate::api::{ApiError, ApiRequest, ApiResponse, ApiResponsePayload, VmInfo, VmmPingResponse}; use crate::config::{DeviceConfig, VmConfig}; -use crate::seccomp_filters::get_seccomp_filter; +use crate::seccomp_filters::{get_seccomp_filter, Thread}; use crate::vm::{Error as VmError, Vm, VmState}; use libc::EFD_NONBLOCK; use seccomp::{SeccompFilter, SeccompLevel}; @@ -176,7 +176,7 @@ pub fn start_vmm_thread( // Retrieve seccomp filter let vmm_seccomp_filter = - get_seccomp_filter(seccomp_level).map_err(Error::CreateSeccompFilter)?; + get_seccomp_filter(seccomp_level, Thread::Vmm).map_err(Error::CreateSeccompFilter)?; // Find the path that the "/proc//exe" symlink points to. Must be done before spawning // a thread as Rust does not put the child threads in the same thread group which prevents the diff --git a/vmm/src/seccomp_filters.rs b/vmm/src/seccomp_filters.rs index 73a0bc8e8..bd815dd5e 100644 --- a/vmm/src/seccomp_filters.rs +++ b/vmm/src/seccomp_filters.rs @@ -12,6 +12,11 @@ use seccomp::{ }; use std::convert::TryInto; +pub enum Thread { + Vmm, + Api, +} + /// Shorthand for chaining `SeccompCondition`s with the `and` operator in a `SeccompRule`. /// The rule will take the `Allow` action if _all_ the conditions are true. /// @@ -94,7 +99,7 @@ const VFIO_IOMMU_MAP_DMA: u64 = 0x3b71; const VFIO_IOMMU_UNMAP_DMA: u64 = 0x3b72; const VFIO_DEVICE_IOEVENTFD: u64 = 0x3b74; -fn create_ioctl_seccomp_rule() -> Result, Error> { +fn create_vmm_ioctl_seccomp_rule() -> Result, Error> { Ok(or![ and![Cond::new(1, ArgLen::DWORD, Eq, FIOCLEX)?], and![Cond::new(1, ArgLen::DWORD, Eq, FIONBIO)?], @@ -156,9 +161,13 @@ fn create_ioctl_seccomp_rule() -> Result, Error> { ]) } -/// The default filter containing the white listed syscall rules required by -/// the VMM to function. -pub fn default_filter() -> Result { +fn create_api_ioctl_seccomp_rule() -> Result, Error> { + Ok(or![and![Cond::new(1, ArgLen::DWORD, Eq, FIONBIO)?],]) +} + +/// The filter containing the white listed syscall rules required by the VMM to +/// function. +pub fn vmm_thread_filter() -> Result { Ok(SeccompFilter::new( vec![ allow_syscall(libc::SYS_accept4), @@ -187,7 +196,7 @@ pub fn default_filter() -> Result { allow_syscall(libc::SYS_getpid), allow_syscall(libc::SYS_getrandom), allow_syscall(libc::SYS_getuid), - allow_syscall_if(libc::SYS_ioctl, create_ioctl_seccomp_rule()?), + allow_syscall_if(libc::SYS_ioctl, create_vmm_ioctl_seccomp_rule()?), allow_syscall(libc::SYS_listen), allow_syscall(libc::SYS_lseek), allow_syscall(libc::SYS_madvise), @@ -234,11 +243,48 @@ pub fn default_filter() -> Result { )?) } +/// The filter containing the white listed syscall rules required by the API to +/// function. +pub fn api_thread_filter() -> Result { + Ok(SeccompFilter::new( + vec![ + allow_syscall(libc::SYS_accept4), + allow_syscall(libc::SYS_bind), + allow_syscall(libc::SYS_close), + allow_syscall(libc::SYS_dup), + allow_syscall(libc::SYS_epoll_create1), + allow_syscall(libc::SYS_epoll_ctl), + allow_syscall(libc::SYS_epoll_wait), + allow_syscall(libc::SYS_exit), + allow_syscall(libc::SYS_futex), + allow_syscall(libc::SYS_getrandom), + allow_syscall_if(libc::SYS_ioctl, create_api_ioctl_seccomp_rule()?), + allow_syscall(libc::SYS_listen), + allow_syscall(libc::SYS_madvise), + allow_syscall(libc::SYS_munmap), + allow_syscall(libc::SYS_recvfrom), + allow_syscall(libc::SYS_sigaltstack), + allow_syscall(libc::SYS_socket), + allow_syscall(libc::SYS_write), + ] + .into_iter() + .collect(), + SeccompAction::Trap, + )?) +} + /// Generate a BPF program based on a seccomp level value. -pub fn get_seccomp_filter(seccomp_level: &SeccompLevel) -> Result { +pub fn get_seccomp_filter( + seccomp_level: &SeccompLevel, + thread_type: Thread, +) -> Result { + let filter = match thread_type { + Thread::Vmm => vmm_thread_filter(), + Thread::Api => api_thread_filter(), + }; match *seccomp_level { SeccompLevel::None => Ok(vec![]), - _ => default_filter() + _ => filter .and_then(|filter| filter.try_into()) .map_err(SeccompError::SeccompFilter), }