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 <chen.bo@intel.com>
This commit is contained in:
Bo Chen 2020-08-03 19:45:53 -07:00 committed by Sebastien Boeuf
parent ff7ed8f628
commit 704edd544c
7 changed files with 125 additions and 4 deletions

View File

@ -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"

View File

@ -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<T: DiskFile> {
queue_size: Vec<u16>,
writeback: Arc<AtomicBool>,
counters: BlockCounters,
seccomp_action: SeccompAction,
}
#[derive(Serialize, Deserialize)]
@ -283,6 +286,7 @@ impl<T: DiskFile> Block<T> {
/// 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<T: DiskFile> Block<T> {
iommu: bool,
num_queues: usize,
queue_size: u16,
seccomp_action: SeccompAction,
) -> io::Result<Block<T>> {
let disk_size = disk_image.seek(SeekFrom::End(0))? as u64;
if disk_size % SECTOR_SIZE != 0 {
@ -343,6 +348,7 @@ impl<T: DiskFile> Block<T> {
queue_size: vec![queue_size; num_queues],
writeback: Arc::new(AtomicBool::new(true)),
counters: BlockCounters::default(),
seccomp_action,
})
}
@ -527,9 +533,19 @@ impl<T: 'static + DiskFile + Send> VirtioDevice for Block<T> {
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);

View File

@ -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;

View File

@ -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>;

View File

@ -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<Vec<SyscallRuleSet>, 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<SeccompFilter, Error> {
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<SeccompFilter, Error> {
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<BpfProgram, SeccompError> {
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),
}
}

View File

@ -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<Arc<Mutex<Self>>> {
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)?,
));

View File

@ -268,7 +268,7 @@ impl Vm {
exit_evt: EventFd,
reset_evt: EventFd,
vmm_path: PathBuf,
_seccomp_action: &SeccompAction,
seccomp_action: &SeccompAction,
hypervisor: Arc<dyn hypervisor::Hypervisor>,
_saved_clock: Option<hypervisor::ClockData>,
) -> Result<Self> {
@ -285,6 +285,7 @@ impl Vm {
&exit_evt,
&reset_evt,
vmm_path,
seccomp_action.clone(),
)
.map_err(Error::DeviceManager)?;