//
// Syd: rock-solid application kernel
// src/cookie.rs: Syscall argument cookies
//
// Copyright (c) 2025, 2026 Ali Polatel <alip@chesswob.org>
//
// SPDX-License-Identifier: GPL-3.0

use std::{
    ffi::CStr,
    mem::MaybeUninit,
    num::NonZeroUsize,
    os::fd::{AsFd, AsRawFd, FromRawFd, RawFd},
    ptr,
    ptr::NonNull,
    sync::LazyLock,
};

use libc::{
    accept4, c_char, c_int, c_long, c_uint, c_void, dev_t, gid_t, mode_t, off64_t, off_t, shutdown,
    sockaddr, socket, socketpair, socklen_t, syscall, uid_t, utimensat, SYS_close, SYS_close_range,
    SYS_execveat, SYS_faccessat2, SYS_fchdir, SYS_fchmod, SYS_fchmodat, SYS_fchown, SYS_fchownat,
    SYS_fremovexattr, SYS_fsetxattr, SYS_getdents64, SYS_linkat, SYS_lremovexattr, SYS_lsetxattr,
    SYS_memfd_create, SYS_mkdirat, SYS_mknodat, SYS_openat2, SYS_pidfd_getfd, SYS_pidfd_open,
    SYS_pidfd_send_signal, SYS_pipe2, SYS_ptrace, SYS_renameat2, SYS_symlinkat, SYS_umask,
    SYS_uname, SYS_unlinkat, SHUT_RD, SHUT_RDWR, SHUT_WR,
};
use nix::{
    errno::Errno,
    fcntl::{AtFlags, OFlag},
    sys::{
        mman::{mmap_anonymous, MapFlags, ProtFlags},
        socket::{
            bind, connect, AddressFamily, Shutdown, SockFlag, SockType, SockaddrLike,
            SockaddrStorage,
        },
        stat::{Mode, SFlag},
    },
    unistd::{AccessFlags, Gid, Pid, Uid, UnlinkatFlags},
    NixPath,
};

use crate::{
    compat::{
        set_vma_anon_name, FallocateFlags, MFdFlags, OpenHow, RenameFlags, SecretMemFlags,
        SYS_REMOVEXATTRAT,
    },
    config::HAVE_PIDFD_THREAD,
    confine::resolve_syscall,
    fd::{SafeOwnedFd, AT_EXECVE_CHECK, PIDFD_THREAD},
    path::{empty_argv, empty_envp, empty_path},
    proc::proc_tgid,
    ptrace::PtraceRequest,
    rng::fillrandom,
    sealbox::{getpagesize, mprotect_none, mprotect_readonly, mseal},
    uts::UtsName,
};

/// A platform-sized secure cookie
///
/// 32 bits on 32-bit, 64 bits on 64-bit targets.
#[cfg(target_pointer_width = "32")]
pub(crate) type Cookie = u32;
#[cfg(target_pointer_width = "64")]
pub(crate) type Cookie = u64;

/// Cookie index enumeration for accessing the sealed pool.
#[repr(usize)]
#[derive(Debug, Clone, Copy)]
#[expect(missing_docs)]
pub enum CookieIdx {
    Accept4Arg4 = 0,
    Accept4Arg5,
    BindArg3,
    BindArg4,
    BindArg5,
    CloseArg1,
    CloseArg2,
    CloseArg3,
    CloseArg4,
    CloseArg5,
    CloseRangeArg3,
    CloseRangeArg4,
    CloseRangeArg5,
    ConnectArg3,
    ConnectArg4,
    ConnectArg5,
    ExecveatArg5,
    Faccessat2Arg4,
    Faccessat2Arg5,
    FallocateArg4,
    FallocateArg5,
    FchdirArg1,
    FchdirArg2,
    FchdirArg3,
    FchdirArg4,
    FchdirArg5,
    FchmodArg2,
    FchmodArg3,
    FchmodArg4,
    FchmodArg5,
    Fchmodat2Arg4,
    Fchmodat2Arg5,
    FchmodatArg3,
    FchmodatArg4,
    FchmodatArg5,
    FchownArg3,
    FchownArg4,
    FchownArg5,
    FchownatArg5,
    FremovexattrArg2,
    FremovexattrArg3,
    FremovexattrArg4,
    FremovexattrArg5,
    FsetxattrArg5,
    Ftruncate64Arg3,
    Ftruncate64Arg4,
    Ftruncate64Arg5,
    FtruncateArg2,
    FtruncateArg3,
    FtruncateArg4,
    FtruncateArg5,
    Getdents64Arg3,
    Getdents64Arg4,
    Getdents64Arg5,
    LinkatArg5,
    LremovexattrArg2,
    LremovexattrArg3,
    LremovexattrArg4,
    LremovexattrArg5,
    LsetxattrArg5,
    MemfdCreateArg2,
    MemfdCreateArg3,
    MemfdCreateArg4,
    MemfdCreateArg5,
    MemfdSecretArg1,
    MemfdSecretArg2,
    MemfdSecretArg3,
    MemfdSecretArg4,
    MemfdSecretArg5,
    MkdiratArg3,
    MkdiratArg4,
    MkdiratArg5,
    MknodatArg4,
    MknodatArg5,
    Openat2Arg4,
    Openat2Arg5,
    PidfdGetInfoArg3,
    PidfdGetInfoArg4,
    PidfdGetInfoArg5,
    PidfdGetfdArg3,
    PidfdGetfdArg4,
    PidfdGetfdArg5,
    PidfdOpenArg2,
    PidfdOpenArg3,
    PidfdOpenArg4,
    PidfdOpenArg5,
    PidfdSendSignalArg4,
    PidfdSendSignalArg5,
    Pipe2Arg2,
    Pipe2Arg3,
    Pipe2Arg4,
    Pipe2Arg5,
    ProcmapQueryArg3,
    ProcmapQueryArg4,
    ProcmapQueryArg5,
    PtraceArg4,
    PtraceArg5,
    RemovexattratArg4,
    RemovexattratArg5,
    Renameat2Arg5,
    SeccompIoctlNotifAddfdArg3,
    SeccompIoctlNotifAddfdArg4,
    SeccompIoctlNotifAddfdArg5,
    SeccompIoctlNotifSendArg3,
    SeccompIoctlNotifSendArg4,
    SeccompIoctlNotifSendArg5,
    Sendfile64Arg4,
    Sendfile64Arg5,
    SendfileArg4,
    SendfileArg5,
    ShutdownArg2,
    ShutdownArg3,
    ShutdownArg4,
    ShutdownArg5,
    SocketArg3,
    SocketArg4,
    SocketArg5,
    SocketpairArg4,
    SocketpairArg5,
    SymlinkatArg3,
    SymlinkatArg4,
    SymlinkatArg5,
    Truncate64Arg3,
    Truncate64Arg4,
    Truncate64Arg5,
    TruncateArg2,
    TruncateArg3,
    TruncateArg4,
    TruncateArg5,
    UmaskArg1,
    UmaskArg2,
    UmaskArg3,
    UmaskArg4,
    UmaskArg5,
    UnameArg1,
    UnameArg2,
    UnameArg3,
    UnameArg4,
    UnameArg5,
    UnlinkatArg3,
    UnlinkatArg4,
    UnlinkatArg5,
}

impl CookieIdx {
    /// Number of cookie indices.
    pub const COUNT: usize = Self::UnlinkatArg5 as usize + 1;
}

/// The sealed syscall cookie pool containing all syscall argument cookies in a
/// single, hardened memory region. This region is:
/// 1. Allocated with mmap as a single contiguous anonymous mapping
/// 2. Guarded by PROT_NONE pages from below and above
/// 3. Populated with a single fillrandom(2) call
/// 4. Made read-only with mprotect(PROT_READ)
/// 5. Named "syd" via prctl(PR_SET_VMA)
/// 6. Sealed with mseal(2) to prevent remapping
///
/// # Invariants
///
/// `ptr` points into a PROT_READ mapping containing CookieIdx::COUNT
/// contiguous Cookie values. The mapping layout is
/// [PROT_NONE guard][PROT_READ data][PROT_NONE guard] and is never
/// munmap'd after initialization.
pub struct SyscookiePool {
    /// Raw pointer into the data region (after the lower guard page).
    ptr: *const Cookie,
    /// Full mapping pointer (including guards) for bookkeeping.
    #[expect(dead_code)]
    map_ptr: NonNull<c_void>,
    /// Total mapping length (guards + data region).
    #[expect(dead_code)]
    map_len: NonZeroUsize,
}

// SAFETY: SyscookiePool is read-only after initialization and sealed with mseal(2).
unsafe impl Sync for SyscookiePool {}
// SAFETY: SyscookiePool is read-only after initialization and sealed with mseal(2).
unsafe impl Send for SyscookiePool {}

/// VMA name for the cookie pool.
const VMA_NAME: &CStr = c" Syd: cookie/pool";

