use std::{mem::size_of, os::fd::RawFd};
use libseccomp::ScmpNotifResp;
use nix::{
errno::Errno,
sys::socket::{getsockopt, sockopt::PeerPidfd},
unistd::Pid,
};
use crate::{
confine::{is_valid_ptr, scmp_arch_is_compat32},
fd::{peer_creds, SafeOwnedFd},
req::UNotifyEventRequest,
};
const SIZEOF_INT_T: usize = size_of::<i32>();
const SIZEOF_FD: usize = size_of::<RawFd>();
pub(crate) fn handle_getsockopt(
fd: SafeOwnedFd,
request: &UNotifyEventRequest,
args: &[u64; 6],
randomize_fds: bool,
) -> Result<ScmpNotifResp, Errno> {
const SOL_SOCKET: u32 = libc::SOL_SOCKET as u32;
const SO_PEERCRED: u32 = libc::SO_PEERCRED as u32;
const SO_PEERPIDFD: u32 = libc::SO_PEERPIDFD as u32;
#[expect(clippy::cast_possible_truncation)]
let level = args[1] as u32;
#[expect(clippy::cast_possible_truncation)]
let optname = args[2] as u32;
if level != SOL_SOCKET {
return Ok(unsafe { request.continue_syscall() });
}
match optname {
SO_PEERCRED => handle_getsockopt_peercred(fd, request, args),
SO_PEERPIDFD => handle_getsockopt_peerpidfd(fd, request, args, randomize_fds),
_ => {
Ok(unsafe { request.continue_syscall() })
}
}
}
fn handle_getsockopt_peercred(
fd: SafeOwnedFd,
request: &UNotifyEventRequest,
args: &[u64; 6],
) -> Result<ScmpNotifResp, Errno> {
let optval_ptr = args[3];
let optlen_ptr = args[4];
if !is_valid_ptr(optlen_ptr, request.scmpreq.data.arch) {
return Err(Errno::EFAULT);
}
let req = request.scmpreq;
let is32 = scmp_arch_is_compat32(req.data.arch);
let mut len_buf = [0u8; SIZEOF_INT_T];
let read = request.read_mem(&mut len_buf, optlen_ptr, SIZEOF_INT_T)?;
if read != SIZEOF_INT_T {
return Err(Errno::EFAULT);
}
let orig_optlen = i32::from_ne_bytes(len_buf);
if orig_optlen < 0 {
return Err(Errno::EINVAL);
}
#[expect(clippy::cast_sign_loss)]
let orig_optlen = orig_optlen as usize;
if !is_valid_ptr(optval_ptr, request.scmpreq.data.arch) && orig_optlen > 0 {
return Err(Errno::EFAULT);
}
let ucred = peer_creds(&fd)?;
let uid = ucred.uid();
let gid = ucred.gid();
let pid = if ucred.pid() != Pid::this().as_raw() {
ucred.pid()
} else {
request.fix_cred_pid(&fd).as_raw()
};
let ucred_size = if is32 {
12usize
} else {
size_of::<libc::ucred>()
};
let to_copy = std::cmp::min(orig_optlen, ucred_size);
if to_copy > 0 {
#[expect(clippy::cast_sign_loss)]
if is32 {
let mut b = [0u8; 12];
b[0..4].copy_from_slice(&(pid as u32).to_ne_bytes());
b[4..8].copy_from_slice(&uid.to_ne_bytes());
b[8..12].copy_from_slice(&gid.to_ne_bytes());
request.write_mem_all(&b[..to_copy], optval_ptr)?;
} else {
let native = libc::ucred { pid, uid, gid };
let native_bytes: &[u8] = unsafe {
std::slice::from_raw_parts(
(&raw const native) as *const u8,
size_of::<libc::ucred>(),
)
};
request.write_mem_all(&native_bytes[..to_copy], optval_ptr)?;
}
}
#[expect(clippy::cast_possible_truncation)]
let buf = (to_copy as u32).to_ne_bytes();
request.write_mem_all(&buf, optlen_ptr)?;
Ok(request.return_syscall(0))
}
fn handle_getsockopt_peerpidfd(
fd: SafeOwnedFd,
request: &UNotifyEventRequest,
args: &[u64; 6],
randomize_fds: bool,
) -> Result<ScmpNotifResp, Errno> {
let optval_ptr = args[3];
let optlen_ptr = args[4];
if !is_valid_ptr(optlen_ptr, request.scmpreq.data.arch) {
return Err(Errno::EFAULT);
}
let mut len_buf = [0u8; SIZEOF_INT_T];
let read = request.read_mem(&mut len_buf, optlen_ptr, SIZEOF_INT_T)?;
if read != SIZEOF_INT_T {
return Err(Errno::EFAULT);
}
let orig_optlen = i32::from_ne_bytes(len_buf);
if orig_optlen < 0 {
return Err(Errno::EINVAL);
}
#[expect(clippy::cast_sign_loss)]
let orig_optlen = orig_optlen as usize;
if !is_valid_ptr(optval_ptr, request.scmpreq.data.arch) && orig_optlen > 0 {
return Err(Errno::EFAULT);
}
let to_copy = orig_optlen.min(SIZEOF_FD);
let ucred = peer_creds(&fd)?;
let pidfd = if ucred.pid() != Pid::this().as_raw() {
getsockopt(&fd, PeerPidfd)?.into()
} else {
request.fix_scm_pidfd(&fd)?
};
let pid0 = 0i32.to_ne_bytes();
request.write_mem_all(&pid0[..to_copy], optval_ptr)?;
let pidfd = request.add_fd(pidfd, true , randomize_fds)?;
let pidfd = pidfd.to_ne_bytes();
request.write_mem_all(&pidfd[..to_copy], optval_ptr)?;
#[expect(clippy::cast_possible_truncation)]
let buf = (to_copy as u32).to_ne_bytes();
request.write_mem_all(&buf, optlen_ptr)?;
Ok(request.return_syscall(0))
}