Skip to content

Draft: seccomp syscall and ioctls #1458

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Draft
wants to merge 1 commit into
base: main
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -132,7 +132,7 @@ mount = []
net = ["linux-raw-sys/net", "linux-raw-sys/netlink", "linux-raw-sys/if_ether", "linux-raw-sys/xdp"]

# Enable `rustix::thread::*`.
thread = ["linux-raw-sys/prctl"]
thread = ["linux-raw-sys/prctl", "linux-raw-sys/ptrace"]

# Enable `rustix::process::*`.
process = ["linux-raw-sys/prctl"]
Expand Down
19 changes: 19 additions & 0 deletions src/backend/libc/thread/syscalls.rs
Original file line number Diff line number Diff line change
Expand Up @@ -778,3 +778,22 @@ pub(crate) fn membarrier_cpu(cmd: MembarrierCommand, cpu: Cpuid) -> io::Result<(
))
}
}

#[cfg(linux_kernel)]
pub(crate) fn seccomp(
operation: SeccompOperation,
flags: Option<SetSecureComputingModeFilterFlags>,
args: *mut c::c_void,
) -> io::Result<()> {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This function should also be unsafe.

syscall! {
fn seccomp(operation: SeccompOperation, flags: c::c_uint, args: *mut c::c_void) via SYS_seccomp -> c::c_int
}

unsafe {
ret(seccomp(
operation,
flags.map_or(0, |flags| flags.bits()),
args,
))
}
}
9 changes: 9 additions & 0 deletions src/backend/libc/thread/types.rs
Original file line number Diff line number Diff line change
Expand Up @@ -58,3 +58,12 @@ pub(crate) fn raw_cpu_set_new() -> RawCpuSet {

#[cfg(any(freebsdlike, linux_kernel, target_os = "fuchsia"))]
pub(crate) const CPU_SETSIZE: usize = c::CPU_SETSIZE as usize;

#[derive(Clone, Copy, Debug, Eq, PartialEq)]
#[repr(u32)]
pub(crate) enum SeccompOperation {
SetModeStrict = libc::SECCOMP_SET_MODE_STRICT,
SetModeFilter = libc::SECCOMP_SET_MODE_FILTER,
GetActionAvailable = libc::SECCOMP_GET_ACTION_AVAIL,
GetNotifSizes = libc::SECCOMP_GET_NOTIF_SIZES,
}
6 changes: 6 additions & 0 deletions src/backend/linux_raw/c.rs
Original file line number Diff line number Diff line change
Expand Up @@ -367,6 +367,12 @@ mod statx_flags {
STATX_ATTR_VERITY,
};
}
#[cfg(feature = "thread")]
pub(crate) use linux_raw_sys::ptrace::{
sock_filter, sock_fprog, SECCOMP_FILTER_FLAG_LOG, SECCOMP_FILTER_FLAG_NEW_LISTENER,
SECCOMP_FILTER_FLAG_SPEC_ALLOW, SECCOMP_FILTER_FLAG_TSYNC, SECCOMP_FILTER_FLAG_TSYNC_ESRCH,
SECCOMP_FILTER_FLAG_WAIT_KILLABLE_RECV,
};
#[cfg(any(
feature = "fs",
all(
Expand Down
18 changes: 18 additions & 0 deletions src/backend/linux_raw/conv.rs
Original file line number Diff line number Diff line change
Expand Up @@ -666,6 +666,24 @@ impl<'a, Num: ArgNumber> From<crate::thread::Cpuid> for ArgReg<'a, Num> {
}
}

#[cfg(feature = "thread")]
impl<'a, Num: ArgNumber> From<super::thread::types::SeccompOperation> for ArgReg<'a, Num> {
#[inline]
fn from(operation: super::thread::types::SeccompOperation) -> Self {
c_uint(operation as u32)
}
}

#[cfg(feature = "thread")]
impl<'a, Num: ArgNumber> From<Option<crate::thread::SetSecureComputingFilterFlags>>
for ArgReg<'a, Num>
{
#[inline]
fn from(operation: Option<crate::thread::SetSecureComputingFilterFlags>) -> Self {
c_uint(operation.map_or(0, |flags| flags.bits()))
}
}

#[cfg(target_pointer_width = "64")]
#[inline]
pub(super) fn dev_t<'a, Num: ArgNumber>(dev: u64) -> ArgReg<'a, Num> {
Expand Down
12 changes: 11 additions & 1 deletion src/backend/linux_raw/thread/syscalls.rs
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,8 @@ use crate::fd::BorrowedFd;
use crate::io;
use crate::pid::Pid;
use crate::thread::{
futex, ClockId, Cpuid, MembarrierCommand, MembarrierQuery, NanosleepRelativeResult, Timespec,
futex, ClockId, Cpuid, MembarrierCommand, MembarrierQuery, NanosleepRelativeResult,
SetSecureComputingFilterFlags, Timespec,
};
use crate::utils::as_mut_ptr;
use core::mem::MaybeUninit;
Expand Down Expand Up @@ -547,3 +548,12 @@ pub(crate) fn membarrier_cpu(cmd: MembarrierCommand, cpu: Cpuid) -> io::Result<(
))
}
}

#[cfg(linux_kernel)]
pub(crate) fn seccomp(
operation: super::types::SeccompOperation,
flags: Option<SetSecureComputingFilterFlags>,
args: *mut c::c_void,
) -> io::Result<c::c_int> {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This function should be unsafe because it effectively dereferences args.

unsafe { ret_c_int(syscall!(__NR_seccomp, operation, flags, args)) }
}
9 changes: 9 additions & 0 deletions src/backend/linux_raw/thread/types.rs
Original file line number Diff line number Diff line change
Expand Up @@ -60,3 +60,12 @@ pub(crate) fn raw_cpu_set_new() -> RawCpuSet {
}