impl SyscookiePool {
    // Allocate and initialize the cookie pool.
    fn new() -> Result<Self, Errno> {
        let page = getpagesize()?;
        let cookie_size = size_of::<Cookie>();
        let data_size = cookie_size
            .checked_mul(CookieIdx::COUNT)
            .ok_or(Errno::EINVAL)?;
        let data_pages = data_size
            .checked_next_multiple_of(page)
            .ok_or(Errno::EINVAL)?;

        // Total layout: [guard page]+[data pages]+[guard page]
        let total_size = page
            .checked_add(data_pages)
            .and_then(|s| s.checked_add(page))
            .ok_or(Errno::EINVAL)?;
        let map_len = NonZeroUsize::new(total_size).ok_or(Errno::EINVAL)?;

        // Allocate the entire region as PROT_READ | PROT_WRITE initially.
        //
        // SAFETY: Valid length and flags guaranteed.
        let map_ptr = unsafe {
            mmap_anonymous(
                None,
                map_len,
                ProtFlags::PROT_READ | ProtFlags::PROT_WRITE,
                MapFlags::MAP_PRIVATE,
            )?
        };

        // Calculate the data region pointer (after lower guard).
        //
        // SAFETY: We allocated at least page + data_pages + page bytes.
        let data_ptr = unsafe { map_ptr.as_ptr().add(page) };

        // Fill the data region with random bytes using a single getrandom(2) call.
        //
        // SAFETY: data_ptr is valid for data_pages bytes.
        let data_slice =
            unsafe { std::slice::from_raw_parts_mut(data_ptr.cast::<u8>(), data_pages) };
        fillrandom(data_slice)?;

        // Make the lower guard page PROT_NONE.
        let guard_len = NonZeroUsize::new(page).ok_or(Errno::EINVAL)?;
        mprotect_none(map_ptr, guard_len)?;

        // Make the upper guard page PROT_NONE.
        //
        // SAFETY: map_ptr + page + data_pages is within the allocated region.
        let upper_guard_ptr = unsafe {
            NonNull::new_unchecked(map_ptr.as_ptr().add(page).add(data_pages).cast::<c_void>())
        };
        mprotect_none(upper_guard_ptr, guard_len)?;

        // Make data region read-only.
        let data_region = NonZeroUsize::new(data_pages).ok_or(Errno::EINVAL)?;
        // SAFETY: map_ptr + page is the start of the data region.
        let data_region_ptr =
            unsafe { NonNull::new_unchecked(map_ptr.as_ptr().add(page).cast::<c_void>()) };
        mprotect_readonly(data_region_ptr, data_region)?;

        // Set VMA name to "syd::cookie::pool" in debug mode.
        let _ = set_vma_anon_name(data_region_ptr, data_region, Some(VMA_NAME));

        // Seal the entire mapping to prevent remapping.
        //
        // ENOSYS: mseal(2) not implemented (Linux >= 6.10)
        // EPERM: Sealing only supported on 64-bit CPUs.
        match mseal(map_ptr, map_len) {
            Ok(_) | Err(Errno::EPERM | Errno::ENOSYS) => {}
            Err(errno) => return Err(errno),
        }

        // INVARIANT: ptr points to the first Cookie in the data region.
        Ok(SyscookiePool {
            ptr: data_ptr.cast::<Cookie>(),
            map_ptr,
            map_len,
        })
    }

    /// Get a cookie by index.
    #[inline(always)]
    pub fn get(&self, idx: CookieIdx) -> Cookie {
        // SAFETY: Index is bounds-checked by enum, data is read-only.
        unsafe { *self.ptr.add(idx as usize) }
    }
}

/// The global sealed syscall cookie pool.
#[expect(clippy::disallowed_methods)]
pub static SYSCOOKIE_POOL: LazyLock<SyscookiePool> =
    LazyLock::new(|| SyscookiePool::new().expect("failed to initialize syscall cookie pool"));

/// Safe close(2) confined by syscall cookies.
#[inline(always)]
pub(crate) fn safe_close(fd: RawFd) -> Result<(), Errno> {
    // SAFETY: `close(2)`: `fd` is a valid file descriptor;
    // all remaining args are sealed cookies.
    Errno::result(unsafe {
        syscall(
            SYS_close,
            fd,
            SYSCOOKIE_POOL.get(CookieIdx::CloseArg1),
            SYSCOOKIE_POOL.get(CookieIdx::CloseArg2),
            SYSCOOKIE_POOL.get(CookieIdx::CloseArg3),
            SYSCOOKIE_POOL.get(CookieIdx::CloseArg4),
            SYSCOOKIE_POOL.get(CookieIdx::CloseArg5),
        )
    })
    .map(drop)
}

/// Safe close_range(2) confined by syscall cookies.
#[inline(always)]
pub(crate) fn safe_close_range(first: c_uint, last: c_uint, flags: c_uint) -> Result<(), Errno> {
    // SAFETY: `close_range(2)`: `first`/`last` are valid fd range
    // bounds; `flags` is caller-provided; trailing args are sealed cookies.
    Errno::result(unsafe {
        syscall(
            SYS_close_range,
            first,
            last,
            flags,
            SYSCOOKIE_POOL.get(CookieIdx::CloseRangeArg3),
            SYSCOOKIE_POOL.get(CookieIdx::CloseRangeArg4),
            SYSCOOKIE_POOL.get(CookieIdx::CloseRangeArg5),
        )
    })
    .map(drop)
}

/// Safe openat2(2) confined by syscall cookies.
#[inline(always)]
pub(crate) fn safe_openat2<Fd: AsFd, P: NixPath + ?Sized>(
    dirfd: Fd,
    path: &P,
    mut how: OpenHow,
) -> Result<SafeOwnedFd, Errno> {
    let res = path.with_nix_path(|cstr| {
        // SAFETY: `openat2(2)`: `dirfd` is valid via `AsFd`;
        // `cstr` is a NUL-terminated CStr from NixPath;
        // `how` is a valid `OpenHow` struct; trailing args are sealed cookies.
        unsafe {
            syscall(
                SYS_openat2,
                dirfd.as_fd().as_raw_fd(),
                cstr.as_ptr(),
                ptr::addr_of_mut!(how),
                size_of::<OpenHow>(),
                SYSCOOKIE_POOL.get(CookieIdx::Openat2Arg4),
                SYSCOOKIE_POOL.get(CookieIdx::Openat2Arg5),
            )
        }
    })?;

    // SAFETY:
    //
    // `openat2(2)` should return a valid fd on success.
    #[expect(clippy::cast_possible_truncation)]
    Errno::result(res).map(|r| unsafe { SafeOwnedFd::from_raw_fd(r as RawFd) })
}

/// socket(2) may be multiplexed by socketcall(2).
pub static SYS_SOCKET: LazyLock<Option<c_long>> = LazyLock::new(|| resolve_syscall("socket"));

/// Safe socket(2) confined by syscall cookies.
#[inline(always)]
pub fn safe_socket(domain: c_int, stype: c_int, proto: c_int) -> Result<SafeOwnedFd, Errno> {
    if let Some(sys_socket) = *SYS_SOCKET {
        // SAFETY: `socket(2)`: `domain`/`stype`/`proto` are
        // caller-provided c_int values; trailing args are sealed cookies.
        #[expect(clippy::cast_possible_truncation)]
        Errno::result(unsafe {
            syscall(
                sys_socket,
                domain,
                stype,
                proto,
                SYSCOOKIE_POOL.get(CookieIdx::SocketArg3),
                SYSCOOKIE_POOL.get(CookieIdx::SocketArg4),
                SYSCOOKIE_POOL.get(CookieIdx::SocketArg5),
            )
        })
        .map(|fd| fd as RawFd)
    } else {
        // SAFETY:
        // socketcall(2) on multiplexed architecture.
        // We use libc version for convenience.
        Errno::result(unsafe { socket(domain, stype, proto) })
    }
    .map(|fd| {
        // SAFETY: socket returns a valid FD on success.
        unsafe { SafeOwnedFd::from_raw_fd(fd) }
    })
}

/// socketpair(2) may be multiplexed by socketcall(2).
pub static SYS_SOCKETPAIR: LazyLock<Option<c_long>> =
    LazyLock::new(|| resolve_syscall("socketpair"));

/// Safe socketpair(2) confined by syscall cookies.
#[inline(always)]
pub fn safe_socketpair(
    domain: AddressFamily,
    ty: SockType,
    proto: c_int,
    flags: SockFlag,
) -> Result<(SafeOwnedFd, SafeOwnedFd), Errno> {
    // Merge SockType and SockFlags together.
    let mut ty = ty as c_int;
    ty |= flags.bits();

    let mut fds = [-1, -1];

    if let Some(sys_socketpair) = *SYS_SOCKETPAIR {
        // SAFETY: `socketpair(2)`: `domain`/`ty`/`proto` are valid;
        // `fds` is a writable 2-element array; trailing args are sealed cookies.
        Errno::result(unsafe {
            syscall(
                sys_socketpair,
                domain as c_int,
                ty,
                proto,
                fds.as_mut_ptr(),
                SYSCOOKIE_POOL.get(CookieIdx::SocketpairArg4),
                SYSCOOKIE_POOL.get(CookieIdx::SocketpairArg5),
            )
        })?;
    } else {
        // SAFETY:
        // socketcall(2) on multiplexed architecture.
        // We use libc version for convenience.
        Errno::result(unsafe { socketpair(domain as c_int, ty, proto, fds.as_mut_ptr()) })?;
    }

    // SAFETY: socketpair returns valid FDs on success.
    unsafe {
        Ok((
            SafeOwnedFd::from_raw_fd(fds[0]),
            SafeOwnedFd::from_raw_fd(fds[1]),
        ))
    }
}

