Skip to content

Commit adcb2c9

Browse files
authored
Merge pull request #93 from noeljackson/feat/seccomp-bpf
feat(seccomp): add seccomp-bpf filter support with TSYNC
2 parents 86aaee2 + fdddbb8 commit adcb2c9

File tree

4 files changed

+57
-0
lines changed

4 files changed

+57
-0
lines changed

src/config.rs

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
use crate::caps::CapabilityBit;
22
use crate::namespace::Namespace;
3+
use crate::seccomp::SeccompFilter;
34
use anyhow::{Result, bail};
45
use libc::{gid_t, pid_t, uid_t};
56
use serde::{Deserialize, Serialize};
@@ -74,6 +75,12 @@ pub struct ExecutableSpec {
7475
/// If `true`, sets `PR_SET_NO_NEW_PRIVS` before
7576
/// spawning the target executable.
7677
pub no_new_privs: bool,
78+
79+
/// An optional seccomp-bpf filter program. Applied after capabilities
80+
/// are set and `PR_SET_NO_NEW_PRIVS` is enabled, but before `execvpe()`.
81+
/// Requires `no_new_privs = true`.
82+
#[serde(default)]
83+
pub seccomp: Option<SeccompFilter>,
7784
}
7885

7986
#[derive(Default, Debug, Serialize, Deserialize)]

src/lib.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ pub mod config;
44
pub mod mount;
55
pub mod namespace;
66
pub mod runner;
7+
pub mod seccomp;
78
pub mod signal;
89
pub mod unshare;
910
pub mod wrap;

src/seccomp.rs

Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
/// A seccomp-bpf filter program.
2+
///
3+
/// The caller builds the BPF program as a list of `(code, jt, jf, k)`
4+
/// instructions. Styrolite installs it via `seccomp(2)` after
5+
/// capabilities are set but before `execvpe()`.
6+
///
7+
/// Requires `no_new_privs = true` on the `ExecutableSpec`.
8+
#[derive(Clone, Debug, serde::Serialize, serde::Deserialize)]
9+
pub struct SeccompFilter {
10+
/// BPF instructions as `(code, jt, jf, k)` tuples.
11+
pub instructions: Vec<(u16, u8, u8, u32)>,
12+
}
13+
14+
impl SeccompFilter {
15+
/// Install the seccomp filter via `seccomp(2)` with
16+
/// `SECCOMP_FILTER_FLAG_TSYNC`.
17+
///
18+
/// Uses `seccomp(2)` instead of `prctl(PR_SET_SECCOMP)` to synchronize the
19+
/// filter across all threads via `SECCOMP_FILTER_FLAG_TSYNC`.
20+
///
21+
/// # Safety
22+
///
23+
/// Must be called after `prctl(PR_SET_NO_NEW_PRIVS, 1)` and before
24+
/// `execvpe()`. The caller must ensure the BPF program is valid.
25+
pub unsafe fn install(&self) -> std::io::Result<()> {
26+
let filters: Vec<libc::sock_filter> = self
27+
.instructions
28+
.iter()
29+
.map(|&(code, jt, jf, k)| libc::sock_filter { code, jt, jf, k })
30+
.collect();
31+
let prog = libc::sock_fprog {
32+
len: filters.len() as u16,
33+
filter: filters.as_ptr() as *mut _,
34+
};
35+
36+
let ret = unsafe { libc::syscall(libc::SYS_seccomp, 1u64, 1u64, &prog as *const _) };
37+
if ret != 0 {
38+
return Err(std::io::Error::last_os_error());
39+
}
40+
Ok(())
41+
}
42+
}

src/wrap.rs

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -694,6 +694,13 @@ impl ExecutableSpec {
694694
self.set_no_new_privs()?;
695695
}
696696

697+
if let Some(filter) = &self.seccomp {
698+
if !self.no_new_privs {
699+
bail!("seccomp filter requires no_new_privs = true");
700+
}
701+
unsafe { filter.install()? };
702+
}
703+
697704
unsafe {
698705
if libc::execvpe(
699706
program_cstring.as_ptr(),

0 commit comments

Comments
 (0)