pub(crate) const CPU_SETSIZE: usize = 8 * core::mem::size_of::<RawCpuSet>();

#[derive(Clone, Copy, Debug, Eq, PartialEq)]
#[repr(u32)]
pub(crate) enum SeccompOperation {
SetModeStrict = linux_raw_sys::ptrace::SECCOMP_SET_MODE_STRICT,
SetModeFilter = linux_raw_sys::ptrace::SECCOMP_SET_MODE_FILTER,
GetActionAvailable = linux_raw_sys::ptrace::SECCOMP_GET_ACTION_AVAIL,
GetNotifSizes = linux_raw_sys::ptrace::SECCOMP_GET_NOTIF_SIZES,
}
4 changes: 4 additions & 0 deletions src/thread/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,8 @@ mod prctl;
mod sched;
mod sched_yield;
#[cfg(linux_kernel)]
mod seccomp;
#[cfg(linux_kernel)]
mod setns;

#[cfg(not(target_os = "redox"))]
Expand All @@ -34,4 +36,6 @@ pub use prctl::*;
pub use sched::*;
pub use sched_yield::sched_yield;
#[cfg(linux_kernel)]
pub use seccomp::*;
#[cfg(linux_kernel)]
pub use setns::*;
4 changes: 4 additions & 0 deletions src/thread/prctl.rs
Original file line number Diff line number Diff line change
Expand Up @@ -168,6 +168,10 @@ const PR_SET_SECCOMP: c_int = 22;
/// - [`prctl(PR_SET_SECCOMP,…)`]
///
/// [`prctl(PR_SET_SECCOMP,…)`]: https://man7.org/linux/man-pages/man2/prctl.2.html
#[deprecated(
since = "1.1.0",
note = "Use set_secure_computing_mode_strict/set_secure_computing_mode_filter"
)]
#[inline]
pub fn set_secure_computing_mode(mode: SecureComputingMode) -> io::Result<()> {
unsafe { prctl_2args(PR_SET_SECCOMP, mode as usize as *mut _) }.map(|_r| ())
Expand Down
73 changes: 73 additions & 0 deletions src/thread/seccomp.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,73 @@
use core::marker::PhantomData;
use core::ptr::null_mut;

use bitflags::bitflags;

use crate::backend::c;
use crate::backend::thread::syscalls;
use crate::backend::thread::types::SeccompOperation;
use crate::io;

bitflags! {
/// fixme
pub struct SetSecureComputingFilterFlags: u32 {
/// fixme
const TSYNC = c::SECCOMP_FILTER_FLAG_TSYNC;
/// fixme
const LOG = c::SECCOMP_FILTER_FLAG_LOG;
/// fixme
const SPEC_ALLOW = c::SECCOMP_FILTER_FLAG_SPEC_ALLOW;
/// fixme
const NEW_LISTENER = c::SECCOMP_FILTER_FLAG_NEW_LISTENER;
/// fixme
const TSYNC_ESRCH = c::SECCOMP_FILTER_FLAG_TSYNC_ESRCH;
/// fixme
const WAIT_KILLABLE_RECV = c::SECCOMP_FILTER_FLAG_WAIT_KILLABLE_RECV;

const _ = !0;
}
}

/// fixme
#[derive(Debug)]
#[repr(transparent)]
pub struct SecureComputingFilterLine(c::sock_filter);

/// fixme
#[derive(Debug)]
#[repr(transparent)]
pub struct SecureComputingFilter<'a>(c::sock_fprog, PhantomData<&'a [SecureComputingFilterLine]>);
impl<'a> From<&'a [SecureComputingFilterLine]> for SecureComputingFilter<'a> {
fn from(filter_lines: &'a [SecureComputingFilterLine]) -> Self {
Self(
c::sock_fprog {
// usize as u16 is lossy. However filter programs with more than BPF_MAXINSNS (4096)
// will be rejected by the kernel with EINVAL.
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If the user passes a filter list that's too long, it seems like it's be better to fail than to silently truncate the list. The user may be assuming that if the call succeeds, the entire filter list is installed.

Copy link
Contributor Author

@rusty-snake rusty-snake May 1, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The kernel will return EINVAL for len > 4096 (~32kB filter code). u16::MAX is 65535 (~500kb filter code). So this is more a theoretical than a practical issue - the filter will get rejected by the kernel. On the other hand u16::try_from(length).map_err(|_| EINVAL)? isn't complicated. edit: This would mean we can not use From. So under the assumption to keep using From we can:

  • u16::try_from(length).unwrap_or(u16::MAX)
  • u16::try_from(length).unwrap_or(0) (Kernel: if (fprog->len == 0 || fprog->len > BPF_MAXINSNS) return ERR_PTR(-EINVAL);)
  • assert!(filter_lines.len() < u16::MAX as usize) as it should not happen in practice.

len: filter_lines.len() as u16,
filter: filter_lines.as_ptr() as *mut c::sock_filter,
},
PhantomData,
)
}
}

/// fixme
pub fn set_secure_computing_mode_strict() -> io::Result<()> {
syscalls::seccomp(SeccompOperation::SetModeStrict, None, null_mut()).map(|_| ())
}

/// fixme
///
/// ... low level interface ... you likely want to use a library like libseccomp.
///
/// ... return value can be anything .... no I/O Safety ...
pub fn set_secure_computing_mode_filter(
filter: &SecureComputingFilter,
flags: SetSecureComputingFilterFlags,
) -> io::Result<i32> {
syscalls::seccomp(
SeccompOperation::SetModeFilter,
Some(flags),
filter as *const _ as *mut c::c_void,
)
}
Loading