/// accept4(2) may be multiplexed by socketcall(2).
pub static SYS_ACCEPT4: LazyLock<Option<c_long>> = LazyLock::new(|| resolve_syscall("accept4"));

/// `SockaddrStorage` with added size information.
#[derive(Debug)]
pub struct SizedSockaddrStorage {
    /// Address buffer
    pub addr: SockaddrStorage,
    /// Address size
    pub size: socklen_t,
}

/// Safe accept4(2) confined by syscall cookies.
///
/// When `want_src_addr` is true, returns the peer's source address and its length.
#[inline(always)]
pub(crate) fn safe_accept4<Fd: AsFd>(
    fd: Fd,
    flags: SockFlag,
    want_src_addr: bool,
) -> Result<(SafeOwnedFd, Option<SizedSockaddrStorage>), Errno> {
    if want_src_addr {
        let mut storage = MaybeUninit::<libc::sockaddr_storage>::zeroed();
        #[expect(clippy::cast_possible_truncation)]
        let mut size = size_of::<libc::sockaddr_storage>() as socklen_t;

        let fd = do_accept4(fd, storage.as_mut_ptr().cast(), &raw mut size, flags)?;

        // SAFETY: accept4 returned success; storage is initialised up to `size` bytes.
        let addr = unsafe { SockaddrStorage::from_raw(storage.as_ptr().cast(), Some(size)) }
            .ok_or(Errno::EINVAL)?;
        let addr = SizedSockaddrStorage { addr, size };

        Ok((fd, Some(addr)))
    } else {
        let fd = do_accept4(fd, ptr::null_mut(), ptr::null_mut(), flags)?;
        Ok((fd, None))
    }
}

/// accept4(2) syscall with cookie confinement.
#[inline(always)]
fn do_accept4<Fd: AsFd>(
    fd: Fd,
    addr: *mut sockaddr,
    len: *mut socklen_t,
    flags: SockFlag,
) -> Result<SafeOwnedFd, Errno> {
    if let Some(sys_accept4) = *SYS_ACCEPT4 {
        // SAFETY: `fd` valid via `AsFd`; `addr`/`len` are valid or NULL;
        // trailing args are sealed cookies.
        #[expect(clippy::cast_possible_truncation)]
        Errno::result(unsafe {
            syscall(
                sys_accept4,
                fd.as_fd().as_raw_fd(),
                addr,
                len,
                flags.bits(),
                SYSCOOKIE_POOL.get(CookieIdx::Accept4Arg4),
                SYSCOOKIE_POOL.get(CookieIdx::Accept4Arg5),
            )
        })
        .map(|fd| {
            // SAFETY: accept4(2) returns a valid fd on success.
            unsafe { SafeOwnedFd::from_raw_fd(fd as RawFd) }
        })
    } else {
        // SAFETY: socketcall(2) on multiplexed architecture.
        Errno::result(unsafe { accept4(fd.as_fd().as_raw_fd(), addr, len, flags.bits()) }).map(
            |fd| {
                // SAFETY: accept4(2) returns a valid fd on success.
                unsafe { SafeOwnedFd::from_raw_fd(fd) }
            },
        )
    }
}

/// bind(2) may be multiplexed by socketcall(2).
pub(crate) static SYS_BIND: LazyLock<Option<c_long>> = LazyLock::new(|| resolve_syscall("bind"));

/// Safe bind(2) confined by syscall cookies.
#[inline(always)]
pub fn safe_bind<Fd: AsFd>(fd: Fd, addr: &dyn SockaddrLike) -> Result<(), Errno> {
    if let Some(sys_bind) = *SYS_BIND {
        // SAFETY: `bind(2)`: `fd` is valid via `AsFd`;
        // `addr.as_ptr()`/`addr.len()` from `SockaddrLike`;
        // trailing args are sealed cookies.
        Errno::result(unsafe {
            syscall(
                sys_bind,
                fd.as_fd().as_raw_fd(),
                addr.as_ptr(),
                addr.len(),
                SYSCOOKIE_POOL.get(CookieIdx::BindArg3),
                SYSCOOKIE_POOL.get(CookieIdx::BindArg4),
                SYSCOOKIE_POOL.get(CookieIdx::BindArg5),
            )
        })
        .map(drop)
    } else {
        // socketcall(2) on multiplexed architecture.
        bind(fd.as_fd().as_raw_fd(), addr)
    }
}

/// connect(2) may be multiplexed by socketcall(2).
pub static SYS_CONNECT: LazyLock<Option<c_long>> = LazyLock::new(|| resolve_syscall("connect"));

/// Safe connect(2) confined by syscall cookies.
#[inline(always)]
pub fn safe_connect<Fd: AsFd>(fd: Fd, addr: &dyn SockaddrLike) -> Result<(), Errno> {
    if let Some(sys_connect) = *SYS_CONNECT {
        // SAFETY: `connect(2)`: `fd` is valid via `AsFd`;
        // `addr.as_ptr()`/`addr.len()` from `SockaddrLike`;
        // trailing args are sealed cookies.
        Errno::result(unsafe {
            syscall(
                sys_connect,
                fd.as_fd().as_raw_fd(),
                addr.as_ptr(),
                addr.len(),
                SYSCOOKIE_POOL.get(CookieIdx::ConnectArg3),
                SYSCOOKIE_POOL.get(CookieIdx::ConnectArg4),
                SYSCOOKIE_POOL.get(CookieIdx::ConnectArg5),
            )
        })
        .map(drop)
    } else {
        // socketcall(2) on multiplexed architecture.
        connect(fd.as_fd().as_raw_fd(), addr)
    }
}

/// shutdown(2) may be multiplexed by socketcall(2).
///
/// This is used by syd-tor(1) only, syd(1) does not hook into shutdown(2).
pub static SYS_SHUTDOWN: LazyLock<Option<c_long>> = LazyLock::new(|| resolve_syscall("shutdown"));

/// Safe shutdown(2) confined by syscall cookies.
#[inline(always)]
pub fn safe_shutdown<Fd: AsFd>(fd: Fd, how: Shutdown) -> Result<(), Errno> {
    let how = match how {
        Shutdown::Read => SHUT_RD,
        Shutdown::Write => SHUT_WR,
        Shutdown::Both => SHUT_RDWR,
    };

    if let Some(sys_shutdown) = *SYS_SHUTDOWN {
        // SAFETY: `shutdown(2)`: `fd` is valid via `AsFd`;
        // `how` is derived from the `Shutdown` enum;
        // trailing args are sealed cookies.
        Errno::result(unsafe {
            syscall(
                sys_shutdown,
                fd.as_fd().as_raw_fd(),
                how,
                SYSCOOKIE_POOL.get(CookieIdx::ShutdownArg2),
                SYSCOOKIE_POOL.get(CookieIdx::ShutdownArg3),
                SYSCOOKIE_POOL.get(CookieIdx::ShutdownArg4),
                SYSCOOKIE_POOL.get(CookieIdx::ShutdownArg5),
            )
        })
        .map(drop)
    } else {
        // SAFETY: socketcall(2) on multiplexed architecture.
        Errno::result(unsafe { shutdown(fd.as_fd().as_raw_fd(), how) }).map(drop)
    }
}

/// Safe memfd_create(2) confined by syscall cookies.
#[inline(always)]
pub fn safe_memfd_create<P: NixPath + ?Sized>(
    name: &P,
    flags: MFdFlags,
) -> Result<SafeOwnedFd, Errno> {
    // name limit is 249 bytes, excluding the terminating null byte.
    if name.len() > 249 {
        return Err(Errno::EINVAL);
    }

    let res = name.with_nix_path(|cstr| {
        // SAFETY: `memfd_create(2)`: `cstr` is a NUL-terminated CStr,
        // length validated <= 249 above; `flags` from `MFdFlags`;
        // trailing args are sealed cookies.
        unsafe {
            syscall(
                SYS_memfd_create,
                cstr.as_ptr(),
                flags.bits(),
                SYSCOOKIE_POOL.get(CookieIdx::MemfdCreateArg2),
                SYSCOOKIE_POOL.get(CookieIdx::MemfdCreateArg3),
                SYSCOOKIE_POOL.get(CookieIdx::MemfdCreateArg4),
                SYSCOOKIE_POOL.get(CookieIdx::MemfdCreateArg5),
            )
        }
    })?;

    // SAFETY:
    //
    // `memfd_create(2)` returns a valid fd on success.
    #[expect(clippy::cast_possible_truncation)]
    Errno::result(res).map(|r| unsafe { SafeOwnedFd::from_raw_fd(r as RawFd) })
}

// memfd_secret(2) may not be available (e.g. on loongarch64),
// and libc::SYS_memfd_secret may not be defined.
// Therefore we query the number using libseccomp.
static SYS_MEMFD_SECRET: LazyLock<Option<c_long>> =
    LazyLock::new(|| resolve_syscall("memfd_secret"));

