dragonball: add seccomp support for dragonball

This commit modifies seccomp framework to
support different restrictions for different threads.

Signed-off-by: wangxinge <wangxinge@bupt.edu.cn>
This commit is contained in:
wangxinge
2025-09-29 13:46:09 +08:00
committed by Xuewei Niu
parent bb6fb8ff39
commit 2abf6965ff
7 changed files with 73 additions and 42 deletions

View File

@@ -344,20 +344,26 @@ name = "dbs-pci"
version = "0.1.0"
dependencies = [
"byteorder",
"dbs-address-space",
"dbs-allocator",
"dbs-arch",
"dbs-boot",
"dbs-device",
"dbs-interrupt",
"dbs-utils",
"dbs-virtio-devices",
"downcast-rs",
"kvm-bindings",
"kvm-ioctls",
"libc",
"log",
"serde",
"thiserror 1.0.48",
"vfio-bindings",
"vfio-ioctls",
"virtio-queue",
"vm-memory",
"vmm-sys-util",
]
[[package]]
@@ -1810,9 +1816,9 @@ checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49"
[[package]]
name = "seccompiler"
version = "0.2.0"
version = "0.5.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e01d1292a1131b22ccea49f30bd106f1238b5ddeec1a98d39268dcc31d540e68"
checksum = "a4ae55de56877481d112a559bbc12667635fdaf5e005712fd4e2b2fa50ffc884"
dependencies = [
"libc",
]

View File

@@ -33,7 +33,7 @@ event-manager = "0.2.1"
kvm-bindings = "0.6.0"
kvm-ioctls = "0.12.0"
linux-loader = "0.8.0"
seccompiler = "0.2.0"
seccompiler = "0.5.0"
vfio-bindings = "0.3.0"
vfio-ioctls = "0.1.0"
virtio-bindings = "0.1.0"

View File

@@ -479,14 +479,13 @@ impl VmmService {
use self::StartMicroVmError::MicroVMAlreadyRunning;
use self::VmmActionError::StartMicroVm;
let vmm_seccomp_filter = vmm.vmm_seccomp_filter();
let vcpu_seccomp_filter = vmm.vcpu_seccomp_filter();
let seccomp_filters = vmm.seccomp_filters();
let vm = vmm.get_vm_mut().ok_or(VmmActionError::InvalidVMID)?;
if vm.is_vm_initialized() {
return Err(StartMicroVm(MicroVMAlreadyRunning));
}
vm.start_microvm(event_mgr, vmm_seccomp_filter, vcpu_seccomp_filter)
vm.start_microvm(event_mgr, seccomp_filters)
.map(|_| VmmData::Empty)
.map_err(StartMicroVm)
}

View File

