//
// 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

// We allow this to easily write portable code.
// FIXME: Do not be lazy.
#![allow(dead_code)]

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

use libc::{c_uint, c_void};
use libseccomp::ScmpSyscall;
use nix::{
    errno::Errno,
    fcntl::{AtFlags, OFlag},
    sys::{
        mman::{mmap_anonymous, MapFlags, ProtFlags},
        socket::{bind, connect, AddressFamily, Shutdown, SockFlag, SockType, SockaddrLike},
        stat::Mode,
    },
    unistd::UnlinkatFlags,
    NixPath,
};

use crate::{
    compat::{set_vma_anon_name, MFdFlags, OpenHow, RenameFlags},
    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 {
    CloseRangeArg3 = 0,
    CloseRangeArg4,
    CloseRangeArg5,
    Openat2Arg4,
    Openat2Arg5,
    FchdirArg1,
    FchdirArg2,
    FchdirArg3,
    FchdirArg4,
    FchdirArg5,
    SocketArg3,
    SocketArg4,
    SocketArg5,
    SocketpairArg4,
    SocketpairArg5,
    Accept4Arg4,
    Accept4Arg5,
    BindArg3,
    BindArg4,
    BindArg5,
    ConnectArg3,
    ConnectArg4,
    ConnectArg5,
    ShutdownArg2,
    ShutdownArg3,
    ShutdownArg4,
    ShutdownArg5,
    MemfdCreateArg2,
    MemfdCreateArg3,
    MemfdCreateArg4,
    MemfdCreateArg5,
    Renameat2Arg5,
    TruncateArg2,
    TruncateArg3,
    TruncateArg4,
    TruncateArg5,
    Truncate64Arg3,
    Truncate64Arg4,
    Truncate64Arg5,
    FtruncateArg2,
    FtruncateArg3,
    FtruncateArg4,
    FtruncateArg5,
    Ftruncate64Arg3,
    Ftruncate64Arg4,
    Ftruncate64Arg5,
    UmaskArg1,
    UmaskArg2,
    UmaskArg3,
    UmaskArg4,
    UmaskArg5,
    UnameArg1,
    UnameArg2,
    UnameArg3,
    UnameArg4,
    UnameArg5,
    UnlinkatArg3,
    UnlinkatArg4,
    UnlinkatArg5,
    LinkatArg5,
    Pipe2Arg2,
    Pipe2Arg3,
    Pipe2Arg4,
    Pipe2Arg5,
    SeccompIoctlNotifAddfdArg3,
    SeccompIoctlNotifAddfdArg4,
    SeccompIoctlNotifAddfdArg5,
    SeccompIoctlNotifSendArg3,
    SeccompIoctlNotifSendArg4,
    SeccompIoctlNotifSendArg5,
    ProcmapQueryArg3,
    ProcmapQueryArg4,
    ProcmapQueryArg5,
}

impl CookieIdx {
    /// Number of cookie indices.
    pub const COUNT: usize = Self::ProcmapQueryArg5 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
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.
unsafe impl Sync for SyscookiePool {}
// SAFETY: ditto.
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: Valid pointer arithmetic.
        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: Valid pointer.
        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),
        }

        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_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: In libc we trust.
    Errno::result(unsafe {
        libc::syscall(
            libc::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<OwnedFd, Errno> {
    // SAFETY: In libc we trust.
    #[expect(clippy::cast_possible_truncation)]
    let fd = path.with_nix_path(|cstr| unsafe {
        libc::syscall(
            libc::SYS_openat2,
            dirfd.as_fd().as_raw_fd(),
            cstr.as_ptr(),
            std::ptr::addr_of_mut!(how),
            size_of::<OpenHow>(),
            SYSCOOKIE_POOL.get(CookieIdx::Openat2Arg4),
            SYSCOOKIE_POOL.get(CookieIdx::Openat2Arg5),
        )
    })? as RawFd;

    Errno::result(fd)?;

    // SAFETY:
    //
    // `openat2(2)` should return a valid owned fd on success
    Ok(unsafe { OwnedFd::from_raw_fd(fd) })
}

/// socket(2) may be multiplexed by socketcall(2).
pub static SYS_SOCKET: LazyLock<Option<libc::c_long>> = LazyLock::new(|| {
    match ScmpSyscall::from_name("socket")
        .map(i32::from)
        .map(libc::c_long::from)
        .ok()
    {
        Some(n) if n < 0 => None,
        Some(n) => Some(n),
        None => None,
    }
});

/// Safe socket(2) confined by syscall cookies.
#[inline(always)]
pub fn safe_socket(
    domain: libc::c_int,
    stype: libc::c_int,
    proto: libc::c_int,
) -> Result<OwnedFd, Errno> {
    if let Some(sys_socket) = *SYS_SOCKET {
        // SAFETY: In libc we trust.
        #[expect(clippy::cast_possible_truncation)]
        Errno::result(unsafe {
            libc::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 { libc::socket(domain, stype, proto) })
    }
    .map(|fd| {
        // SAFETY: socket returns a valid FD on success.
        unsafe { OwnedFd::from_raw_fd(fd) }
    })
}

/// socketpair(2) may be multiplexed by socketcall(2).
pub static SYS_SOCKETPAIR: LazyLock<Option<libc::c_long>> = LazyLock::new(|| {
    match ScmpSyscall::from_name("socketpair")
        .map(i32::from)
        .map(libc::c_long::from)
        .ok()
    {
        Some(n) if n < 0 => None,
        Some(n) => Some(n),
        None => None,
    }
});

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

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

    if let Some(sys_socketpair) = *SYS_SOCKETPAIR {
        // SAFETY: In libc we trust.
        Errno::result(unsafe {
            libc::syscall(
                sys_socketpair,
                domain as libc::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 {
            libc::socketpair(domain as libc::c_int, ty, proto, fds.as_mut_ptr())
        })?;
    }

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

/// accept4(2) may be multiplexed by socketcall(2).
pub static SYS_ACCEPT4: LazyLock<Option<libc::c_long>> = LazyLock::new(|| {
    match ScmpSyscall::from_name("accept4")
        .map(i32::from)
        .map(libc::c_long::from)
        .ok()
    {
        Some(n) if n < 0 => None,
        Some(n) => Some(n),
        None => None,
    }
});

/// Safe accept4(2) confined by syscall cookies.
///
/// # Safety
///
/// Dereferences raw pointers `addr` and `len`.
#[inline(always)]
pub unsafe fn safe_accept4<Fd: AsFd>(
    fd: Fd,
    addr: *mut libc::sockaddr,
    len: *mut libc::socklen_t,
    flags: SockFlag,
) -> Result<OwnedFd, Errno> {
    if let Some(sys_accept4) = *SYS_ACCEPT4 {
        // SAFETY: In libc we trust.
        #[expect(clippy::cast_possible_truncation)]
        Errno::result(unsafe {
            libc::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 returns a valid fd on success.
            unsafe { OwnedFd::from_raw_fd(fd as RawFd) }
        })
    } else {
        // SAFETY: socketcall(2) on multiplexed architecture.
        Errno::result(unsafe { libc::accept4(fd.as_fd().as_raw_fd(), addr, len, flags.bits()) })
            .map(|fd| {
                // SAFETY: accept4 returns a valid fd on success.
                unsafe { OwnedFd::from_raw_fd(fd) }
            })
    }
}

/// Safe accept4(2) confined by syscall cookies.
///
/// Unlike `safe_accept4`, this version does not return source address,
/// therefore does not need an unsafe clause to dereference pointers.
#[inline(always)]
pub fn safer_accept4<Fd: AsFd>(fd: Fd, flags: SockFlag) -> Result<OwnedFd, Errno> {
    // SAFETY: accept4(2) does not dereference NULL pointers.
    unsafe { safe_accept4(fd, std::ptr::null_mut(), std::ptr::null_mut(), flags) }
}

/// bind(2) may be multiplexed by socketcall(2).
pub(crate) static SYS_BIND: LazyLock<Option<libc::c_long>> = LazyLock::new(|| {
    match ScmpSyscall::from_name("bind")
        .map(i32::from)
        .map(libc::c_long::from)
        .ok()
    {
        Some(n) if n < 0 => None,
        Some(n) => Some(n),
        None => None,
    }
});

/// 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: In libc we trust.
        Errno::result(unsafe {
            libc::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<libc::c_long>> = LazyLock::new(|| {
    match ScmpSyscall::from_name("connect")
        .map(i32::from)
        .map(libc::c_long::from)
        .ok()
    {
        Some(n) if n < 0 => None,
        Some(n) => Some(n),
        None => None,
    }
});

/// 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: In libc we trust.
        Errno::result(unsafe {
            libc::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<libc::c_long>> = LazyLock::new(|| {
    match ScmpSyscall::from_name("shutdown")
        .map(i32::from)
        .map(libc::c_long::from)
        .ok()
    {
        Some(n) if n < 0 => None,
        Some(n) => Some(n),
        None => None,
    }
});

/// 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 => libc::SHUT_RD,
        Shutdown::Write => libc::SHUT_WR,
        Shutdown::Both => libc::SHUT_RDWR,
    };

    if let Some(sys_shutdown) = *SYS_SHUTDOWN {
        // SAFETY: In libc we trust.
        Errno::result(unsafe {
            libc::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 { libc::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<OwnedFd, 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: In libc we trust.
        unsafe {
            libc::syscall(
                libc::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),
            )
        }
    })?;

    #[expect(clippy::cast_possible_truncation)]
    let fd = Errno::result(res).map(|fd| fd as RawFd)?;

    // SAFETY:
    //
    // `memfd_create(2)` returns a valid owned fd on success.
    Ok(unsafe { OwnedFd::from_raw_fd(fd) })
}

/// 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| {
        // SAFETY: In libc we trust.
        new_path.with_nix_path(|new_cstr| unsafe {
            libc::syscall(
                libc::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)
}

/// truncate(2) may be aliased to truncate64(2) by libc.
static SYS_TRUNCATE: LazyLock<Option<libc::c_long>> =
    LazyLock::new(|| {
        match ScmpSyscall::from_name("truncate")
            .map(i32::from)
            .map(libc::c_long::from)
            .ok()
        {
            Some(n) if n < 0 => None,
            Some(n) => Some(n),
            None => None,
        }
    });

/// truncate64(2) may not always be available via libc.
static SYS_TRUNCATE64: LazyLock<Option<libc::c_long>> = LazyLock::new(|| {
    match ScmpSyscall::from_name("truncate64")
        .map(i32::from)
        .map(libc::c_long::from)
        .ok()
    {
        Some(n) if n < 0 => None,
        Some(n) => Some(n),
        None => None,
    }
});

/// ftruncate(2) may be aliased to ftruncate64(2) by libc.
static SYS_FTRUNCATE: LazyLock<Option<libc::c_long>> = LazyLock::new(|| {
    match ScmpSyscall::from_name("ftruncate")
        .map(i32::from)
        .map(libc::c_long::from)
        .ok()
    {
        Some(n) if n < 0 => None,
        Some(n) => Some(n),
        None => None,
    }
});

/// ftruncate64(2) may not always be available via libc.
static SYS_FTRUNCATE64: LazyLock<Option<libc::c_long>> = LazyLock::new(|| {
    match ScmpSyscall::from_name("ftruncate64")
        .map(i32::from)
        .map(libc::c_long::from)
        .ok()
    {
        Some(n) if n < 0 => None,
        Some(n) => Some(n),
        None => None,
    }
});

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

    let sys_truncate = SYS_TRUNCATE.ok_or(Errno::ENOSYS)?;

    // SAFETY: In libc we trust.
    let res = path.with_nix_path(|cstr| unsafe {
        libc::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: libc::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)?;

        // i386: low, high
        let val = len as u64;
        let low = (val & 0xFFFF_FFFF) as libc::c_long;
        let high = (val >> 32) as libc::c_long;

        // SAFETY: In libc we trust.
        Errno::result(path.with_nix_path(|cstr| unsafe {
            libc::syscall(
                sys_truncate64,
                cstr.as_ptr(),
                low,
                high,
                SYSCOOKIE_POOL.get(CookieIdx::Truncate64Arg3),
                SYSCOOKIE_POOL.get(CookieIdx::Truncate64Arg4),
                SYSCOOKIE_POOL.get(CookieIdx::Truncate64Arg5),
            )
        })?)
        .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)?;

        // 32-bit ARM/ppc/mips: 0, low, high
        let val = len as u64;
        let low = (val & 0xFFFF_FFFF) as libc::c_long;
        let high = (val >> 32) as libc::c_long;

        // SAFETY: In libc we trust.
        Errno::result(path.with_nix_path(|cstr| unsafe {
            libc::syscall(
                sys_truncate64,
                cstr.as_ptr(),
                0 as libc::c_long,
                low,
                high,
                SYSCOOKIE_POOL.get(CookieIdx::Truncate64Arg4),
                SYSCOOKIE_POOL.get(CookieIdx::Truncate64Arg5),
            )
        })?)
        .map(drop)
    }
}

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

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

    // SAFETY: In libc we trust.
    Errno::result(unsafe {
        libc::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: libc::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)?;

        // i386: low, high
        let val = len as u64;
        let low = (val & 0xFFFF_FFFF) as libc::c_long;
        let high = (val >> 32) as libc::c_long;

        // SAFETY: In libc we trust.
        Errno::result(unsafe {
            libc::syscall(
                sys_ftruncate64,
                fd.as_fd().as_raw_fd(),
                low,
                high,
                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)?;

        // 32-bit ARM/ppc/mips: 0, low, high
        let val = len as u64;
        let low = (val & 0xFFFF_FFFF) as libc::c_long;
        let high = (val >> 32) as libc::c_long;

        // SAFETY: In libc we trust.
        Errno::result(unsafe {
            libc::syscall(
                sys_ftruncate64,
                fd.as_fd().as_raw_fd(),
                0 as libc::c_long,
                low,
                high,
                SYSCOOKIE_POOL.get(CookieIdx::Ftruncate64Arg4),
                SYSCOOKIE_POOL.get(CookieIdx::Ftruncate64Arg5),
            )
        })
        .map(drop)
    }
}

/// 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(),
    };

    // SAFETY: In libc we trust.
    let res = path.with_nix_path(|cstr| unsafe {
        libc::syscall(
            libc::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| {
        // SAFETY: In libc we trust.
        newpath.with_nix_path(|newcstr| unsafe {
            libc::syscall(
                libc::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 fchdir(2) confined by syscall cookies.
#[inline(always)]
pub fn safe_fchdir<Fd: AsFd>(dirfd: Fd) -> Result<(), Errno> {
    // SAFETY: In libc we trust.
    Errno::result(unsafe {
        libc::syscall(
            libc::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 uname(2) confined by syscall cookies.
#[inline(always)]
pub fn safe_uname() -> Result<UtsName, Errno> {
    let mut name = UtsName::default();

    // SAFETY: In libc we trust.
    Errno::result(unsafe {
        libc::syscall(
            libc::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: In libc we trust.
    #[expect(clippy::cast_possible_truncation)]
    #[expect(clippy::cast_sign_loss)]
    let prev = unsafe {
        libc::syscall(
            libc::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 libc::mode_t;
    #[expect(clippy::disallowed_methods)]
    Mode::from_bits(prev).expect("[BUG] umask returned invalid Mode")
}

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

    // SAFETY: In libc we trust.
    Errno::result(unsafe {
        libc::syscall(
            libc::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))
}