/// Safe memfd_secret(2) confined by syscall cookies.
#[inline(always)]
pub fn safe_memfd_secret(flags: SecretMemFlags) -> Result<SafeOwnedFd, Errno> {
    let sys_memfd_secret = SYS_MEMFD_SECRET.ok_or(Errno::ENOSYS)?;

    // SAFETY: `memfd_secret(2)`: `flags` from `SecretMemFlags`;
    // all remaining args are sealed cookies.
    Errno::result(unsafe {
        syscall(
            sys_memfd_secret,
            flags.bits(),
            SYSCOOKIE_POOL.get(CookieIdx::MemfdSecretArg1),
            SYSCOOKIE_POOL.get(CookieIdx::MemfdSecretArg2),
            SYSCOOKIE_POOL.get(CookieIdx::MemfdSecretArg3),
            SYSCOOKIE_POOL.get(CookieIdx::MemfdSecretArg4),
            SYSCOOKIE_POOL.get(CookieIdx::MemfdSecretArg5),
        )
    })
    .map(|r| {
        // SAFETY: `memfd_create(2)` returns a valid fd on success.
        #[expect(clippy::cast_possible_truncation)]
        unsafe {
            SafeOwnedFd::from_raw_fd(r as RawFd)
        }
    })
}

/// Safe renameat2(2) confined by syscall cookies.
#[inline(always)]
pub(crate) fn safe_renameat2<Fd1: AsFd, Fd2: AsFd, P1: NixPath + ?Sized, P2: NixPath + ?Sized>(
    old_dirfd: Fd1,
    old_path: &P1,
    new_dirfd: Fd2,
    new_path: &P2,
    flags: RenameFlags,
) -> Result<(), Errno> {
    let res = old_path.with_nix_path(|old_cstr| {
        new_path.with_nix_path(|new_cstr| {
            // SAFETY: `renameat2(2)`: both `dirfd`s valid via `AsFd`;
            // both paths are NUL-terminated CStr from NixPath;
            // `flags` from `RenameFlags`; trailing arg is a sealed cookie.
            unsafe {
                syscall(
                    SYS_renameat2,
                    old_dirfd.as_fd().as_raw_fd(),
                    old_cstr.as_ptr(),
                    new_dirfd.as_fd().as_raw_fd(),
                    new_cstr.as_ptr(),
                    flags.bits(),
                    SYSCOOKIE_POOL.get(CookieIdx::Renameat2Arg5),
                )
            }
        })
    })??;
    Errno::result(res).map(drop)
}

// fchmodat2(2) may not be available, and libc::SYS_fchmodat2 may not be defined.
// Therefore we query the number using libseccomp.
static SYS_FCHMODAT2: LazyLock<Option<c_long>> = LazyLock::new(|| resolve_syscall("fchmodat2"));

/// truncate(2) may be aliased to truncate64(2) by libc.
static SYS_TRUNCATE: LazyLock<Option<c_long>> = LazyLock::new(|| resolve_syscall("truncate"));

/// truncate64(2) may not always be available via libc.
// This is not present on some architectures.
#[allow(dead_code)]
static SYS_TRUNCATE64: LazyLock<Option<c_long>> = LazyLock::new(|| resolve_syscall("truncate64"));

/// ftruncate(2) may be aliased to ftruncate64(2) by libc.
static SYS_FTRUNCATE: LazyLock<Option<c_long>> = LazyLock::new(|| resolve_syscall("ftruncate"));

/// ftruncate64(2) may not always be available via libc.
// This is not present on some architectures.
#[allow(dead_code)]
static SYS_FTRUNCATE64: LazyLock<Option<c_long>> = LazyLock::new(|| resolve_syscall("ftruncate64"));

/// sendfile(2) may be aliased to sendfile64(2) by libc.
static SYS_SENDFILE: LazyLock<Option<c_long>> = LazyLock::new(|| resolve_syscall("sendfile"));

/// sendfile64(2) may not always be available (absent on 64-bit native).
pub static SYS_SENDFILE64: LazyLock<Option<c_long>> =
    LazyLock::new(|| resolve_syscall("sendfile64"));

/// Safe truncate(2) confined by syscall cookies.
pub(crate) fn safe_truncate<P: NixPath + ?Sized>(path: &P, len: off_t) -> Result<(), Errno> {
    // On ILP32 where off_t is 64-bit, dispatch to truncate64 ABI.
    if size_of::<off_t>() > size_of::<c_long>() {
        return safe_truncate64(path, off64_t::from(len));
    }

    let sys_truncate = SYS_TRUNCATE.ok_or(Errno::ENOSYS)?;
    let res = path.with_nix_path(|cstr| {
        // SAFETY: `truncate(2)`: `cstr` is a NUL-terminated CStr
        // from NixPath; `len` is a valid `off_t`;
        // trailing args are sealed cookies.
        unsafe {
            syscall(
                sys_truncate,
                cstr.as_ptr(),
                len,
                SYSCOOKIE_POOL.get(CookieIdx::TruncateArg2),
                SYSCOOKIE_POOL.get(CookieIdx::TruncateArg3),
                SYSCOOKIE_POOL.get(CookieIdx::TruncateArg4),
                SYSCOOKIE_POOL.get(CookieIdx::TruncateArg5),
            )
        }
    })?;
    Errno::result(res).map(drop)
}

/// Safe truncate64(2) confined by syscall cookies.
pub(crate) fn safe_truncate64<P: NixPath + ?Sized>(path: &P, len: off64_t) -> Result<(), Errno> {
    #[cfg(not(any(
        target_pointer_width = "64",
        all(target_arch = "x86_64", target_pointer_width = "32"),
        target_arch = "x86",
        target_arch = "arm",
        target_arch = "powerpc",
        target_arch = "m68k",
        target_arch = "mips",
        target_arch = "mips32r6",
    )))]
    {
        compile_error!("BUG: safe_truncate64 is not implemented for this architecture!");
    }

    #[cfg(any(
        target_pointer_width = "64",
        all(target_arch = "x86_64", target_pointer_width = "32"),
    ))]
    {
        safe_truncate(path, len)
    }

    #[cfg(any(target_arch = "m68k", target_arch = "x86",))]
    {
        let sys_truncate64 = SYS_TRUNCATE64.ok_or(Errno::ENOSYS)?;

        let val = len as u64;
        let low = (val & 0xFFFF_FFFF) as c_long;
        let high = (val >> 32) as c_long;
        let (a, b) = if cfg!(target_endian = "little") {
            (low, high)
        } else {
            (high, low)
        };

        let res = path.with_nix_path(|cstr| {
            // SAFETY: `truncate64(2)` (x86/m68k): `cstr` is a
            // NUL-terminated CStr; `a`/`b` are the split high/low
            // halves of the 64-bit offset; trailing args are sealed cookies.
            unsafe {
                syscall(
                    sys_truncate64,
                    cstr.as_ptr(),
                    a,
                    b,
                    SYSCOOKIE_POOL.get(CookieIdx::Truncate64Arg3),
                    SYSCOOKIE_POOL.get(CookieIdx::Truncate64Arg4),
                    SYSCOOKIE_POOL.get(CookieIdx::Truncate64Arg5),
                )
            }
        })?;
        Errno::result(res).map(drop)
    }

    #[cfg(any(
        target_arch = "arm",
        target_arch = "powerpc",
        target_arch = "mips",
        target_arch = "mips32r6"
    ))]
    {
        let sys_truncate64 = SYS_TRUNCATE64.ok_or(Errno::ENOSYS)?;

        let val = len as u64;
        let low = (val & 0xFFFF_FFFF) as c_long;
        let high = (val >> 32) as c_long;
        let (a, b) = if cfg!(target_endian = "little") {
            (low, high)
        } else {
            (high, low)
        };

        let res = path.with_nix_path(|cstr| {
            // SAFETY: `truncate64(2)` (arm/ppc/mips): `cstr` is a
            // NUL-terminated CStr; pad arg is 0; `a`/`b` are the
            // split 64-bit offset; trailing args are sealed cookies.
            unsafe {
                syscall(
                    sys_truncate64,
                    cstr.as_ptr(),
                    0 as c_long,
                    a,
                    b,
                    SYSCOOKIE_POOL.get(CookieIdx::Truncate64Arg4),
                    SYSCOOKIE_POOL.get(CookieIdx::Truncate64Arg5),
                )
            }
        })?;
        Errno::result(res).map(drop)
    }
}

/// Safe ftruncate(2) confined by syscall cookies.
pub(crate) fn safe_ftruncate<Fd: AsFd>(fd: Fd, len: off_t) -> Result<(), Errno> {
    // On ILP32 where off_t is 64-bit, dispatch to ftruncate64 ABI.
    if size_of::<off_t>() > size_of::<c_long>() {
        return safe_ftruncate64(fd, off64_t::from(len));
    }

    let sys_ftruncate = SYS_FTRUNCATE.ok_or(Errno::ENOSYS)?;

    // SAFETY: `ftruncate(2)`: `fd` is valid via `AsFd`;
    // `len` is a valid `off_t`; trailing args are sealed cookies.
    Errno::result(unsafe {
        syscall(
            sys_ftruncate,
            fd.as_fd().as_raw_fd(),
            len,
            SYSCOOKIE_POOL.get(CookieIdx::FtruncateArg2),
            SYSCOOKIE_POOL.get(CookieIdx::FtruncateArg3),
            SYSCOOKIE_POOL.get(CookieIdx::FtruncateArg4),
            SYSCOOKIE_POOL.get(CookieIdx::FtruncateArg5),
        )
    })
    .map(drop)
}

