use std::sync::Arc;
use tokio::sync::Mutex;
use crate::seccomp::ctx::SupervisorCtx;
use crate::seccomp::notif::{spawn_pid_watcher, NotifAction, NotifPolicy};
use crate::seccomp::state::ResourceState;
use crate::sys::structs::{
SeccompNotif, CLONE_NS_FLAGS, EAGAIN, EPERM,
};
const CLONE_THREAD: u64 = 0x0001_0000;
const MAP_ANONYMOUS: u64 = 0x20;
pub(crate) async fn handle_fork(
notif: &SeccompNotif,
resource: &Arc<Mutex<ResourceState>>,
_policy: &NotifPolicy,
) -> NotifAction {
let nr = notif.data.nr as i64;
let args = ¬if.data.args;
if nr == libc::SYS_clone || Some(nr) == crate::arch::SYS_VFORK {
if nr == libc::SYS_clone && (args[0] & CLONE_NS_FLAGS) != 0 {
return NotifAction::Errno(EPERM);
}
if nr == libc::SYS_clone && (args[0] & CLONE_THREAD) != 0 {
return NotifAction::Continue;
}
}
let mut rs = resource.lock().await;
if rs.hold_forks {
rs.held_notif_ids.push(notif.id);
return NotifAction::Hold;
}
if rs.proc_count >= rs.max_processes {
return NotifAction::Errno(EAGAIN);
}
rs.proc_count += 1;
NotifAction::Continue
}
pub(crate) async fn register_child_if_new(ctx: &Arc<SupervisorCtx>, pid: i32) {
if ctx.processes.contains(pid) {
return;
}
let pidfd = match crate::sys::syscall::pidfd_open(pid as u32, 0) {
Ok(fd) => fd,
Err(_) => return, };
let key = match ctx.processes.register(pid) {
Some(k) => k,
None => return, };
spawn_pid_watcher(Arc::clone(ctx), key, pidfd);
}
pub(crate) async fn handle_wait(
_notif: &SeccompNotif,
resource: &Arc<Mutex<ResourceState>>,
) -> NotifAction {
let mut rs = resource.lock().await;
rs.proc_count = rs.proc_count.saturating_sub(1);
NotifAction::Continue
}
pub(crate) async fn handle_memory(
notif: &SeccompNotif,
ctx: &Arc<SupervisorCtx>,
policy: &NotifPolicy,
) -> NotifAction {
let nr = notif.data.nr as i64;
let args = ¬if.data.args;
let limit = policy.max_memory_bytes;
let mut st = ctx.resource.lock().await;
let kill = NotifAction::Kill { sig: libc::SIGKILL, pgid: notif.pid as i32 };
if nr == libc::SYS_mmap {
let len = args[1];
let flags = args[3];
if (flags & MAP_ANONYMOUS) != 0 {
if st.mem_used.saturating_add(len) > limit {
return kill;
}
st.mem_used += len;
}
} else if nr == libc::SYS_munmap {
let len = args[1];
st.mem_used = st.mem_used.saturating_sub(len);
} else if nr == libc::SYS_brk {
let new_brk = args[0];
if new_brk == 0 {
return NotifAction::Continue;
}
drop(st);
let entry = match ctx.processes.entry_for(notif.pid as i32) {
Some(e) => e,
None => return NotifAction::Continue,
};
let mut perproc = entry.1.lock().await;
let mut st = ctx.resource.lock().await;
let base = *perproc.brk_base.get_or_insert(new_brk);
if new_brk > base {
let delta = new_brk - base;
if st.mem_used.saturating_add(delta) > limit {
return kill;
}
st.mem_used += delta;
perproc.brk_base = Some(new_brk);
} else if new_brk < base {
let delta = base - new_brk;
st.mem_used = st.mem_used.saturating_sub(delta);
perproc.brk_base = Some(new_brk);
}
} else if nr == libc::SYS_mremap {
let old_len = args[1];
let new_len = args[2];
if new_len > old_len {
let growth = new_len - old_len;
if st.mem_used.saturating_add(growth) > limit {
return kill;
}
st.mem_used += growth;
} else if new_len < old_len {
let shrink = old_len - new_len;
st.mem_used = st.mem_used.saturating_sub(shrink);
}
} else if nr == libc::SYS_shmget {
let size = args[1];
if size > 0 && st.mem_used.saturating_add(size) > limit {
return kill;
}
st.mem_used += size;
}
NotifAction::Continue
}