From 704edd544c87c83ecb6d4aaa583e7e65fc7b98a9 Mon Sep 17 00:00:00 2001 From: Bo Chen Date: Mon, 3 Aug 2020 19:45:53 -0700 Subject: [PATCH] virtio-devices: seccomp: Add seccomp_filter module This patch added the seccomp_filter module to the virtio-devices crate by taking reference code from the vmm crate. This patch also adds allowed-list for the virtio-block worker thread. Partially fixes: #925 Signed-off-by: Bo Chen --- virtio-devices/Cargo.toml | 4 +- virtio-devices/src/block.rs | 18 +++++- virtio-devices/src/epoll_helper.rs | 1 + virtio-devices/src/lib.rs | 3 + virtio-devices/src/seccomp_filters.rs | 91 +++++++++++++++++++++++++++ vmm/src/device_manager.rs | 9 +++ vmm/src/vm.rs | 3 +- 7 files changed, 125 insertions(+), 4 deletions(-) create mode 100644 virtio-devices/src/seccomp_filters.rs diff --git a/virtio-devices/Cargo.toml b/virtio-devices/Cargo.toml index aee40f879..3d5aa2f60 100644 --- a/virtio-devices/Cargo.toml +++ b/virtio-devices/Cargo.toml @@ -13,7 +13,7 @@ io_uring = ["block_util/io_uring"] [dependencies] anyhow = "1.0" arc-swap = ">=0.4.4" -block_util = { path = "../block_util" } +block_util = { path = "../block_util" } byteorder = "1.3.4" devices = { path = "../devices" } epoll = ">=4.0.1" @@ -23,6 +23,7 @@ log = "0.4.11" net_gen = { path = "../net_gen" } net_util = { path = "../net_util" } pci = { path = "../pci", optional = true } +seccomp = { git = "https://github.com/firecracker-microvm/firecracker", tag = "v0.21.1" } serde = ">=1.0.27" serde_derive = ">=1.0.27" serde_json = ">=1.0.9" @@ -36,4 +37,3 @@ vm-memory = { version = "0.2.1", features = ["backend-mmap", "backend-atomic"] } vm-migration = { path = "../vm-migration" } vm-virtio = { path = "../vm-virtio" } vmm-sys-util = ">=0.3.1" - diff --git a/virtio-devices/src/block.rs b/virtio-devices/src/block.rs index bfac38e6c..aafb92ad9 100644 --- a/virtio-devices/src/block.rs +++ b/virtio-devices/src/block.rs @@ -13,10 +13,12 @@ use super::{ ActivateError, ActivateResult, EpollHelper, EpollHelperError, EpollHelperHandler, Queue, VirtioDevice, VirtioDeviceType, VirtioInterruptType, EPOLL_HELPER_EVENT_LAST, }; +use crate::seccomp_filters::{get_seccomp_filter, Thread}; use crate::VirtioInterrupt; use anyhow::anyhow; use block_util::{build_disk_image_id, Request, RequestType, VirtioBlockConfig}; use libc::EFD_NONBLOCK; +use seccomp::{SeccompAction, SeccompFilter}; use std::collections::HashMap; use std::io::{self, Read, Seek, SeekFrom, Write}; use std::num::Wrapping; @@ -268,6 +270,7 @@ pub struct Block { queue_size: Vec, writeback: Arc, counters: BlockCounters, + seccomp_action: SeccompAction, } #[derive(Serialize, Deserialize)] @@ -283,6 +286,7 @@ impl Block { /// Create a new virtio block device that operates on the given file. /// /// The given file must be seekable and sizable. + #[allow(clippy::too_many_arguments)] pub fn new( id: String, mut disk_image: T, @@ -291,6 +295,7 @@ impl Block { iommu: bool, num_queues: usize, queue_size: u16, + seccomp_action: SeccompAction, ) -> io::Result> { let disk_size = disk_image.seek(SeekFrom::End(0))? as u64; if disk_size % SECTOR_SIZE != 0 { @@ -343,6 +348,7 @@ impl Block { queue_size: vec![queue_size; num_queues], writeback: Arc::new(AtomicBool::new(true)), counters: BlockCounters::default(), + seccomp_action, }) } @@ -527,9 +533,19 @@ impl VirtioDevice for Block { handler.queue.set_event_idx(event_idx); let paused = self.paused.clone(); + + // Retrieve seccomp filter for virtio_blk thread + let api_seccomp_filter = get_seccomp_filter(&self.seccomp_action, Thread::VirtioBlk) + .map_err(ActivateError::CreateSeccompFilter)?; + thread::Builder::new() .name("virtio_blk".to_string()) - .spawn(move || handler.run(paused)) + .spawn(move || { + SeccompFilter::apply(api_seccomp_filter) + .map_err(EpollHelperError::ApplySeccompFilter)?; + + handler.run(paused) + }) .map(|thread| epoll_threads.push(thread)) .map_err(|e| { error!("failed to clone the virtio-blk epoll thread: {}", e); diff --git a/virtio-devices/src/epoll_helper.rs b/virtio-devices/src/epoll_helper.rs index 0cf306aa9..d0bae0b6b 100644 --- a/virtio-devices/src/epoll_helper.rs +++ b/virtio-devices/src/epoll_helper.rs @@ -25,6 +25,7 @@ pub enum EpollHelperError { CreateFd(std::io::Error), Ctl(std::io::Error), Wait(std::io::Error), + ApplySeccompFilter(seccomp::Error), } pub const EPOLL_HELPER_EVENT_PAUSE: u16 = 0; diff --git a/virtio-devices/src/lib.rs b/virtio-devices/src/lib.rs index 5eeeafec7..886e041d5 100644 --- a/virtio-devices/src/lib.rs +++ b/virtio-devices/src/lib.rs @@ -41,6 +41,7 @@ pub mod net; pub mod net_util; mod pmem; mod rng; +pub mod seccomp_filters; pub mod transport; pub mod vhost_user; pub mod vsock; @@ -97,6 +98,8 @@ pub enum ActivateError { VhostUserBlkSetup(vhost_user::Error), /// Failed to reset vhost-user daemon. VhostUserReset(vhost_user::Error), + /// Cannot create seccomp filter + CreateSeccompFilter(seccomp::SeccompError), } pub type ActivateResult = std::result::Result<(), ActivateError>; diff --git a/virtio-devices/src/seccomp_filters.rs b/virtio-devices/src/seccomp_filters.rs new file mode 100644 index 000000000..ef6e2dfd1 --- /dev/null +++ b/virtio-devices/src/seccomp_filters.rs @@ -0,0 +1,91 @@ +// Copyright 2018 Amazon.com, Inc. or its affiliates. All Rights Reserved. +// +// Copyright © 2020 Intel Corporation +// +// SPDX-License-Identifier: Apache-2.0 + +use seccomp::{ + allow_syscall, BpfProgram, Error, SeccompAction, SeccompError, SeccompFilter, SyscallRuleSet, +}; +use std::convert::TryInto; + +pub enum Thread { + VirtioBlk, +} + +// The filter containing the allowed syscall rules required by the +// virtio_blk thread to function. +fn virtio_blk_thread_rules() -> Result, Error> { + Ok(vec![ + allow_syscall(libc::SYS_brk), + 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_pwait), + #[cfg(target_arch = "x86_64")] + allow_syscall(libc::SYS_epoll_wait), + allow_syscall(libc::SYS_exit), + allow_syscall(libc::SYS_fallocate), + allow_syscall(libc::SYS_fdatasync), + allow_syscall(libc::SYS_fsync), + #[cfg(target_arch = "x86_64")] + allow_syscall(libc::SYS_ftruncate), + #[cfg(target_arch = "aarch64")] + // The definition of libc::SYS_ftruncate is missing on AArch64. + // Use a hard-code number instead. + allow_syscall(46), + allow_syscall(libc::SYS_futex), + allow_syscall(libc::SYS_lseek), + allow_syscall(libc::SYS_madvise), + allow_syscall(libc::SYS_mmap), + allow_syscall(libc::SYS_mprotect), + allow_syscall(libc::SYS_munmap), + allow_syscall(libc::SYS_openat), + allow_syscall(libc::SYS_prctl), + allow_syscall(libc::SYS_read), + allow_syscall(libc::SYS_rt_sigprocmask), + allow_syscall(libc::SYS_sched_getaffinity), + allow_syscall(libc::SYS_set_robust_list), + allow_syscall(libc::SYS_sigaltstack), + allow_syscall(libc::SYS_write), + ]) +} + +fn get_seccomp_filter_trap(thread_type: Thread) -> Result { + let rules = match thread_type { + Thread::VirtioBlk => virtio_blk_thread_rules()?, + }; + + Ok(SeccompFilter::new( + rules.into_iter().collect(), + SeccompAction::Trap, + )?) +} + +fn get_seccomp_filter_log(thread_type: Thread) -> Result { + let rules = match thread_type { + Thread::VirtioBlk => virtio_blk_thread_rules()?, + }; + + Ok(SeccompFilter::new( + rules.into_iter().collect(), + SeccompAction::Log, + )?) +} + +/// Generate a BPF program based on the seccomp_action value +pub fn get_seccomp_filter( + seccomp_action: &SeccompAction, + thread_type: Thread, +) -> Result { + match seccomp_action { + SeccompAction::Allow => Ok(vec![]), + SeccompAction::Log => get_seccomp_filter_log(thread_type) + .and_then(|filter| filter.try_into()) + .map_err(SeccompError::SeccompFilter), + _ => get_seccomp_filter_trap(thread_type) + .and_then(|filter| filter.try_into()) + .map_err(SeccompError::SeccompFilter), + } +} diff --git a/vmm/src/device_manager.rs b/vmm/src/device_manager.rs index 3e41d67b1..a4867cd66 100644 --- a/vmm/src/device_manager.rs +++ b/vmm/src/device_manager.rs @@ -54,6 +54,7 @@ use pci::{ VfioPciDevice, }; use qcow::{self, ImageType, QcowFile}; +use seccomp::SeccompAction; #[cfg(feature = "pci_support")] use std::any::Any; use std::collections::HashMap; @@ -801,6 +802,9 @@ pub struct DeviceManager { #[cfg(target_arch = "aarch64")] id_to_dev_info: HashMap<(DeviceType, String), MMIODeviceInfo>, + + // seccomp action + seccomp_action: SeccompAction, } impl DeviceManager { @@ -811,6 +815,7 @@ impl DeviceManager { _exit_evt: &EventFd, #[cfg_attr(target_arch = "aarch64", allow(unused_variables))] reset_evt: &EventFd, vmm_path: PathBuf, + seccomp_action: SeccompAction, ) -> DeviceManagerResult>> { let device_tree = Arc::new(Mutex::new(DeviceTree::new())); @@ -872,6 +877,7 @@ impl DeviceManager { reset_evt: reset_evt.try_clone().map_err(DeviceManagerError::EventFd)?, #[cfg(target_arch = "aarch64")] id_to_dev_info: HashMap::new(), + seccomp_action, }; #[cfg(feature = "acpi")] @@ -1710,6 +1716,7 @@ impl DeviceManager { disk_cfg.iommu, disk_cfg.num_queues, disk_cfg.queue_size, + self.seccomp_action.clone(), ) .map_err(DeviceManagerError::CreateVirtioBlock)?, )); @@ -1736,6 +1743,7 @@ impl DeviceManager { disk_cfg.iommu, disk_cfg.num_queues, disk_cfg.queue_size, + self.seccomp_action.clone(), ) .map_err(DeviceManagerError::CreateVirtioBlock)?, )); @@ -1762,6 +1770,7 @@ impl DeviceManager { disk_cfg.iommu, disk_cfg.num_queues, disk_cfg.queue_size, + self.seccomp_action.clone(), ) .map_err(DeviceManagerError::CreateVirtioBlock)?, )); diff --git a/vmm/src/vm.rs b/vmm/src/vm.rs index d7694885d..0e725ff80 100644 --- a/vmm/src/vm.rs +++ b/vmm/src/vm.rs @@ -268,7 +268,7 @@ impl Vm { exit_evt: EventFd, reset_evt: EventFd, vmm_path: PathBuf, - _seccomp_action: &SeccompAction, + seccomp_action: &SeccompAction, hypervisor: Arc, _saved_clock: Option, ) -> Result { @@ -285,6 +285,7 @@ impl Vm { &exit_evt, &reset_evt, vmm_path, + seccomp_action.clone(), ) .map_err(Error::DeviceManager)?;