/// Safe ftruncate64(2) confined by syscall cookies.
pub(crate) fn safe_ftruncate64<Fd: AsFd>(fd: Fd, len: off64_t) -> Result<(), Errno> {
    #[cfg(not(any(
        target_pointer_width = "64",
        all(target_arch = "x86_64", target_pointer_width = "32"),
        target_arch = "x86",
        target_arch = "arm",
        target_arch = "powerpc",
        target_arch = "m68k",
        target_arch = "mips",
        target_arch = "mips32r6",
    )))]
    {
        compile_error!("BUG: safe_ftruncate64 is not implemented for this architecture!");
    }

    #[cfg(any(
        target_pointer_width = "64",
        all(target_arch = "x86_64", target_pointer_width = "32"),
    ))]
    {
        safe_ftruncate(fd, len)
    }

    #[cfg(any(target_arch = "m68k", target_arch = "x86",))]
    {
        let sys_ftruncate64 = SYS_FTRUNCATE64.ok_or(Errno::ENOSYS)?;

        let val = len as u64;
        let low = (val & 0xFFFF_FFFF) as c_long;
        let high = (val >> 32) as c_long;
        let (a, b) = if cfg!(target_endian = "little") {
            (low, high)
        } else {
            (high, low)
        };

        // SAFETY: `ftruncate64(2)` (x86/m68k): `fd` valid via
        // `AsFd`; `a`/`b` are the split 64-bit offset;
        // trailing args are sealed cookies.
        Errno::result(unsafe {
            syscall(
                sys_ftruncate64,
                fd.as_fd().as_raw_fd(),
                a,
                b,
                SYSCOOKIE_POOL.get(CookieIdx::Ftruncate64Arg3),
                SYSCOOKIE_POOL.get(CookieIdx::Ftruncate64Arg4),
                SYSCOOKIE_POOL.get(CookieIdx::Ftruncate64Arg5),
            )
        })
        .map(drop)
    }

    #[cfg(any(
        target_arch = "arm",
        target_arch = "powerpc",
        target_arch = "mips",
        target_arch = "mips32r6"
    ))]
    {
        let sys_ftruncate64 = SYS_FTRUNCATE64.ok_or(Errno::ENOSYS)?;

        let val = len as u64;
        let low = (val & 0xFFFF_FFFF) as c_long;
        let high = (val >> 32) as c_long;
        let (a, b) = if cfg!(target_endian = "little") {
            (low, high)
        } else {
            (high, low)
        };

        // SAFETY: `ftruncate64(2)` (arm/ppc/mips): `fd` valid via
        // `AsFd`; pad arg is 0; `a`/`b` are the split 64-bit offset;
        // trailing args are sealed cookies.
        Errno::result(unsafe {
            syscall(
                sys_ftruncate64,
                fd.as_fd().as_raw_fd(),
                0 as c_long,
                a,
                b,
                SYSCOOKIE_POOL.get(CookieIdx::Ftruncate64Arg4),
                SYSCOOKIE_POOL.get(CookieIdx::Ftruncate64Arg5),
            )
        })
        .map(drop)
    }
}

/// Safe fallocate(2) confined by syscall cookies.
pub(crate) fn safe_fallocate<Fd: AsFd>(
    fd: Fd,
    mode: FallocateFlags,
    offset: off64_t,
    len: off64_t,
) -> Result<(), Errno> {
    // On 64-bit architectures, fallocate(2) uses 4 register slots:
    //   (fd, mode, offset, len)
    // leaving arg4 and arg5 available for cookies.
    //
    // On 32-bit architectures, fallocate(2) uses all 6 slots:
    //   (fd, mode, off_hi, off_lo, len_hi, len_lo)
    // so we fall back to libc::fallocate64 without cookies.
    #[cfg(target_pointer_width = "64")]
    {
        // SAFETY: `fallocate(2)` (64-bit): `fd` valid via `AsFd`;
        // `mode` from `FallocateFlags`; `offset`/`len` are `off64_t`;
        // trailing args are sealed cookies.
        Errno::result(unsafe {
            syscall(
                libc::SYS_fallocate,
                fd.as_fd().as_raw_fd(),
                mode.bits(),
                offset,
                len,
                SYSCOOKIE_POOL.get(CookieIdx::FallocateArg4),
                SYSCOOKIE_POOL.get(CookieIdx::FallocateArg5),
            )
        })
        .map(drop)
    }

    #[cfg(target_pointer_width = "32")]
    {
        crate::fs::fallocate64(fd, mode, offset, len)
    }
}

/// Safe unlinkat(2) confined by syscall cookies.
#[inline(always)]
pub(crate) fn safe_unlinkat<Fd: AsFd, P: NixPath + ?Sized>(
    dirfd: Fd,
    path: &P,
    flag: UnlinkatFlags,
) -> Result<(), Errno> {
    let atflag = match flag {
        UnlinkatFlags::RemoveDir => AtFlags::AT_REMOVEDIR,
        UnlinkatFlags::NoRemoveDir => AtFlags::empty(),
    };

    let res = path.with_nix_path(|cstr| {
        // SAFETY: `unlinkat(2)`: `dirfd` valid via `AsFd`;
        // `cstr` is a NUL-terminated CStr; `atflag` from `AtFlags`;
        // trailing args are sealed cookies.
        unsafe {
            syscall(
                SYS_unlinkat,
                dirfd.as_fd().as_raw_fd(),
                cstr.as_ptr(),
                atflag.bits(),
                SYSCOOKIE_POOL.get(CookieIdx::UnlinkatArg3),
                SYSCOOKIE_POOL.get(CookieIdx::UnlinkatArg4),
                SYSCOOKIE_POOL.get(CookieIdx::UnlinkatArg5),
            )
        }
    })?;
    Errno::result(res).map(drop)
}

/// Safe linkat(2) confined by syscall cookies.
#[inline(always)]
pub(crate) fn safe_linkat<Fd1: AsFd, Fd2: AsFd, P1: NixPath + ?Sized, P2: NixPath + ?Sized>(
    olddirfd: Fd1,
    oldpath: &P1,
    newdirfd: Fd2,
    newpath: &P2,
    flag: AtFlags,
) -> Result<(), Errno> {
    let res = oldpath.with_nix_path(|oldcstr| {
        newpath.with_nix_path(|newcstr| {
            // SAFETY: `linkat(2)`: both `dirfd`s valid via `AsFd`;
            // both paths are NUL-terminated CStr; `flag` from `AtFlags`;
            // trailing arg is a sealed cookie.
            unsafe {
                syscall(
                    SYS_linkat,
                    olddirfd.as_fd().as_raw_fd(),
                    oldcstr.as_ptr(),
                    newdirfd.as_fd().as_raw_fd(),
                    newcstr.as_ptr(),
                    flag.bits(),
                    SYSCOOKIE_POOL.get(CookieIdx::LinkatArg5),
                )
            }
        })
    })??;
    Errno::result(res).map(drop)
}

/// Safe symlinkat(2) confined by syscall cookies.
#[inline(always)]
pub(crate) fn safe_symlinkat<Fd: AsFd, P1: NixPath + ?Sized, P2: NixPath + ?Sized>(
    path1: &P1,
    dirfd: Fd,
    path2: &P2,
) -> Result<(), Errno> {
    let res = path1.with_nix_path(|path1| {
        path2.with_nix_path(|path2| {
            // SAFETY: `symlinkat(2)`: `path1`/`path2` are NUL-terminated
            // CStr; `dirfd` valid via `AsFd`; trailing args are sealed cookies.
            unsafe {
                syscall(
                    SYS_symlinkat,
                    path1.as_ptr(),
                    dirfd.as_fd().as_raw_fd(),
                    path2.as_ptr(),
                    SYSCOOKIE_POOL.get(CookieIdx::SymlinkatArg3),
                    SYSCOOKIE_POOL.get(CookieIdx::SymlinkatArg4),
                    SYSCOOKIE_POOL.get(CookieIdx::SymlinkatArg5),
                )
            }
        })
    })??;
    Errno::result(res).map(drop)
}

/// Safe mkdirat(2) confined by syscall cookies.
#[inline(always)]
pub(crate) fn safe_mkdirat<Fd: AsFd, P: NixPath + ?Sized>(
    dirfd: Fd,
    path: &P,
    mode: Mode,
) -> Result<(), Errno> {
    let res = path.with_nix_path(|cstr| {
        // SAFETY: `mkdirat(2)`: `dirfd` valid via `AsFd`;
        // `cstr` is a NUL-terminated CStr; `mode` from `Mode`;
        // trailing args are sealed cookies.
        unsafe {
            syscall(
                SYS_mkdirat,
                dirfd.as_fd().as_raw_fd(),
                cstr.as_ptr(),
                mode.bits(),
                SYSCOOKIE_POOL.get(CookieIdx::MkdiratArg3),
                SYSCOOKIE_POOL.get(CookieIdx::MkdiratArg4),
                SYSCOOKIE_POOL.get(CookieIdx::MkdiratArg5),
            )
        }
    })?;
    Errno::result(res).map(drop)
}

