Skip to content

Commit 4ac1ad4

Browse files
committed
Add support for seccomp_unotify
This allows users to install rules which notify a supervisory process. See: https://man7.org/linux/man-pages/man2/seccomp_unotify.2.html
1 parent 9d0b55c commit 4ac1ad4

File tree

3 files changed

+40
-10
lines changed

3 files changed

+40
-10
lines changed

Cargo.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ edition = "2021"
1111

1212
[features]
1313
json = ["serde", "serde_json"]
14+
seccomp_unotify = []
1415

1516
[dependencies]
1617
libc = "^0.2.153"

src/backend/mod.rs

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,10 @@ use libc::{
2525
SECCOMP_RET_KILL_THREAD, SECCOMP_RET_LOG, SECCOMP_RET_TRACE, SECCOMP_RET_TRAP,
2626
};
2727

28+
// Not available in the libc crate yet.
29+
#[cfg(feature = "seccomp_unotify")]
30+
const SECCOMP_RET_USER_NOTIF: libc::c_uint = 0x7fc00000;
31+
2832
use bpf::{ARG_NUMBER_MAX, AUDIT_ARCH_AARCH64, AUDIT_ARCH_X86_64, BPF_MAX_LEN};
2933

3034
pub use bpf::{sock_filter, BpfProgram, BpfProgramRef};
@@ -162,6 +166,9 @@ pub enum SeccompAction {
162166
Trace(u32),
163167
/// Sends `SIGSYS` to the calling process.
164168
Trap,
169+
/// Sends a notification to a supervisory process.
170+
#[cfg(feature = "seccomp_unotify")]
171+
Notify,
165172
}
166173

167174
impl From<SeccompAction> for u32 {
@@ -181,6 +188,8 @@ impl From<SeccompAction> for u32 {
181188
SeccompAction::Log => SECCOMP_RET_LOG,
182189
SeccompAction::Trace(x) => SECCOMP_RET_TRACE | (x & SECCOMP_RET_DATA),
183190
SeccompAction::Trap => SECCOMP_RET_TRAP,
191+
#[cfg(feature = "seccomp_unotify")]
192+
SeccompAction::Notify => SECCOMP_RET_USER_NOTIF,
184193
}
185194
}
186195
}

src/lib.rs

Lines changed: 30 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -195,6 +195,9 @@ use std::convert::TryInto;
195195
#[cfg(feature = "json")]
196196
use std::io::Read;
197197

198+
#[cfg(feature = "seccomp_unotify")]
199+
use std::os::fd::{FromRawFd as _, OwnedFd};
200+
198201
use std::collections::HashMap;
199202
use std::fmt::{Display, Formatter};
200203
use std::io;
@@ -309,7 +312,8 @@ impl From<JsonFrontendError> for Error {
309312
///
310313
/// [`BpfProgram`]: type.BpfProgram.html
311314
pub fn apply_filter(bpf_filter: BpfProgramRef) -> Result<()> {
312-
apply_filter_with_flags(bpf_filter, 0)
315+
apply_filter_with_flags(bpf_filter, 0)?;
316+
Ok(())
313317
}
314318

315319
/// Apply a BPF filter to the all threads in the process via the TSYNC feature. Please read the
@@ -321,7 +325,28 @@ pub fn apply_filter(bpf_filter: BpfProgramRef) -> Result<()> {
321325
///
322326
/// [`BpfProgram`]: type.BpfProgram.html
323327
pub fn apply_filter_all_threads(bpf_filter: BpfProgramRef) -> Result<()> {
324-
apply_filter_with_flags(bpf_filter, libc::SECCOMP_FILTER_FLAG_TSYNC)
328+
let rc = apply_filter_with_flags(bpf_filter, libc::SECCOMP_FILTER_FLAG_TSYNC)?;
329+
330+
if rc > 0 {
331+
return Err(Error::ThreadSync(rc));
332+
}
333+
334+
Ok(())
335+
}
336+
337+
/// Apply a filter with the SECCOMP_FILTER_FLAG_NEW_LISTENER flag.
338+
///
339+
/// The returned FD can be polled for notifications generated by a rule with the
340+
/// action [SeccompAction::Notify]. See `man 2 seccomp_unotify` for more
341+
/// information.
342+
#[cfg(feature = "seccomp_unotify")]
343+
pub fn apply_filter_with_notify_fd(bpf_filter: BpfProgramRef) -> Result<OwnedFd> {
344+
let rc = apply_filter_with_flags(bpf_filter, libc::SECCOMP_FILTER_FLAG_NEW_LISTENER)?;
345+
346+
// SAFETY: seccomp_unotify documents that it returns a valid FD if the
347+
// syscall is a success.
348+
let fd = unsafe { OwnedFd::from_raw_fd(rc as _) };
349+
Ok(fd)
325350
}
326351

327352
/// Apply a BPF filter to the calling thread.
@@ -332,7 +357,7 @@ pub fn apply_filter_all_threads(bpf_filter: BpfProgramRef) -> Result<()> {
332357
/// * `flags` - A u64 representing a bitset of seccomp's flags parameter.
333358
///
334359
/// [`BpfProgram`]: type.BpfProgram.html
335-
fn apply_filter_with_flags(bpf_filter: BpfProgramRef, flags: libc::c_ulong) -> Result<()> {
360+
fn apply_filter_with_flags(bpf_filter: BpfProgramRef, flags: libc::c_ulong) -> Result<i64> {
336361
// If the program is empty, don't install the filter.
337362
if bpf_filter.is_empty() {
338363
return Err(Error::EmptyFilter);
@@ -363,16 +388,11 @@ fn apply_filter_with_flags(bpf_filter: BpfProgramRef, flags: libc::c_ulong) -> R
363388
)
364389
};
365390

366-
#[allow(clippy::comparison_chain)]
367-
// Per manpage, if TSYNC fails, retcode is >0 and equals the pid of the thread that caused the
368-
// failure. Otherwise, error code is -1 and errno is set.
369391
if rc < 0 {
370-
return Err(Error::Seccomp(io::Error::last_os_error()));
371-
} else if rc > 0 {
372-
return Err(Error::ThreadSync(rc));
392+
return Err(Error::Seccomp(std::io::Error::last_os_error()));
373393
}
374394

375-
Ok(())
395+
Ok(rc)
376396
}
377397

378398
/// Compile [`BpfProgram`]s from JSON.

0 commit comments

Comments
 (0)