use libseccomp::ScmpArch;
use nix::{errno::Errno, unistd::Pid};
use crate::{
compat::setgroups_none,
config::NGROUPS_MAX,
confine::{is_valid_ptr, scmp_arch_has_uid16, scmp_arch_raw},
ptrace::{ptrace_set_arg, ptrace_syscall_info_seccomp},
req::RemoteProcess,
warn,
};
pub(crate) fn sysenter_setgroups(
pid: Pid,
arch: ScmpArch,
data: ptrace_syscall_info_seccomp,
) -> Result<(), Errno> {
let is_16 = scmp_arch_has_uid16(arch);
handle_setgroups(pid, "setgroups", is_16, arch, data)
}
pub(crate) fn sysenter_setgroups32(
pid: Pid,
arch: ScmpArch,
data: ptrace_syscall_info_seccomp,
) -> Result<(), Errno> {
handle_setgroups(pid, "setgroups32", false , arch, data)
}
#[expect(clippy::cognitive_complexity)]
fn handle_setgroups(
pid: Pid,
name: &str,
is_16: bool,
arch: ScmpArch,
data: ptrace_syscall_info_seccomp,
) -> Result<(), Errno> {
#[expect(clippy::cast_possible_truncation)]
let count = data.args[0] as u32;
if count > NGROUPS_MAX {
return Err(Errno::EINVAL);
}
let count = count as usize;
if count > 0 {
let list = data.args[1];
if !is_valid_ptr(list, arch) {
return Err(Errno::EFAULT);
}
let process = RemoteProcess::new(pid);
let gids = unsafe { process.remote_gidlist(arch, list, count, is_16) }?;
for gid in &gids {
if *gid == u32::MAX {
return Err(Errno::EINVAL);
}
}
}
if let Err(errno) = setgroups_none() {
if errno != Errno::EPERM {
warn!("ctx": "safesetid", "op": "syd_nogroup",
"err": errno as i32, "sys": name, "pid": pid.as_raw(),
"msg": format!("drop additional groups for Syd failed: {errno}"),
"tip": "check with SYD_LOG=debug and/or submit a bug report");
}
return Err(errno);
}
if let Err(errno) = ptrace_set_arg(pid, scmp_arch_raw(arch), 0, 0) {
if errno != Errno::ESRCH {
warn!("ctx": "safesetid", "op": "set_nogroup",
"err": errno as i32, "sys": name, "pid": pid.as_raw(),
"msg": format!("drop additional groups failed: {errno}"),
"tip": "check with SYD_LOG=debug and/or submit a bug report");
}
return Err(errno);
}
Ok(())
}