/// Safe mknodat(2) confined by syscall cookies.
#[inline(always)]
pub(crate) fn safe_mknodat<Fd: AsFd, P: NixPath + ?Sized>(
    dirfd: Fd,
    path: &P,
    kind: SFlag,
    perm: Mode,
    dev: dev_t,
) -> Result<(), Errno> {
    let mode = kind.bits() | perm.bits();

    let res = path.with_nix_path(|cstr| {
        // SAFETY: `mknodat(2)`:
        // `dirfd` valid via `AsFd`; `cstr` is a NUL-terminated CStr;
        // `mode` from `SFlag|Mode`; `dev` is explicitly truncated to
        // unsigned 32-bit; trailing args are sealed cookies.
        unsafe {
            syscall(
                SYS_mknodat,
                dirfd.as_fd().as_raw_fd(),
                cstr.as_ptr(),
                mode,
                (dev & 0xFFFF_FFFF) as libc::c_ulong,
                SYSCOOKIE_POOL.get(CookieIdx::MknodatArg4),
                SYSCOOKIE_POOL.get(CookieIdx::MknodatArg5),
            )
        }
    })?;
    Errno::result(res).map(drop)
}

/// Safe getdents64(2) confined by syscall cookies.
#[expect(clippy::cast_possible_truncation)]
#[expect(clippy::cast_sign_loss)]
pub fn safe_getdents64<Fd: AsFd>(fd: Fd, buf: &mut [u8]) -> Result<usize, Errno> {
    // SAFETY: `getdents64(2)`: `fd` valid via `AsFd`;
    // `buf` is a valid mutable slice; trailing args are sealed cookies.
    Errno::result(unsafe {
        syscall(
            SYS_getdents64,
            fd.as_fd().as_raw_fd(),
            buf.as_mut_ptr().cast::<c_void>(),
            buf.len(),
            SYSCOOKIE_POOL.get(CookieIdx::Getdents64Arg3),
            SYSCOOKIE_POOL.get(CookieIdx::Getdents64Arg4),
            SYSCOOKIE_POOL.get(CookieIdx::Getdents64Arg5),
        )
    })
    .map(|size| size as usize)
}

/// Safe fchdir(2) confined by syscall cookies.
#[inline(always)]
pub fn safe_fchdir<Fd: AsFd>(dirfd: Fd) -> Result<(), Errno> {
    // SAFETY: `fchdir(2)`: `dirfd` valid via `AsFd`;
    // all remaining args are sealed cookies.
    Errno::result(unsafe {
        syscall(
            SYS_fchdir,
            dirfd.as_fd().as_raw_fd(),
            SYSCOOKIE_POOL.get(CookieIdx::FchdirArg1),
            SYSCOOKIE_POOL.get(CookieIdx::FchdirArg2),
            SYSCOOKIE_POOL.get(CookieIdx::FchdirArg3),
            SYSCOOKIE_POOL.get(CookieIdx::FchdirArg4),
            SYSCOOKIE_POOL.get(CookieIdx::FchdirArg5),
        )
    })
    .map(drop)
}

/// Safe faccessat2(2) confined by syscall cookies.
///
/// This calls faccessat2(2) with AT_EMPTY_PATH under the hood.
pub fn safe_faccess<Fd: AsFd>(fd: Fd, mode: AccessFlags, mut flags: AtFlags) -> Result<(), Errno> {
    // Remove AT_SYMLINK_NOFOLLOW and add AT_EMPTY_PATH to flags.
    flags.remove(AtFlags::AT_SYMLINK_NOFOLLOW);
    flags.insert(AtFlags::AT_EMPTY_PATH);

    // SAFETY: No libc wrapper for faccessat2 yet.
    Errno::result(unsafe {
        syscall(
            SYS_faccessat2,
            fd.as_fd().as_raw_fd(),
            empty_path() as *const c_char,
            mode.bits(),
            flags.bits(),
            SYSCOOKIE_POOL.get(CookieIdx::Faccessat2Arg4),
            SYSCOOKIE_POOL.get(CookieIdx::Faccessat2Arg5),
        )
    })
    .map(drop)
}

/// Safe execveat(2) for executability check confined by syscall cookie.
///
/// This uses AT_EXECVE_CHECK | AT_EMPTY_PATH to check if the file
/// referenced by fd is executable, without actually executing it.
/// Requires Linux >= 6.14.
// No HAVE_AT_EXECVE_CHECK check happens here, see syd::fd::check_executable.
#[inline(always)]
pub fn safe_execve_check<Fd: AsFd>(fd: Fd) -> Result<(), Errno> {
    let flags = (AT_EXECVE_CHECK | AtFlags::AT_EMPTY_PATH).bits();

    // SAFETY: `execveat(2)` with `AT_EXECVE_CHECK|AT_EMPTY_PATH`:
    // `fd` valid via `AsFd`; path/argv/envp are static empty
    // sentinels; trailing arg is a sealed cookie.
    Errno::result(unsafe {
        syscall(
            SYS_execveat,
            fd.as_fd().as_raw_fd(),
            empty_path() as *const c_char,
            empty_argv() as *const *const c_char,
            empty_envp() as *const *const c_char,
            flags,
            SYSCOOKIE_POOL.get(CookieIdx::ExecveatArg5),
        )
    })
    .map(drop)
}

/// Safe fchmodat(2) confined by syscall cookies.
///
/// Note: fchmodat(2) does not have a flags argument and always follows symlinks.
#[inline(always)]
pub(crate) fn safe_fchmodat<Fd: AsFd, P: NixPath + ?Sized>(
    dirfd: Fd,
    path: &P,
    mode: Mode,
) -> Result<(), Errno> {
    let res = path.with_nix_path(|cstr| {
        // SAFETY: `fchmodat(2)`: `dirfd` valid via `AsFd`;
        // `cstr` is a NUL-terminated CStr; `mode` from `Mode`;
        // trailing args are sealed cookies.
        unsafe {
            syscall(
                SYS_fchmodat,
                dirfd.as_fd().as_raw_fd(),
                cstr.as_ptr(),
                mode.bits(),
                SYSCOOKIE_POOL.get(CookieIdx::FchmodatArg3),
                SYSCOOKIE_POOL.get(CookieIdx::FchmodatArg4),
                SYSCOOKIE_POOL.get(CookieIdx::FchmodatArg5),
            )
        }
    })?;
    Errno::result(res).map(drop)
}

/// Safe fchmod(2) confined by syscall cookies.
#[inline(always)]
pub(crate) fn safe_fchmod<Fd: AsFd>(fd: Fd, mode: Mode) -> Result<(), Errno> {
    // SAFETY: `fchmod(2)`: `fd` valid via `AsFd`;
    // `mode` from `Mode`; trailing args are sealed cookies.
    Errno::result(unsafe {
        syscall(
            SYS_fchmod,
            fd.as_fd().as_raw_fd(),
            mode.bits(),
            SYSCOOKIE_POOL.get(CookieIdx::FchmodArg2),
            SYSCOOKIE_POOL.get(CookieIdx::FchmodArg3),
            SYSCOOKIE_POOL.get(CookieIdx::FchmodArg4),
            SYSCOOKIE_POOL.get(CookieIdx::FchmodArg5),
        )
    })
    .map(drop)
}

/// Safe fchmodat2(2) confined by syscall cookies.
///
/// This variant uses AT_EMPTY_PATH with fchmodat2(2) for fd-based chmod.
#[inline(always)]
pub(crate) fn safe_fchmodat2<Fd: AsFd>(dirfd: Fd, mode: Mode) -> Result<(), Errno> {
    let sys_fchmodat2 = SYS_FCHMODAT2.ok_or(Errno::ENOSYS)?;

    // SAFETY: `fchmodat2(2)` with `AT_EMPTY_PATH`: `dirfd` valid
    // via `AsFd`; path is the static empty sentinel; `mode` from
    // `Mode`; trailing args are sealed cookies.
    Errno::result(unsafe {
        syscall(
            sys_fchmodat2,
            dirfd.as_fd().as_raw_fd(),
            empty_path() as *const c_char,
            mode.bits(),
            AtFlags::AT_EMPTY_PATH.bits(),
            SYSCOOKIE_POOL.get(CookieIdx::Fchmodat2Arg4),
            SYSCOOKIE_POOL.get(CookieIdx::Fchmodat2Arg5),
        )
    })
    .map(drop)
}

