use libseccomp::ScmpNotifResp;
use nix::errno::Errno;
use crate::{
compat::SHM_EXEC, config::SHM_UNSAFE_MASK, req::UNotifyEventRequest, sandbox::Action, warn,
};
const O_CREAT: u64 = libc::O_CREAT as u64;
const SHM_X: u64 = SHM_EXEC as u64;
const IPC_64: u64 = 0x100;
const IPC_SET: u64 = crate::compat::IPC_SET as u64;
const MSG_STAT_ANY: u64 = 13;
const SEM_STAT_ANY: u64 = 20;
const SHM_STAT_ANY: u64 = 15;
const MSG_COPY: u64 = 0o40000;
pub(crate) fn sys_ipc(request: UNotifyEventRequest) -> ScmpNotifResp {
let req = request.scmpreq;
let call = req.data.args[0] & 0xffff;
let version = req.data.args[0] >> 16;
if call == 21 && version == 1 {
return request.fail_syscall(Errno::EINVAL);
}
match call {
2 => syscall_semget_handler(request, req.data.args[3]),
3 => syscall_semctl_handler(request, req.data.args[3]),
12 => syscall_msgrcv_handler(request, req.data.args[3]),
13 => syscall_msgget_handler(request, req.data.args[2]),
14 => syscall_msgctl_handler(request, req.data.args[2]),
21 => syscall_shmat_handler(request, req.data.args[2]),
23 => syscall_shmget_handler(request, req.data.args[3]),
24 => syscall_shmctl_handler(request, req.data.args[2]),
_ => {
unsafe { request.continue_syscall() }
}
}
}
pub(crate) fn sys_shmat(request: UNotifyEventRequest) -> ScmpNotifResp {
let req = request.scmpreq;
syscall_shmat_handler(request, req.data.args[2])
}
pub(crate) fn sys_msgctl(request: UNotifyEventRequest) -> ScmpNotifResp {
let req = request.scmpreq;
syscall_msgctl_handler(request, req.data.args[1])
}
pub(crate) fn sys_semctl(request: UNotifyEventRequest) -> ScmpNotifResp {
let req = request.scmpreq;
syscall_semctl_handler(request, req.data.args[2])
}
pub(crate) fn sys_shmctl(request: UNotifyEventRequest) -> ScmpNotifResp {
let req = request.scmpreq;
syscall_shmctl_handler(request, req.data.args[1])
}
pub(crate) fn sys_msgget(request: UNotifyEventRequest) -> ScmpNotifResp {
let req = request.scmpreq;
syscall_msgget_handler(request, req.data.args[1])
}
pub(crate) fn sys_msgrcv(request: UNotifyEventRequest) -> ScmpNotifResp {
let req = request.scmpreq;
syscall_msgrcv_handler(request, req.data.args[4])
}
pub(crate) fn sys_semget(request: UNotifyEventRequest) -> ScmpNotifResp {
let req = request.scmpreq;
syscall_semget_handler(request, req.data.args[2])
}
pub(crate) fn sys_shmget(request: UNotifyEventRequest) -> ScmpNotifResp {
let req = request.scmpreq;
syscall_shmget_handler(request, req.data.args[2])
}
fn syscall_shmat_handler(request: UNotifyEventRequest, flags: u64) -> ScmpNotifResp {
if flags & SHM_X == 0 {
return unsafe { request.continue_syscall() };
}
let act = Action::Kill;
let req = request.scmpreq;
warn!("ctx": "ipc", "op": "check_shm",
"sys": "shmat", "shm_flags": flags,
"act": act, "pid": req.pid,
"msg": "unsafe shmat call with SHM_EXEC",
"tip": "configure `trace/allow_unsafe_perm_shm:1'");
let _ = request.kill(act);
request.fail_syscall(Errno::EACCES)
}
fn syscall_msgctl_handler(request: UNotifyEventRequest, cmd: u64) -> ScmpNotifResp {
let cmd = to_ipc_cmd(cmd);
if !matches!(cmd, IPC_SET | MSG_STAT_ANY) {
return unsafe { request.continue_syscall() };
}
let act = Action::Kill;
let req = request.scmpreq;
warn!("ctx": "ipc", "op": "check_shm",
"sys": "msgctl", "msg_cmd": cmd,
"act": act, "pid": req.pid,
"msg": "unsafe msgctl call",
"tip": "configure `trace/allow_unsafe_perm_shm:1'");
let _ = request.kill(act);
request.fail_syscall(Errno::EACCES)
}
fn syscall_semctl_handler(request: UNotifyEventRequest, cmd: u64) -> ScmpNotifResp {
let cmd = to_ipc_cmd(cmd);
if !matches!(cmd, IPC_SET | SEM_STAT_ANY) {
return unsafe { request.continue_syscall() };
}
let act = Action::Kill;
let req = request.scmpreq;
warn!("ctx": "ipc", "op": "check_shm",
"sys": "semctl", "sem_cmd": cmd,
"act": act, "pid": req.pid,
"msg": "unsafe semctl call",
"tip": "configure `trace/allow_unsafe_perm_shm:1'");
let _ = request.kill(act);
request.fail_syscall(Errno::EACCES)
}
fn syscall_shmctl_handler(request: UNotifyEventRequest, cmd: u64) -> ScmpNotifResp {
let cmd = to_ipc_cmd(cmd);
if !matches!(cmd, IPC_SET | SHM_STAT_ANY) {
return unsafe { request.continue_syscall() };
}
let act = Action::Kill;
let req = request.scmpreq;
warn!("ctx": "ipc", "op": "check_shm",
"sys": "shmctl", "shm_cmd": cmd,
"act": act, "pid": req.pid,
"msg": "unsafe shmctl call",
"tip": "configure `trace/allow_unsafe_perm_shm:1'");
let _ = request.kill(act);
request.fail_syscall(Errno::EACCES)
}
fn syscall_msgrcv_handler(request: UNotifyEventRequest, flags: u64) -> ScmpNotifResp {
if flags & MSG_COPY == 0 {
return unsafe { request.continue_syscall() };
}
let sandbox = request.get_sandbox();
let allow_copy = sandbox.options.allow_unsafe_copy();
drop(sandbox); if allow_copy {
return unsafe { request.continue_syscall() };
}
let act = Action::Kill;
let req = request.scmpreq;
warn!("ctx": "ipc", "op": "check_shm",
"sys": "msgrcv", "msg_flags": flags,
"act": act, "pid": req.pid,
"msg": "unsafe msgrcv MSG_COPY call",
"tip": "configure `trace/allow_unsafe_copy:1'");
let _ = request.kill(act);
request.fail_syscall(Errno::EACCES)
}
fn syscall_msgget_handler(request: UNotifyEventRequest, flags: u64) -> ScmpNotifResp {
if flags & SHM_UNSAFE_MASK == 0 {
return unsafe { request.continue_syscall() };
}
let act = Action::Kill;
let req = request.scmpreq;
warn!("ctx": "ipc", "op": "check_shm",
"sys": "msgget", "msg_flags": flags,
"act": act, "pid": req.pid,
"msg": "unsafe msgget call",
"tip": "configure `trace/allow_unsafe_perm_shm:1'");
let _ = request.kill(act);
request.fail_syscall(Errno::EACCES)
}
fn syscall_semget_handler(request: UNotifyEventRequest, flags: u64) -> ScmpNotifResp {
if flags & SHM_UNSAFE_MASK == 0 {
return unsafe { request.continue_syscall() };
}
let act = Action::Kill;
let req = request.scmpreq;
warn!("ctx": "ipc", "op": "check_shm",
"sys": "semget", "sem_flags": flags,
"act": act, "pid": req.pid,
"msg": "unsafe semget call",
"tip": "configure `trace/allow_unsafe_perm_shm:1'");
let _ = request.kill(act);
request.fail_syscall(Errno::EACCES)
}
fn syscall_shmget_handler(request: UNotifyEventRequest, flags: u64) -> ScmpNotifResp {
if flags & SHM_UNSAFE_MASK == 0 {
return unsafe { request.continue_syscall() };
}
let act = Action::Kill;
let req = request.scmpreq;
warn!("ctx": "ipc", "op": "check_shm",
"sys": "shmget", "shm_flags": flags,
"act": act, "pid": req.pid,
"msg": "unsafe shmget call",
"tip": "configure `trace/allow_unsafe_perm_shm:1'");
let _ = request.kill(act);
request.fail_syscall(Errno::EACCES)
}
pub(crate) fn sys_mq_open(request: UNotifyEventRequest) -> ScmpNotifResp {
let req = request.scmpreq;
let oflag = req.data.args[1];
if oflag & O_CREAT == 0 {
return unsafe { request.continue_syscall() };
}
let mode = req.data.args[2];
if mode & SHM_UNSAFE_MASK == 0 {
return unsafe { request.continue_syscall() };
}
let act = Action::Kill;
warn!("ctx": "ipc", "op": "check_shm",
"sys": "mq_open", "oflag": oflag, "mode": mode,
"act": act, "pid": req.pid,
"msg": "unsafe mq_open call",
"tip": "configure `trace/allow_unsafe_perm_msgqueue:1'");
let _ = request.kill(act);
request.fail_syscall(Errno::EACCES)
}
#[expect(clippy::cast_possible_truncation)]
const fn to_ipc_cmd(op: u64) -> u64 {
(op as u32 & !(IPC_64 as u32)) as u64
}