@@ -219,6 +219,10 @@ pub enum StartMicroVmError {
/// Failed to register DMA memory address range.
#[error("failure while registering DMA address range: {0:?}")]
RegisterDMAAddress(#[source] VfioDeviceError),
/// Cannot build seccomp filters.
#[error("failure while configuring seccomp filters: {0}")]
SeccompFilters(#[source] seccompiler::Error),
}
/// Errors associated with starting the instance.

View File

@@ -48,7 +48,7 @@ mod vmm;
pub use self::error::StartMicroVmError;
pub use self::io_manager::IoManagerCached;
pub use self::vmm::Vmm;
pub use self::vmm::{Vmm, ALL_THREADS, VCPU_THREAD, VMM_THREAD};
/// Success exit code.
pub const EXIT_CODE_OK: u8 = 0;

View File

@@ -1,6 +1,7 @@
// Copyright (C) 2021 Alibaba Cloud. All rights reserved.
// SPDX-License-Identifier: Apache-2.0
use std::collections::HashMap;
use std::io::{self, Read, Seek, SeekFrom};
use std::ops::Deref;
use std::os::unix::io::RawFd;
@@ -18,6 +19,7 @@ use dbs_utils::time::TimestampUs;
use kvm_ioctls::VmFd;
use linux_loader::loader::{KernelLoader, KernelLoaderResult};
use seccompiler::BpfProgram;
use seccompiler::{apply_filter_all_threads, Error as SecError};
use serde_derive::{Deserialize, Serialize};
use slog::{error, info};
use vm_memory::{Bytes, GuestAddress, GuestAddressSpace};
@@ -42,6 +44,7 @@ use crate::resource_manager::ResourceManager;
use crate::vcpu::{VcpuManager, VcpuManagerError};
#[cfg(feature = "hotplug")]
use crate::vcpu::{VcpuResizeError, VcpuResizeInfo};
use crate::{ALL_THREADS, VCPU_THREAD, VMM_THREAD};
#[cfg(target_arch = "aarch64")]
use dbs_arch::gic::Error as GICError;
@@ -709,10 +712,27 @@ impl Vm {
pub fn start_microvm(
&mut self,
event_mgr: &mut EventManager,
vmm_seccomp_filter: BpfProgram,
vcpu_seccomp_filter: BpfProgram,
seccomp_filters: HashMap<String, BpfProgram>,
) -> std::result::Result<(), StartMicroVmError> {
info!(self.logger, "VM: received instance start command");
if let Some(process_seccomp_filter) = seccomp_filters.get(ALL_THREADS) {
// Load seccomp filters for the whole process.
// Execution panics if filters cannot be loaded, use --seccomp-level=0 if skipping filters
// altogether is the desired behaviour.
if let Err(e) = apply_filter_all_threads(process_seccomp_filter) {
if !matches!(e, SecError::EmptyFilter) {
error!(
self.logger,
"VM: failed to apply process-wide seccomp filters: {}", e
);
return Err(StartMicroVmError::SeccompFilters(e));
}
} else {
info!(self.logger, "VM: process-wide seccomp filters applied");
}
}
if self.is_vm_initialized() {
return Err(StartMicroVmError::MicroVMAlreadyRunning);
}
@@ -738,8 +758,14 @@ impl Vm {
AddressManagerError::GuestMemoryNotInitialized,
))?;
self.init_vcpu_manager(vm_as.clone(), vcpu_seccomp_filter)
.map_err(StartMicroVmError::Vcpu)?;
self.init_vcpu_manager(
vm_as.clone(),
seccomp_filters
.get(VCPU_THREAD)
.cloned()
.unwrap_or_default(),
)
.map_err(StartMicroVmError::Vcpu)?;
self.init_microvm(event_mgr.epoll_manager(), vm_as.clone(), request_ts)?;
self.init_configure_system(&vm_as)?;
#[cfg(feature = "dbs-upcall")]
@@ -751,7 +777,7 @@ impl Vm {
info!(self.logger, "VM: start vcpus");
self.vcpu_manager()
.map_err(StartMicroVmError::Vcpu)?
.start_boot_vcpus(vmm_seccomp_filter)
.start_boot_vcpus(seccomp_filters.get(VMM_THREAD).cloned().unwrap_or_default())
.map_err(StartMicroVmError::Vcpu)?;
// Use expect() to crash if the other thread poisoned this lock.

View File

@@ -6,6 +6,7 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the THIRD-PARTY file.
use std::collections::HashMap;
use std::fmt::Formatter;
use std::os::unix::io::RawFd;
use std::sync::{Arc, Mutex, RwLock};
@@ -22,6 +23,19 @@ use crate::event_manager::{EventContext, EventManager};
use crate::vm::Vm;
use crate::{EXIT_CODE_GENERIC_ERROR, EXIT_CODE_OK};
// Thread types to which the seccomp restrictions apply
// Currently the restrictions are applied to the whole process
// More thread types can be added later to accommodate more granular constraints.
/// Union of restrictions for all threads;
/// The corresponding restrictions are applied to the whole process
/// This type should be used with any other thread type which has specific restrictions
pub const ALL_THREADS: &str = "all";
/// VMM thread
pub const VMM_THREAD: &str = "vmm";
/// Vcpu thread
pub const VCPU_THREAD: &str = "vcpu";
/// Global coordinator to manage API servers, virtual machines, upgrade etc.
///
/// Originally firecracker assumes an VMM only manages an VM, and doesn't distinguish VMM and VM.
@@ -41,8 +55,7 @@ pub struct Vmm {
// Will change to a HashMap when enabling 1 VMM with multiple VMs.
vm: Vm,
vcpu_seccomp_filter: BpfProgram,
vmm_seccomp_filter: BpfProgram,
seccomp_filters: HashMap<String, BpfProgram>,
}
impl Vmm {
@@ -50,8 +63,7 @@ impl Vmm {
pub fn new(
api_shared_info: Arc<RwLock<InstanceInfo>>,
api_event_fd: EventFd,
vmm_seccomp_filter: BpfProgram,
vcpu_seccomp_filter: BpfProgram,
seccomp_filters: HashMap<String, BpfProgram>,
kvm_fd: Option<RawFd>,
) -> Result<Self> {
let epoll_manager = EpollManager::default();
@@ -59,8 +71,7 @@ impl Vmm {
api_shared_info,
api_event_fd,
epoll_manager,
vmm_seccomp_filter,
vcpu_seccomp_filter,
seccomp_filters,
kvm_fd,
)
}
@@ -70,8 +81,7 @@ impl Vmm {
api_shared_info: Arc<RwLock<InstanceInfo>>,
api_event_fd: EventFd,
epoll_manager: EpollManager,
vmm_seccomp_filter: BpfProgram,
vcpu_seccomp_filter: BpfProgram,
seccomp_filters: HashMap<String, BpfProgram>,
kvm_fd: Option<RawFd>,
) -> Result<Self> {
let vm = Vm::new(kvm_fd, api_shared_info, epoll_manager.clone())?;
@@ -81,8 +91,7 @@ impl Vmm {
event_ctx,
epoll_manager,
vm,
vcpu_seccomp_filter,
vmm_seccomp_filter,
seccomp_filters,
})
}
@@ -96,14 +105,9 @@ impl Vmm {
Some(&mut self.vm)
}
/// Get the seccomp rules for vCPU threads.
pub fn vcpu_seccomp_filter(&self) -> BpfProgram {
self.vcpu_seccomp_filter.clone()
}
/// Get the seccomp rules for VMM threads.
pub fn vmm_seccomp_filter(&self) -> BpfProgram {
self.vmm_seccomp_filter.clone()
/// Get all seccomp rules.
pub fn seccomp_filters(&self) -> HashMap<String, BpfProgram> {
self.seccomp_filters.clone()
}
/// Run the event loop to service API requests.
@@ -206,14 +210,15 @@ impl std::fmt::Debug for Vmm {
f.debug_struct("Vmm")
.field("event_ctx", &self.event_ctx)
.field("vm", &self.vm.shared_info())
.field("vcpu_seccomp_filter", &self.vcpu_seccomp_filter)
.field("vmm_seccomp_filter", &self.vmm_seccomp_filter)
.field("seccomp_filters", &self.seccomp_filters)
.finish()
}
}
#[cfg(test)]
pub(crate) mod tests {
use std::collections::HashMap;
use test_utils::skip_if_not_root;
use super::*;
@@ -221,17 +226,8 @@ pub(crate) mod tests {
pub fn create_vmm_instance(epoll_manager: EpollManager) -> Vmm {
let info = Arc::new(RwLock::new(InstanceInfo::default()));
let event_fd = EventFd::new(libc::EFD_NONBLOCK).unwrap();
let seccomp_filter: BpfProgram = Vec::new();
Vmm::new_with_epoll_manager(
info,
event_fd,
epoll_manager,
seccomp_filter.clone(),
seccomp_filter,
None,
)
.unwrap()
Vmm::new_with_epoll_manager(info, event_fd, epoll_manager, HashMap::new(), None).unwrap()
}
#[test]