/// Safe fchown(2) confined by syscall cookies.
#[inline(always)]
pub(crate) fn safe_fchown<Fd: AsFd>(
    fd: Fd,
    owner: Option<Uid>,
    group: Option<Gid>,
) -> Result<(), Errno> {
    // SAFETY: `fchown(2)`: `fd` valid via `AsFd`;
    // `uid`/`gid` from `chown_raw_ids` (Option<Uid/Gid> -> raw);
    // trailing args are sealed cookies.
    Errno::result(unsafe {
        let (uid, gid) = chown_raw_ids(owner, group);
        syscall(
            SYS_fchown,
            fd.as_fd().as_raw_fd(),
            uid,
            gid,
            SYSCOOKIE_POOL.get(CookieIdx::FchownArg3),
            SYSCOOKIE_POOL.get(CookieIdx::FchownArg4),
            SYSCOOKIE_POOL.get(CookieIdx::FchownArg5),
        )
    })
    .map(drop)
}

/// Safe fchownat(2) confined by syscall cookies.
///
/// This calls fchownat(2) with AT_EMPTY_PATH under the hood.
#[inline(always)]
pub(crate) fn safe_fchownat<Fd: AsFd>(
    dirfd: Fd,
    owner: Option<Uid>,
    group: Option<Gid>,
) -> Result<(), Errno> {
    // SAFETY: `fchownat(2)` with `AT_EMPTY_PATH`: `dirfd` valid
    // via `AsFd`; path is the static empty sentinel; `uid`/`gid`
    // from `chown_raw_ids`; trailing arg is a sealed cookie.
    Errno::result(unsafe {
        let (uid, gid) = chown_raw_ids(owner, group);
        syscall(
            SYS_fchownat,
            dirfd.as_fd().as_raw_fd(),
            empty_path() as *const c_char,
            uid,
            gid,
            AtFlags::AT_EMPTY_PATH.bits(),
            SYSCOOKIE_POOL.get(CookieIdx::FchownatArg5),
        )
    })
    .map(drop)
}

/// Computes the raw UID and GID values to pass to a `*chown` call.
// The cast is not unnecessary on all platforms.
// Borrowed from nix-0.31.0.
#[allow(clippy::unnecessary_cast)]
fn chown_raw_ids(owner: Option<Uid>, group: Option<Gid>) -> (uid_t, gid_t) {
    // According to the POSIX specification, -1 is used to indicate that owner and group
    // are not to be changed.  Since uid_t and gid_t are unsigned types, we have to wrap
    // around to get -1.
    let uid = owner
        .map(Into::into)
        .unwrap_or_else(|| (0 as uid_t).wrapping_sub(1));

    let gid = group
        .map(Into::into)
        .unwrap_or_else(|| (0 as gid_t).wrapping_sub(1));
    (uid, gid)
}

/// Safe uname(2) confined by syscall cookies.
#[inline(always)]
pub fn safe_uname() -> Result<UtsName, Errno> {
    let mut name = UtsName::default();

    // SAFETY: `uname(2)`: `name` is a valid, writable stack-allocated
    // `UtsName`; all remaining args are sealed cookies.
    Errno::result(unsafe {
        syscall(
            SYS_uname,
            &raw mut name,
            SYSCOOKIE_POOL.get(CookieIdx::UnameArg1),
            SYSCOOKIE_POOL.get(CookieIdx::UnameArg2),
            SYSCOOKIE_POOL.get(CookieIdx::UnameArg3),
            SYSCOOKIE_POOL.get(CookieIdx::UnameArg4),
            SYSCOOKIE_POOL.get(CookieIdx::UnameArg5),
        )
    })?;

    Ok(name)
}

/// Safe (2) confined by syscall cookies.
#[inline(always)]
pub fn safe_umask(mode: Mode) -> Mode {
    // SAFETY: `umask(2)`: `mode` from `Mode`; `umask` always succeeds;
    // all remaining args are sealed cookies.
    #[expect(clippy::cast_possible_truncation)]
    #[expect(clippy::cast_sign_loss)]
    let prev = unsafe {
        syscall(
            SYS_umask,
            mode.bits(),
            SYSCOOKIE_POOL.get(CookieIdx::UmaskArg1),
            SYSCOOKIE_POOL.get(CookieIdx::UmaskArg2),
            SYSCOOKIE_POOL.get(CookieIdx::UmaskArg3),
            SYSCOOKIE_POOL.get(CookieIdx::UmaskArg4),
            SYSCOOKIE_POOL.get(CookieIdx::UmaskArg5),
        )
    } as mode_t;
    #[expect(clippy::disallowed_methods)]
    Mode::from_bits(prev).expect("[BUG] umask returned invalid Mode")
}

/// Safe utimensat(2)
///
/// 1. This uses AT_EMPTY_PATH for fd-based timestamp updates.
/// 2. This is NOT confined by syscall cookies because handling
///    utimensat, utimensat_time64 and all that 32/64-bit is
///    terribly error-prone so we call into libc.
#[inline(always)]
pub(crate) fn safe_utimensat<Fd: AsFd>(
    dirfd: Fd,
    times: &[libc::timespec; 2],
) -> Result<(), Errno> {
    // SAFETY: `utimensat(2)` with `AT_EMPTY_PATH`: `dirfd` valid
    // via `AsFd`; path is the static empty sentinel; `times` is a
    // valid 2-element timespec array. No cookies -- delegated to libc.
    Errno::result(unsafe {
        utimensat(
            dirfd.as_fd().as_raw_fd(),
            empty_path() as *const c_char,
            &raw const times[0],
            AtFlags::AT_EMPTY_PATH.bits() as c_int,
        )
    })
    .map(drop)
}

/// Safe fremovexattr(2) confined by syscall cookies.
#[inline(always)]
pub fn safe_fremovexattr<Fd: AsFd>(fd: Fd, name: &CStr) -> Result<(), Errno> {
    // SAFETY: `fremovexattr(2)`: `fd` valid via `AsFd`;
    // `name` is a NUL-terminated CStr; trailing args are sealed cookies.
    Errno::result(unsafe {
        syscall(
            SYS_fremovexattr,
            fd.as_fd().as_raw_fd(),
            name.as_ptr(),
            SYSCOOKIE_POOL.get(CookieIdx::FremovexattrArg2),
            SYSCOOKIE_POOL.get(CookieIdx::FremovexattrArg3),
            SYSCOOKIE_POOL.get(CookieIdx::FremovexattrArg4),
            SYSCOOKIE_POOL.get(CookieIdx::FremovexattrArg5),
        )
    })
    .map(drop)
}

/// Safe lremovexattr(2) confined by syscall cookies.
#[inline(always)]
pub fn safe_lremovexattr<P: NixPath + ?Sized>(path: &P, name: &CStr) -> Result<(), Errno> {
    let res = path.with_nix_path(|cstr| {
        // SAFETY: `lremovexattr(2)`: `cstr` is a NUL-terminated CStr
        // from NixPath; `name` is a NUL-terminated CStr;
        // trailing args are sealed cookies.
        unsafe {
            syscall(
                SYS_lremovexattr,
                cstr.as_ptr(),
                name.as_ptr(),
                SYSCOOKIE_POOL.get(CookieIdx::LremovexattrArg2),
                SYSCOOKIE_POOL.get(CookieIdx::LremovexattrArg3),
                SYSCOOKIE_POOL.get(CookieIdx::LremovexattrArg4),
                SYSCOOKIE_POOL.get(CookieIdx::LremovexattrArg5),
            )
        }
    })?;
    Errno::result(res).map(drop)
}

/// Safe removexattrat(2) confined by syscall cookies.
#[inline(always)]
pub fn safe_removexattrat<Fd: AsFd, P: NixPath + ?Sized>(
    dirfd: Fd,
    path: &P,
    name: &CStr,
    flags: AtFlags,
) -> Result<(), Errno> {
    let sysno = SYS_REMOVEXATTRAT.ok_or(Errno::ENOSYS)?;

    let res = path.with_nix_path(|c_path| {
        // SAFETY: `removexattrat(2)`: `dirfd` valid via `AsFd`;
        // `c_path` is a NUL-terminated CStr; `flags` from `AtFlags`;
        // `name` is a NUL-terminated CStr; trailing args are sealed cookies.
        unsafe {
            libc::syscall(
                sysno,
                dirfd.as_fd().as_raw_fd(),
                c_path.as_ptr(),
                flags.bits(), // flags come before name!
                name.as_ptr(),
                SYSCOOKIE_POOL.get(CookieIdx::RemovexattratArg4),
                SYSCOOKIE_POOL.get(CookieIdx::RemovexattratArg5),
            )
        }
    })?;

    Errno::result(res).map(drop)
}

/// Safe fsetxattr(2) confined by syscall cookies.
#[inline(always)]
pub fn safe_fsetxattr<Fd: AsFd>(
    fd: Fd,
    name: &CStr,
    value: Option<&[u8]>,
    flags: c_int,
) -> Result<(), Errno> {
    let (val, len) = if let Some(value) = value.as_ref() {
        let val = value.as_ptr() as *const c_void;
        let len = value.len();
        (val, len)
    } else {
        (ptr::null(), 0)
    };

    // SAFETY: `fsetxattr(2)`: `fd` valid via `AsFd`;
    // `name` is a NUL-terminated CStr; `val`/`len` from the caller's
    // byte slice (or null/0); `flags` is caller-provided;
    // trailing arg is a sealed cookie.
    Errno::result(unsafe {
        syscall(
            SYS_fsetxattr,
            fd.as_fd().as_raw_fd(),
            name.as_ptr(),
            val,
            len,
            flags,
            SYSCOOKIE_POOL.get(CookieIdx::FsetxattrArg5),
        )
    })
    .map(drop)
}

/// Safe lsetxattr(2) confined by syscall cookies.
#[inline(always)]
pub fn safe_lsetxattr<P: NixPath + ?Sized>(
    path: &P,
    name: &CStr,
    value: Option<&[u8]>,
    flags: c_int,
) -> Result<(), Errno> {
    let (val, len) = if let Some(value) = value.as_ref() {
        let val = value.as_ptr() as *const c_void;
        let len = value.len();
        (val, len)
    } else {
        (ptr::null(), 0)
    };

    let res = path.with_nix_path(|c_path| {
        // SAFETY: `lsetxattr(2)`: `c_path` is a NUL-terminated CStr;
        // `name` is a NUL-terminated CStr; `val`/`len` from the
        // caller's byte slice (or null/0); `flags` from caller;
        // trailing arg is a sealed cookie.
        unsafe {
            syscall(
                SYS_lsetxattr,
                c_path.as_ptr(),
                name.as_ptr(),
                val,
                len,
                flags,
                SYSCOOKIE_POOL.get(CookieIdx::LsetxattrArg5),
            )
        }
    })?;
    Errno::result(res).map(drop)
}

/// Safe pipe2(2) confined by syscall cookies.
#[inline(always)]
pub fn safe_pipe2(flags: OFlag) -> Result<(SafeOwnedFd, SafeOwnedFd), Errno> {
    let mut fds = MaybeUninit::<[SafeOwnedFd; 2]>::uninit();

    // SAFETY: `pipe2(2)`: `fds` is a valid, writable `MaybeUninit`
    // array; `flags` from `OFlag`; trailing args are sealed cookies.
    Errno::result(unsafe {
        syscall(
            SYS_pipe2,
            fds.as_mut_ptr(),
            flags.bits(),
            SYSCOOKIE_POOL.get(CookieIdx::Pipe2Arg2),
            SYSCOOKIE_POOL.get(CookieIdx::Pipe2Arg3),
            SYSCOOKIE_POOL.get(CookieIdx::Pipe2Arg4),
            SYSCOOKIE_POOL.get(CookieIdx::Pipe2Arg5),
        )
    })?;

    // SAFETY: pipe2 returns valid FDs on success.
    let [read, write] = unsafe { fds.assume_init() };
    Ok((read, write))
}

/// Safe sendfile(2)/sendfile64(2) confined by syscall cookies.
///
/// Tries sendfile64(2) first, and falls back to sendfile(2).
#[inline(always)]
pub fn safe_sendfile<Fd1: AsFd, Fd2: AsFd>(
    out_fd: Fd1,
    in_fd: Fd2,
    count: usize,
) -> Result<usize, Errno> {
    if let Some(sys_sendfile64) = *SYS_SENDFILE64 {
        // SAFETY: `sendfile64(2)`: both fds valid via `AsFd`;
        // offset is null (copy from current position); `count` from
        // caller; trailing args are sealed cookies.
        #[expect(clippy::cast_possible_truncation)]
        #[expect(clippy::cast_sign_loss)]
        return Errno::result(unsafe {
            syscall(
                sys_sendfile64,
                out_fd.as_fd().as_raw_fd(),
                in_fd.as_fd().as_raw_fd(),
                ptr::null_mut::<i64>(),
                count,
                SYSCOOKIE_POOL.get(CookieIdx::Sendfile64Arg4),
                SYSCOOKIE_POOL.get(CookieIdx::Sendfile64Arg5),
            )
        })
        .map(|n| n as usize);
    }

    let sys_sendfile = SYS_SENDFILE.ok_or(Errno::ENOSYS)?;
    // SAFETY: `sendfile(2)`: both fds valid via `AsFd`;
    // offset is null (copy from current position); `count` from
    // caller; trailing args are sealed cookies.
    #[expect(clippy::cast_possible_truncation)]
    #[expect(clippy::cast_sign_loss)]
    Errno::result(unsafe {
        syscall(
            sys_sendfile,
            out_fd.as_fd().as_raw_fd(),
            in_fd.as_fd().as_raw_fd(),
            ptr::null_mut::<i64>(),
            count,
            SYSCOOKIE_POOL.get(CookieIdx::SendfileArg4),
            SYSCOOKIE_POOL.get(CookieIdx::SendfileArg5),
        )
    })
    .map(|n| n as usize)
}

/// Safe pidfd_open(2) confined by syscall cookies.
#[inline(always)]
pub fn safe_pidfd_open(pid: Pid, mut flags: u32) -> Result<SafeOwnedFd, Errno> {
    // Use PIDFD_THREAD if available, pass-through PIDFD_NONBLOCK.
    let pid = if *HAVE_PIDFD_THREAD || flags & PIDFD_THREAD == 0 {
        pid
    } else {
        flags &= !PIDFD_THREAD;
        proc_tgid(pid)?
    };

    // SAFETY: `pidfd_open(2)`: `pid` is a valid `Pid`;
    // `flags` validated above (PIDFD_THREAD stripped if unavailable);
    // trailing args are sealed cookies.
    #[expect(clippy::cast_possible_truncation)]
    Errno::result(unsafe {
        syscall(
            SYS_pidfd_open,
            pid.as_raw(),
            flags,
            SYSCOOKIE_POOL.get(CookieIdx::PidfdOpenArg2),
            SYSCOOKIE_POOL.get(CookieIdx::PidfdOpenArg3),
            SYSCOOKIE_POOL.get(CookieIdx::PidfdOpenArg4),
            SYSCOOKIE_POOL.get(CookieIdx::PidfdOpenArg5),
        )
    })
    .map(|fd| {
        // SAFETY: pidfd_open(2) returned success, fd is valid.
        unsafe { SafeOwnedFd::from_raw_fd(fd as RawFd) }
    })
}

/// Safe pidfd_getfd(2) confined by syscall cookies.
#[inline(always)]
pub fn safe_pidfd_getfd<Fd: AsFd>(pid_fd: Fd, remote_fd: RawFd) -> Result<SafeOwnedFd, Errno> {
    // SAFETY: `pidfd_getfd(2)`: `pid_fd` valid via `AsFd`;
    // `remote_fd` is the target fd number; flags arg is 0;
    // trailing args are sealed cookies.
    #[expect(clippy::cast_possible_truncation)]
    Errno::result(unsafe {
        syscall(
            SYS_pidfd_getfd,
            pid_fd.as_fd().as_raw_fd(),
            remote_fd,
            0,
            SYSCOOKIE_POOL.get(CookieIdx::PidfdGetfdArg3),
            SYSCOOKIE_POOL.get(CookieIdx::PidfdGetfdArg4),
            SYSCOOKIE_POOL.get(CookieIdx::PidfdGetfdArg5),
        )
    })
    .map(|fd| {
        // SAFETY: pidfd_getfd(2) returned success, fd is valid.
        unsafe { SafeOwnedFd::from_raw_fd(fd as RawFd) }
    })
}

/// Safe pidfd_send_signal(2) confined by syscall cookies.
#[inline(always)]
pub fn safe_pidfd_send_signal<Fd: AsFd>(pid_fd: Fd, sig: i32) -> Result<(), Errno> {
    // SAFETY: `pidfd_send_signal(2)`: `pid_fd` valid via `AsFd`;
    // `sig` is a valid signal number (or 0 for liveness check);
    // siginfo is null; flags is 0; trailing args are sealed cookies.
    Errno::result(unsafe {
        syscall(
            SYS_pidfd_send_signal,
            pid_fd.as_fd().as_raw_fd(),
            sig,
            0,
            0,
            SYSCOOKIE_POOL.get(CookieIdx::PidfdSendSignalArg4),
            SYSCOOKIE_POOL.get(CookieIdx::PidfdSendSignalArg5),
        )
    })
    .map(drop)
}

/// Safe wrapper for pidfd_send_signal(2) with signal 0 confined by syscall cookies.
#[inline(always)]
pub fn safe_pidfd_is_alive<Fd: AsFd>(pid_fd: Fd) -> Result<(), Errno> {
    safe_pidfd_send_signal(pid_fd, 0)
}

/// Safe ptrace(2) confined by syscall cookies.
///
/// # Safety
///
/// Same safety requirements as libc::ptrace.
/// The `addr` and `data` pointers must be valid for the given `request`.
#[inline(always)]
pub unsafe fn safe_ptrace(
    request: PtraceRequest,
    pid: c_int,
    addr: *mut c_void,
    data: *mut c_void,
) -> c_long {
    // SAFETY: `ptrace(2)`: `request` is a valid `PtraceRequest`;
    // `pid` is a valid tracee pid; `addr`/`data` validity is the
    // caller's responsibility (fn is `unsafe`); trailing args are
    // sealed cookies.
    unsafe {
        syscall(
            SYS_ptrace,
            request,
            pid,
            addr,
            data,
            SYSCOOKIE_POOL.get(CookieIdx::PtraceArg4),
            SYSCOOKIE_POOL.get(CookieIdx::PtraceArg5),
        )
    }
}
