//
// Syd: rock-solid application kernel
// src/workers/emu.rs: `syd_emu' emulator threads
//
// Copyright (c) 2024, 2025, 2026 Ali Polatel <alip@chesswob.org>
// Based in part upon rusty_pool which is:
//     Copyright (c) Robin Friedli <robinfriedli@icloud.com>
//     SPDX-License-Identifier: Apache-2.0
//
// SPDX-License-Identifier: GPL-3.0

use std::{
    mem::MaybeUninit,
    option::Option,
    os::fd::RawFd,
    sync::{
        atomic::{AtomicBool, Ordering},
        Arc, RwLock,
    },
    thread,
};

#[expect(deprecated)]
use libc::SOCK_PACKET;
use libc::{AF_ALG, AF_INET, AF_INET6, AF_NETLINK, AF_UNIX, EACCES, SOCK_RAW};
use libseccomp::{scmp_cmp, RawSyscall, ScmpAction, ScmpArch, ScmpFilterContext, ScmpSyscall};
use libseccomp_sys::{const_scmp_filter_ctx, seccomp_load, seccomp_notify_receive};
use nix::{
    errno::Errno,
    fcntl::OFlag,
    sched::{unshare, CloneFlags},
    unistd::{Gid, Pid, Uid},
};

use crate::{
    alert,
    compat::{seccomp_notif, seccomp_notif_resp},
    config::*,
    confine::{
        confine_scmp, confine_scmp_clone, confine_scmp_clone3, confine_scmp_madvise, extend_ioctl,
        scmp_add_setid_rules, scmp_arch_raw, ScmpNotifReq, SydArch, Sydcall, EIDRM, EOWNERDEAD,
        X32_SYSCALL_BIT,
    },
    cookie::{
        CookieIdx, SYSCOOKIE_POOL, SYS_ACCEPT4, SYS_BIND, SYS_CONNECT, SYS_SOCKET, SYS_SOCKETPAIR,
    },
    err::{err2no, SydJoinHandle, SydResult},
    fd::{close, AT_EXECVE_CHECK},
    fs::{
        seccomp_notify_respond, SECCOMP_IOCTL_NOTIF_ADDFD, SECCOMP_IOCTL_NOTIF_LIST,
        SECCOMP_IOCTL_NOTIF_SEND,
    },
    hook::HandlerMap,
    info,
    path::dotdot_with_nul,
    proc::{proc_get_vma, PROCMAP_QUERY},
    req::UNotifyEventRequest,
    sandbox::{Flags, Sandbox, SandboxGuard},
    timer::AlarmTimer,
    workers::{WorkerCache, WorkerData},
};

#[derive(Clone)]
pub(crate) struct Worker {
    fd: RawFd,
    flags: Flags,
    cache: Arc<WorkerCache>,
    sandbox: Arc<RwLock<Sandbox>>,
    handlers: Arc<HandlerMap>,
    keep_alive: Option<u16>,
    should_exit: Arc<AtomicBool>,
    worker_data: Arc<WorkerData>,
}

impl Worker {
    pub(crate) fn new(
        fd: RawFd,
        cache: Arc<WorkerCache>,
        sandbox: Arc<RwLock<Sandbox>>,
        handlers: Arc<HandlerMap>,
        keep_alive: Option<u16>,
        should_exit: Arc<AtomicBool>,
        worker_data: Arc<WorkerData>,
    ) -> Self {
        let my_sandbox = SandboxGuard::Read(sandbox.read().unwrap_or_else(|err| err.into_inner()));
        let flags = *my_sandbox.flags;
        drop(my_sandbox); // release the read lock.

        Self {
            fd,
            flags,
            cache,
            sandbox,
            handlers,
            keep_alive,
            should_exit,
            worker_data,
        }
    }

    #[expect(clippy::cognitive_complexity)]
    pub(crate) fn try_spawn(
        self,
        ctx: Option<&ScmpFilterContext>,
    ) -> Result<SydJoinHandle<()>, Errno> {
        // SAFETY: ScmpFilterContext is not Send,
        // so we cannot pass it between threads.
        // Therefore we pass a pointer which is
        // owned by the monitor thread. This
        // pointer is guaranteed to be valid
        // throughout Syd's lifetime.
        let mut ctx = ctx.map(|ctx| ctx.as_ptr() as usize);

        thread::Builder::new()
            .name("syd_emu".to_string())
            .stack_size(EMU_STACK_SIZE)
            .spawn(move || {
                // Unshare:
                // 1. CLONE_FS so cwd and umask are per-thread.
                // 2. CLONE_FILES so file descriptor table is per-thread.
                // 3. CLONE_SYSVSEM so System V semaphores are per-thread.
                //
                // Note, we cannot do 2 if Crypt sanboxing is on because
                // emulator threads have to share memory fds with AES
                // threads.
                let mut unshare_flags = CloneFlags::CLONE_FS | CloneFlags::CLONE_SYSVSEM;
                let is_crypt = self.cache.crypt_map.is_some();
                if !is_crypt {
                    unshare_flags.insert(CloneFlags::CLONE_FILES);
                }

                // SAFETY: We use exit_group(2) here to bail,
                // because this unsharing is a critical safety feature.
                if let Err(errno) = unshare(unshare_flags) {
                    alert!("ctx": "boot", "op": "unshare_emu_thread",
                        "msg": format!("failed to unshare({unshare_flags:?}): {errno}"),
                        "err": errno as i32);
                    std::process::exit(101);
                }

                // SAFETY: We use exit_group(2) here to bail,
                // because expiring idle threads is a critical safety feature.
                let mut timer = if let Some(keep_alive) = self.keep_alive {
                    match AlarmTimer::from_seconds(keep_alive.into()) {
                        Ok(timer) => Some(timer),
                        Err(errno) => {
                            alert!("ctx": "boot", "op": "timer_create_for_emu_thread",
                                "msg": format!("failed to set up timer: {errno}"),
                                "err": errno as i32);
                            std::process::exit(101);
                        }
                    }
                } else {
                    None
                };

                // Deny critical system calls on sandbox lock.
                let mut filter_crit = false;

                // Create sentinel, that will handle graceful teardown.
                let mut sentinel = Sentinel::new(&self);

                // Thread successfully started, increment total worker count.
                self.worker_data.increment_worker_total();

                loop {
                    // Confine thread if sandbox is locked.
                    if Sandbox::locked_once() {
                        if let Some(filter) = ctx {
                            // SAFETY: filter pointer is owned by the
                            // monitor thread and is valid for Syd's
                            // lifetime.
                            let error = unsafe { seccomp_load(filter as const_scmp_filter_ctx) };
                            ctx = None; // Leak ctx intentionally.

                            // SAFETY: We use exit_group(2) here to bail,
                            // because this confinement is a critical safety feature.
                            if error != 0 {
                                let errno = Errno::from_raw(error.abs());
                                alert!("ctx": "boot", "op": "confine_emu_thread",
                                    "msg": format!("failed to confine: {error}"),
                                    "err": errno as i32);
                                std::process::exit(101);
                            }
                        }

                        if !filter_crit {
                            filter_crit = true;

                            // SAFETY: We use exit_group(2) here to bail,
                            // because this confinement is a critical safety feature.
                            if let Err(error) =
                                confine_scmp(ScmpAction::KillProcess, EMU_LOCK_SYSCALLS)
                            {
                                let errno = error.errno().unwrap_or(Errno::ENOSYS);
                                alert!("ctx": "boot", "op": "confine_emu_thread",
                                    "msg": format!("failed to confine: {error}"),
                                    "err": errno as i32);
                                std::process::exit(101);
                            }

                            let safe_setid = self.flags.intersects(
                                Flags::FL_ALLOW_SAFE_SETUID | Flags::FL_ALLOW_SAFE_SETGID,
                            );
                            info!("ctx": "confine", "op": "confine_emu_thread",
                                "msg": format!("emulator thread confined with{} SROP mitigation",
                                    if safe_setid { "out" } else { "" }));
                        }
                    }

                    // Receive seccomp notification.
                    let request = if let Ok(request) = self.receive(&mut timer) {
                        request
                    } else {
                        // Critical error, decrement worker total and exit.
                        self.worker_data.decrement_worker_total();
                        break;
                    };

                    if let Some(request) = request {
                        // Mark thread busy.
                        sentinel.seccomp_id = Some(request.id);
                        self.worker_data.increment_worker_busy();

                        // Handle request.
                        self.handle(request);

                        // Mark thread idle again.
                        sentinel.seccomp_id = None;
                        self.worker_data.decrement_worker_busy();
                    } // else process died-midway, continue.

                    // Exit if there's nothing else to handle.
                    if self.should_exit.load(Ordering::Relaxed) {
                        // Time to exit.
                        break;
                    }
                }

                Ok(())
            })
            .map_err(|err| err2no(&err))
    }

    fn receive(&self, timer: &mut Option<AlarmTimer>) -> Result<Option<ScmpNotifReq>, Errno> {
        // Receive and return request.
        // Break if file descriptor was closed.
        // Ignore rest of the errors as we cannot handle them,
        // e.g: EINTR|ENOENT: task is killed mid-way.
        match self.read(timer) {
            Ok(request) => Ok(Some(request)),
            Err(Errno::EBADF) => Err(Errno::EBADF),
            Err(Errno::EINTR) if timer.is_some() => Err(Errno::EINTR),
            Err(_) => Ok(None),
        }
    }

    fn read(&self, timer: &mut Option<AlarmTimer>) -> Result<ScmpNotifReq, Errno> {
        // Use libc::seccomp_notif rather than libseccomp_sys's.
        // The latter is opaque and requires us to do a heap
        // allocation which we don't always want.
        let mut req: MaybeUninit<seccomp_notif> = MaybeUninit::zeroed();

        if let Some(timer) = timer {
            // SAFETY: Something is awfully wrong if we cannot
            // set the timer so we panic here to indicate this.
            #[expect(clippy::disallowed_methods)]
            timer.start().expect("timer_settime");
        }

        // SAFETY: libseccomp's wrapper allocates each call.
        // Note: EINTR may also mean child killed by signal!
        let res =
            Errno::result(unsafe { seccomp_notify_receive(self.fd, req.as_mut_ptr().cast()) });

        if let Some(timer) = timer {
            // SAFETY: See above.
            #[expect(clippy::disallowed_methods)]
            timer.stop().expect("timer_settime");
        }
        res?;

        // SAFETY: seccomp_notify_receive returned success.
        // Request is populated and accessing it is safe.
        let req = ScmpNotifReq::from_sys(unsafe { req.assume_init() })?;

        if req.id != 0 && req.pid != 0 {
            Ok(req)
        } else {
            // interrupted/task killed mid-way.
            Err(Errno::EINTR)
        }
    }

    #[expect(clippy::cognitive_complexity)]
    fn handle(&self, mut req: ScmpNotifReq) {
        // Correct architecture for x32 syscalls.
        if req.data.arch == ScmpArch::X8664
            && req.data.syscall.as_raw_syscall() & X32_SYSCALL_BIT != 0
        {
            req.data.arch = ScmpArch::X32;
        }

        // Lookup the system call handler, panic if not found.
        let syscall = Sydcall(req.data.syscall, scmp_arch_raw(req.data.arch));
        let handler = if let Some(handler) = self.handlers.get(&syscall) {
            handler
        } else {
            unreachable!("BUG: Missing hook for request {req:?}!");
        };

        let request = UNotifyEventRequest::new(
            req,
            syscall,
            self.fd,
            Arc::clone(&self.cache),
            Arc::clone(&self.sandbox),
        );
        let mut response = handler(request);

        // Check for the following pseudo errnos:
        // 1. EIDRM:
        //    a. A previous SECCOMP_IOCTL_NOTIF_ADDFD has replied to the request with
        //       SECCOMP_ADDFD_FLAG_SEND already, no need to return a reply again here.
        //    b. A read-write encryption request was made and the encrypted fd has
        //       already been returned as a reply with SECCOMP_IOCTL_NOTIF_ADDFD with
        //       the flag SECCOMP_ADDFD_FLAG_SEND.
        // 2. EOWNERDEAD: Enter ghost mode.
        let ghost = match response.error {
            EIDRM if response.id == 0 && response.val == 0 && response.flags == 0 => return,
            EOWNERDEAD if response.id == 0 && response.val == 0 && response.flags == 0 => {
                #[expect(clippy::cast_possible_wrap)]
                let pid = Pid::from_raw(req.pid as libc::pid_t);
                let vma = proc_get_vma(pid, req.data.instr_pointer).ok();
                crate::warn!("ctx": "confine", "op": "enter_ghost_mode", "pid": req.pid,
                    "sys": syscall, "arch": SydArch::from(req.data.arch), "args": req.data.args,
                    "ip": req.data.instr_pointer, "src": vma);

                // Correct the pseudo errno back to success.
                response.error = 0;
                response.val = 0;

                true
            }
            _ => false,
        };

        let response = seccomp_notif_resp {
            id: response.id,
            val: response.val,
            error: response.error,
            flags: response.flags,
        };

        // EINTR is not retried because it may mean child is signaled.
        // ENOENT means child died mid-way.
        // Nothing else we can do on errors here.
        let _ = seccomp_notify_respond(self.fd, std::ptr::addr_of!(response));

        // Finalize ghost mode as necessary.
        if ghost {
            let _ = close(self.fd);

            // Inform the monitor thread and other emulator threads to exit,
            // and notify the syd_aes thread as necessary.
            self.should_exit.store(true, Ordering::Relaxed);
            if let Some(ref crypt_map) = self.cache.crypt_map {
                let (aes_map, cvar) = &**crypt_map;
                let _aes_map = aes_map.lock().unwrap_or_else(|e| e.into_inner());
                cvar.notify_one();
            } // Lock is released here.
        }
    }

    /// Confine Worker thread.
    #[expect(clippy::cognitive_complexity)]
    pub(crate) fn prepare_confine(
        seccomp_fd: RawFd,
        flags: Flags,
        is_crypt: bool,
        transit_uids: &[(Uid, Uid)],
        transit_gids: &[(Gid, Gid)],
    ) -> SydResult<ScmpFilterContext> {
        // Create seccomp filter with default action.
        let mut ctx = ScmpFilterContext::new(ScmpAction::KillProcess)?;

        // Enforce the NO_NEW_PRIVS functionality before
        // loading the seccomp filter into the kernel.
        ctx.set_ctl_nnp(true)?;

        // Disable Speculative Store Bypass mitigations
        // with trace/allow_unsafe_exec_speculative:1
        ctx.set_ctl_ssb(flags.allow_unsafe_exec_speculative())?;

        // DO NOT synchronize filter to all threads.
        // Main thread will confine itself.
        ctx.set_ctl_tsync(false)?;

        // We kill for bad system call and bad arch.
        ctx.set_act_badarch(ScmpAction::KillProcess)?;

        // Use a binary tree sorted by syscall number if possible.
        let _ = ctx.set_ctl_optimize(2);

        // SAFETY: Do NOT add supported architectures to the filter.
        // This ensures Syd can never run a non-native system call,
        // which we do not need at all.
        // seccomp_add_architectures(&mut ctx)?;

        // Allow clones without namespace flags.
        confine_scmp_clone(&mut ctx)?;
        // Deny clone3 with ENOSYS for compatibility.
        confine_scmp_clone3(&mut ctx)?;

        // Allow safe madvise(2) advice.
        confine_scmp_madvise(&mut ctx)?;

        // Deny open and {l,}stat with ENOSYS rather than KillProcess.
        // We need this because std::thread::spawn has unwanted
        // side-effects such as opening /sys/devices/system/cpu/online
        // on some architectures.
        //
        // Note, we avoid this when profiling is enabled,
        // as gperf requires it to write profiling data.
        for sysname in ["open", "stat", "lstat"] {
            match ScmpSyscall::from_name(sysname) {
                Ok(syscall) => {
                    let action = if !cfg!(feature = "prof") {
                        ScmpAction::Errno(Errno::ENOSYS as i32)
                    } else {
                        ScmpAction::Allow
                    };
                    ctx.add_rule(action, syscall)?;
                }
                Err(_) => {
                    info!("ctx": "confine", "op": "allow_emu_syscall",
                        "msg": format!("invalid or unsupported syscall {sysname}"));
                }
            }
        }

        // Ioctl Restrictions:
        // 1. Allow a restricted set of ioctl(2) operations to the seccomp(2) fd.
        // 2. Allow ioctl(2) request PROCMAP_QUERY to lookup proc_pid_maps(5) efficiently.
        //    This request is new in Linux-6.11.
        //
        // Syscall argument cookies for PROCMAP_QUERY and
        // SECCOMP_IOCTL_NOTIF_SEND my be disabled at startup with
        // trace/allow_unsafe_nocookie:1.
        let sysname = "ioctl";
        #[expect(clippy::cast_sign_loss)]
        #[expect(clippy::useless_conversion)]
        match ScmpSyscall::from_name(sysname) {
            Ok(syscall) => {
                for ioctl_request in SECCOMP_IOCTL_NOTIF_LIST {
                    let mut rules = vec![scmp_cmp!($arg0 == seccomp_fd as u64)];

                    // SAFETY: We protect SECCOMP_IOCTL_NOTIF_ADDFD with system call argument
                    // cookies, to raise the bar against an attacker who has compromised Syd
                    // and aims steal file descriptors.
                    //
                    // Randomizing the seccomp-fd at startup is another mitigation against this.
                    if !flags.allow_unsafe_nocookie() && *ioctl_request == SECCOMP_IOCTL_NOTIF_ADDFD
                    {
                        rules.extend(&[
                            scmp_cmp!($arg3 == SYSCOOKIE_POOL.get(CookieIdx::SeccompIoctlNotifAddfdArg3).into()),
                            scmp_cmp!($arg4 == SYSCOOKIE_POOL.get(CookieIdx::SeccompIoctlNotifAddfdArg4).into()),
                            scmp_cmp!($arg5 == SYSCOOKIE_POOL.get(CookieIdx::SeccompIoctlNotifAddfdArg5).into()),
                        ]);
                    }

                    // SAFETY: We protect SECCOMP_IOCTL_NOTIF_SEND with system call argument
                    // cookies, to raise the bar against an attacker who has compromised Syd
                    // and aims to inject the flag SECCOMP_USER_NOTIF_FLAG_CONTINUE to this
                    // response in order to pass-through a system call to the host Linux kernel.
                    //
                    // Randomizing the seccomp-fd at startup is another mitigation against this.
                    if !flags.allow_unsafe_nocookie() && *ioctl_request == SECCOMP_IOCTL_NOTIF_SEND
                    {
                        rules.extend(&[
                            scmp_cmp!($arg3 == SYSCOOKIE_POOL.get(CookieIdx::SeccompIoctlNotifSendArg3).into()),
                            scmp_cmp!($arg4 == SYSCOOKIE_POOL.get(CookieIdx::SeccompIoctlNotifSendArg4).into()),
                            scmp_cmp!($arg5 == SYSCOOKIE_POOL.get(CookieIdx::SeccompIoctlNotifSendArg5).into()),
                        ]);
                    }

                    rules.push(scmp_cmp!($arg1 == (*ioctl_request).into()));
                    ctx.add_rule_conditional(ScmpAction::Allow, syscall, &rules)?;

                    if let Some(ioctl_request) = extend_ioctl((*ioctl_request).into()) {
                        rules.pop();
                        rules.push(scmp_cmp!($arg1 == ioctl_request));
                        ctx.add_rule_conditional(ScmpAction::Allow, syscall, &rules)?;
                    }
                }

                if *HAVE_PROCMAP_QUERY {
                    #[expect(clippy::unnecessary_cast)]
                    let ioctl_request = PROCMAP_QUERY as u64;
                    let mut rules =
                        Vec::with_capacity(if flags.allow_unsafe_nocookie() { 1 } else { 4 });
                    if !flags.allow_unsafe_nocookie() {
                        rules.extend(&[
                            scmp_cmp!($arg3 == SYSCOOKIE_POOL.get(CookieIdx::ProcmapQueryArg3).into()),
                            scmp_cmp!($arg4 == SYSCOOKIE_POOL.get(CookieIdx::ProcmapQueryArg4).into()),
                            scmp_cmp!($arg5 == SYSCOOKIE_POOL.get(CookieIdx::ProcmapQueryArg5).into()),
                        ]);
                    }

                    rules.push(scmp_cmp!($arg1 == ioctl_request));
                    ctx.add_rule_conditional(ScmpAction::Allow, syscall, &rules)?;

                    if let Some(ioctl_request) = extend_ioctl(ioctl_request) {
                        rules.pop();
                        rules.push(scmp_cmp!($arg1 == ioctl_request));
                        ctx.add_rule_conditional(ScmpAction::Allow, syscall, &rules)?;
                    }
                }
            }
            Err(_) => {
                info!("ctx": "confine", "op": "allow_emu_syscall",
                    "msg": format!("invalid or unsupported syscall {sysname}"));
            }
        }

        // Allow unshare(2) with CLONE_FS|CLONE_FILES|CLONE_SYSVSEM only.
        let sysname = "unshare";
        match ScmpSyscall::from_name(sysname) {
            Ok(syscall) => {
                let flags = if is_crypt {
                    (libc::CLONE_FS | libc::CLONE_SYSVSEM) as u64
                } else {
                    (libc::CLONE_FILES | libc::CLONE_FS | libc::CLONE_SYSVSEM) as u64
                };
                ctx.add_rule_conditional(ScmpAction::Allow, syscall, &[scmp_cmp!($arg0 == flags)])?;
            }
            Err(_) => {
                info!("ctx": "confine", "op": "allow_emu_syscall",
                    "msg": format!("invalid or unsupported syscall {sysname}"));
            }
        }

        // Restrict pipe2(2) flags.
        // Restrict pipe2(2) using syscall argument cookies.
        let sysname = "pipe2";
        #[expect(clippy::cast_sign_loss)]
        #[expect(clippy::useless_conversion)]
        match ScmpSyscall::from_name(sysname) {
            Ok(syscall) => {
                let mut rules = vec![scmp_cmp!($arg1 == OFlag::O_CLOEXEC.bits() as u64)];
                if !flags.allow_unsafe_nocookie() {
                    rules.extend(&[
                        scmp_cmp!($arg2 == SYSCOOKIE_POOL.get(CookieIdx::Pipe2Arg2).into()),
                        scmp_cmp!($arg3 == SYSCOOKIE_POOL.get(CookieIdx::Pipe2Arg3).into()),
                        scmp_cmp!($arg4 == SYSCOOKIE_POOL.get(CookieIdx::Pipe2Arg4).into()),
                        scmp_cmp!($arg5 == SYSCOOKIE_POOL.get(CookieIdx::Pipe2Arg5).into()),
                    ]);
                }

                ctx.add_rule_conditional(ScmpAction::Allow, syscall, &rules)?;
            }
            Err(_) => {
                info!("ctx": "confine", "op": "allow_emu_syscall",
                    "msg": format!("invalid or unsupported syscall {sysname}"));
            }
        }

        // Allow safe fcntl(2) utility calls.
        for sysname in ["fcntl", "fcntl64"] {
            let syscall = match ScmpSyscall::from_name(sysname) {
                Ok(syscall) => syscall,
                Err(_) => {
                    info!("ctx": "confine", "op": "allow_emu_syscall",
                        "msg": format!("invalid or unsupported syscall {sysname}"));
                    continue;
                }
            };

            for op in EMU_FCNTL_OPS {
                ctx.add_rule_conditional(ScmpAction::Allow, syscall, &[scmp_cmp!($arg1 == *op)])?;
            }
        }

        // Allow safe prctl(2) operations.
        let sysname = "prctl";
        if let Ok(syscall) = ScmpSyscall::from_name(sysname) {
            let safe_setid =
                flags.intersects(Flags::FL_ALLOW_SAFE_SETUID | Flags::FL_ALLOW_SAFE_SETGID);
            let iter = EMU_PRCTL_OPS.iter().chain(if safe_setid {
                EMU_PRCTL_OPS_SAFESETID.iter()
            } else {
                [].iter()
            });
            for (_, op) in iter {
                ctx.add_rule_conditional(ScmpAction::Allow, syscall, &[scmp_cmp!($arg0 == *op)])?;
            }
        } else {
            info!("ctx": "confine", "op": "allow_emu_syscall",
                "msg": format!("invalid or unsupported syscall {sysname}"));
        }

        // Deny installing new signal handlers for {rt_,}sigaction(2).
        for sysname in ["sigaction", "rt_sigaction"] {
            let syscall = match ScmpSyscall::from_name(sysname) {
                Ok(syscall) => syscall,
                Err(_) => {
                    info!("ctx": "confine", "op": "allow_emu_syscall",
                        "msg": format!("invalid or unsupported syscall {sysname}"));
                    continue;
                }
            };

            // Installs a signal handler if first argument is non-NULL.
            // We deny this case, but allow returning the current handler.
            ctx.add_rule_conditional(ScmpAction::Allow, syscall, &[scmp_cmp!($arg1 == 0)])?;
        }

        // Allow safe system calls.
        for sysname in EMU_SYSCALLS.iter().chain(VDSO_SYSCALLS) {
            match ScmpSyscall::from_name(sysname) {
                Ok(syscall) => {
                    ctx.add_rule(ScmpAction::Allow, syscall)?;
                }
                Err(_) => {
                    info!("ctx": "confine", "op": "allow_emu_syscall",
                        "msg": format!("invalid or unsupported syscall {sysname}"));
                }
            }
        }

        // Syscall argument cookies may be disabled
        // at startup with trace/allow_unsafe_nocookie:1.
        if !flags.allow_unsafe_nocookie() {
            // memfd_create(2) may be used only with syscall argument cookies.
            let sysname = "memfd_create";
            #[expect(clippy::useless_conversion)]
            match ScmpSyscall::from_name(sysname) {
                Ok(syscall) => {
                    ctx.add_rule_conditional(
                        ScmpAction::Allow,
                        syscall,
                        &[
                            scmp_cmp!($arg2 == SYSCOOKIE_POOL.get(CookieIdx::MemfdCreateArg2).into()),
                            scmp_cmp!($arg3 == SYSCOOKIE_POOL.get(CookieIdx::MemfdCreateArg3).into()),
                            scmp_cmp!($arg4 == SYSCOOKIE_POOL.get(CookieIdx::MemfdCreateArg4).into()),
                            scmp_cmp!($arg5 == SYSCOOKIE_POOL.get(CookieIdx::MemfdCreateArg5).into()),
                        ],
                    )?;
                }
                Err(_) => {
                    info!("ctx": "confine", "op": "allow_emu_syscall",
                        "msg": format!("invalid or unsupported syscall {sysname}"));
                }
            }

            // renameat2(2) may be used only with syscall argument cookies.
            // We also prevent AT_FDCWD usage as fd argument.
            let sysname = "renameat2";
            #[expect(clippy::useless_conversion)]
            match ScmpSyscall::from_name(sysname) {
                Ok(syscall) => {
                    ctx.add_rule_conditional(
                        ScmpAction::Allow,
                        syscall,
                        &[
                            scmp_cmp!($arg0 <= RawFd::MAX as u64),
                            scmp_cmp!($arg5 == SYSCOOKIE_POOL.get(CookieIdx::Renameat2Arg5).into()),
                        ],
                    )?;
                }
                Err(_) => {
                    info!("ctx": "confine", "op": "allow_emu_syscall",
                        "msg": format!("invalid or unsupported syscall {sysname}"));
                }
            }

            // {,f}truncate{,64}(2) may be used only with syscall argument cookies.
            let sysname = "truncate";
            #[expect(clippy::useless_conversion)]
            match ScmpSyscall::from_name(sysname) {
                Ok(syscall) => {
                    ctx.add_rule_conditional(
                        ScmpAction::Allow,
                        syscall,
                        &[
                            scmp_cmp!($arg2 == SYSCOOKIE_POOL.get(CookieIdx::TruncateArg2).into()),
                            scmp_cmp!($arg3 == SYSCOOKIE_POOL.get(CookieIdx::TruncateArg3).into()),
                            scmp_cmp!($arg4 == SYSCOOKIE_POOL.get(CookieIdx::TruncateArg4).into()),
                            scmp_cmp!($arg5 == SYSCOOKIE_POOL.get(CookieIdx::TruncateArg5).into()),
                        ],
                    )?;
                }
                Err(_) => {
                    info!("ctx": "confine", "op": "allow_emu_syscall",
                        "msg": format!("invalid or unsupported syscall {sysname}"));
                }
            }

            let sysname = "truncate64";
            #[expect(clippy::useless_conversion)]
            match ScmpSyscall::from_name(sysname) {
                Ok(syscall) => {
                    ctx.add_rule_conditional(
                        ScmpAction::Allow,
                        syscall,
                        &[
                            #[cfg(target_arch = "x86")]
                            scmp_cmp!($arg3 == SYSCOOKIE_POOL.get(CookieIdx::Truncate64Arg3).into()),
                            scmp_cmp!($arg4 == SYSCOOKIE_POOL.get(CookieIdx::Truncate64Arg4).into()),
                            scmp_cmp!($arg5 == SYSCOOKIE_POOL.get(CookieIdx::Truncate64Arg5).into()),
                        ],
                    )?;
                }
                Err(_) => {
                    info!("ctx": "confine", "op": "allow_emu_syscall",
                        "msg": format!("invalid or unsupported syscall {sysname}"));
                }
            }

            let sysname = "ftruncate";
            #[expect(clippy::useless_conversion)]
            match ScmpSyscall::from_name(sysname) {
                Ok(syscall) => {
                    ctx.add_rule_conditional(
                        ScmpAction::Allow,
                        syscall,
                        &[
                            scmp_cmp!($arg2 == SYSCOOKIE_POOL.get(CookieIdx::FtruncateArg2).into()),
                            scmp_cmp!($arg3 == SYSCOOKIE_POOL.get(CookieIdx::FtruncateArg3).into()),
                            scmp_cmp!($arg4 == SYSCOOKIE_POOL.get(CookieIdx::FtruncateArg4).into()),
                            scmp_cmp!($arg5 == SYSCOOKIE_POOL.get(CookieIdx::FtruncateArg5).into()),
                        ],
                    )?;
                }
                Err(_) => {
                    info!("ctx": "confine", "op": "allow_emu_syscall",
                        "msg": format!("invalid or unsupported syscall {sysname}"));
                }
            }

            let sysname = "ftruncate64";
            #[expect(clippy::useless_conversion)]
            match ScmpSyscall::from_name(sysname) {
                Ok(syscall) => {
                    ctx.add_rule_conditional(
                        ScmpAction::Allow,
                        syscall,
                        &[
                            #[cfg(target_arch = "x86")]
                            scmp_cmp!($arg3 == SYSCOOKIE_POOL.get(CookieIdx::Ftruncate64Arg3).into()),
                            scmp_cmp!($arg4 == SYSCOOKIE_POOL.get(CookieIdx::Ftruncate64Arg4).into()),
                            scmp_cmp!($arg5 == SYSCOOKIE_POOL.get(CookieIdx::Ftruncate64Arg5).into()),
                        ],
                    )?;
                }
                Err(_) => {
                    info!("ctx": "confine", "op": "allow_emu_syscall",
                        "msg": format!("invalid or unsupported syscall {sysname}"));
                }
            }

            // linkat(2) may be used only with syscall argument cookies.
            // We also enforce PROC_FD usage as fd argument.
            // This fd is randomized so it further raises the bar.
            let sysname = "linkat";
            #[expect(clippy::cast_sign_loss)]
            #[expect(clippy::useless_conversion)]
            match ScmpSyscall::from_name(sysname) {
                Ok(syscall) => {
                    ctx.add_rule_conditional(
                        ScmpAction::Allow,
                        syscall,
                        &[
                            scmp_cmp!($arg0 == PROC_FD() as u64),
                            scmp_cmp!($arg5 == SYSCOOKIE_POOL.get(CookieIdx::LinkatArg5).into()),
                        ],
                    )?;
                }
                Err(_) => {
                    info!("ctx": "confine", "op": "allow_emu_syscall",
                        "msg": format!("invalid or unsupported syscall {sysname}"));
                }
            }

            // unlinkat(2) may be used only with syscall argument cookies.
            // We also prevent AT_FDCWD usage as fd argument.
            let sysname = "unlinkat";
            #[expect(clippy::useless_conversion)]
            match ScmpSyscall::from_name(sysname) {
                Ok(syscall) => {
                    ctx.add_rule_conditional(
                        ScmpAction::Allow,
                        syscall,
                        &[
                            scmp_cmp!($arg0 <= RawFd::MAX as u64),
                            scmp_cmp!($arg3 == SYSCOOKIE_POOL.get(CookieIdx::UnlinkatArg3).into()),
                            scmp_cmp!($arg4 == SYSCOOKIE_POOL.get(CookieIdx::UnlinkatArg4).into()),
                            scmp_cmp!($arg5 == SYSCOOKIE_POOL.get(CookieIdx::UnlinkatArg5).into()),
                        ],
                    )?;
                }
                Err(_) => {
                    info!("ctx": "confine", "op": "allow_emu_syscall",
                        "msg": format!("invalid or unsupported syscall {sysname}"));
                }
            }

            // openat2(2) may be used only with syscall argument cookies.
            // We also prevent AT_FDCWD usage as fd argument.
            let sysname = "openat2";
            #[expect(clippy::useless_conversion)]
            match ScmpSyscall::from_name(sysname) {
                Ok(syscall) => {
                    ctx.add_rule_conditional(
                        ScmpAction::Allow,
                        syscall,
                        &[
                            scmp_cmp!($arg0 <= RawFd::MAX as u64),
                            scmp_cmp!($arg4 == SYSCOOKIE_POOL.get(CookieIdx::Openat2Arg4).into()),
                            scmp_cmp!($arg5 == SYSCOOKIE_POOL.get(CookieIdx::Openat2Arg5).into()),
                        ],
                    )?;
                }
                Err(_) => {
                    info!("ctx": "confine", "op": "allow_emu_syscall",
                        "msg": format!("invalid or unsupported syscall {sysname}"));
                }
            }

            // uname(2) may be used only with syscall argument cookies.
            let sysname = "uname";
            #[expect(clippy::useless_conversion)]
            match ScmpSyscall::from_name(sysname) {
                Ok(syscall) => {
                    ctx.add_rule_conditional(
                        ScmpAction::Allow,
                        syscall,
                        &[
                            scmp_cmp!($arg1 == SYSCOOKIE_POOL.get(CookieIdx::UnameArg1).into()),
                            scmp_cmp!($arg2 == SYSCOOKIE_POOL.get(CookieIdx::UnameArg2).into()),
                            scmp_cmp!($arg3 == SYSCOOKIE_POOL.get(CookieIdx::UnameArg3).into()),
                            scmp_cmp!($arg4 == SYSCOOKIE_POOL.get(CookieIdx::UnameArg4).into()),
                            scmp_cmp!($arg5 == SYSCOOKIE_POOL.get(CookieIdx::UnameArg5).into()),
                        ],
                    )?;
                }
                Err(_) => {
                    info!("ctx": "confine", "op": "allow_emu_syscall",
                        "msg": format!("invalid or unsupported syscall {sysname}"));
                }
            }

            // fchdir(2) may be used only with syscall argument cookies,
            // and positive file descriptor arguments.
            let sysname = "fchdir";
            #[expect(clippy::useless_conversion)]
            match ScmpSyscall::from_name(sysname) {
                Ok(syscall) => {
                    ctx.add_rule_conditional(
                        ScmpAction::Allow,
                        syscall,
                        &[
                            scmp_cmp!($arg0 <= RawFd::MAX as u64),
                            scmp_cmp!($arg1 == SYSCOOKIE_POOL.get(CookieIdx::FchdirArg1).into()),
                            scmp_cmp!($arg2 == SYSCOOKIE_POOL.get(CookieIdx::FchdirArg2).into()),
                            scmp_cmp!($arg3 == SYSCOOKIE_POOL.get(CookieIdx::FchdirArg3).into()),
                            scmp_cmp!($arg4 == SYSCOOKIE_POOL.get(CookieIdx::FchdirArg4).into()),
                            scmp_cmp!($arg5 == SYSCOOKIE_POOL.get(CookieIdx::FchdirArg5).into()),
                        ],
                    )?;
                }
                Err(_) => {
                    info!("ctx": "confine", "op": "allow_emu_syscall",
                        "msg": format!("invalid or unsupported syscall {sysname}"));
                }
            }

            // umask(2) may be used only with syscall argument cookies,
            let sysname = "umask";
            #[expect(clippy::useless_conversion)]
            match ScmpSyscall::from_name(sysname) {
                Ok(syscall) => {
                    ctx.add_rule_conditional(
                        ScmpAction::Allow,
                        syscall,
                        &[
                            scmp_cmp!($arg1 == SYSCOOKIE_POOL.get(CookieIdx::UmaskArg1).into()),
                            scmp_cmp!($arg2 == SYSCOOKIE_POOL.get(CookieIdx::UmaskArg2).into()),
                            scmp_cmp!($arg3 == SYSCOOKIE_POOL.get(CookieIdx::UmaskArg3).into()),
                            scmp_cmp!($arg4 == SYSCOOKIE_POOL.get(CookieIdx::UmaskArg4).into()),
                            scmp_cmp!($arg5 == SYSCOOKIE_POOL.get(CookieIdx::UmaskArg5).into()),
                        ],
                    )?;
                }
                Err(_) => {
                    info!("ctx": "confine", "op": "allow_emu_syscall",
                        "msg": format!("invalid or unsupported syscall {sysname}"));
                }
            }
        } else {
            // trace/allow_unsafe_nocookie: Allow access without cookies.
            for sysname in [
                "memfd_create",
                "renameat2",
                "truncate",
                "truncate64",
                "ftruncate",
                "ftruncate64",
                "linkat",
                "unlinkat",
                "openat2",
                "uname",
                "fchdir",
                "umask",
            ] {
                match ScmpSyscall::from_name(sysname) {
                    Ok(syscall) => {
                        ctx.add_rule(ScmpAction::Allow, syscall)?;
                    }
                    Err(_) => {
                        info!("ctx": "confine", "op": "allow_emu_syscall",
                        "msg": format!("invalid or unsupported syscall {sysname}"));
                    }
                }
            }
        }

        // openat(2) may be used to open the parent directory only by getdir_long().
        // The rest of the attempts are denied with ENOSYS for compat.
        let sysname = "openat";
        match ScmpSyscall::from_name(sysname) {
            Ok(syscall) => {
                let dotdot = dotdot_with_nul();
                let oflags = (libc::O_RDONLY
                    | libc::O_CLOEXEC
                    | libc::O_DIRECTORY
                    | libc::O_LARGEFILE
                    | libc::O_NOCTTY
                    | libc::O_NOFOLLOW) as u64;
                ctx.add_rule_conditional(
                    ScmpAction::Allow,
                    syscall,
                    &[
                        scmp_cmp!($arg0 <= RawFd::MAX as u64),
                        scmp_cmp!($arg1 == dotdot),
                        scmp_cmp!($arg2 & oflags == oflags),
                    ],
                )?;
                ctx.add_rule_conditional(
                    ScmpAction::Errno(Errno::ENOSYS as i32),
                    syscall,
                    &[scmp_cmp!($arg0 > RawFd::MAX as u64)],
                )?;
                ctx.add_rule_conditional(
                    ScmpAction::Errno(Errno::ENOSYS as i32),
                    syscall,
                    &[scmp_cmp!($arg1 != dotdot)],
                )?;
            }
            Err(_) => {
                info!("ctx": "confine", "op": "allow_emu_syscall",
                    "msg": format!("invalid or unsupported syscall {sysname}"));
            }
        }

        // socket(2) may be used only with syscall argument cookies.
        //
        // We only enforce this on architectures where the system call is direct,
        // and there's no socketcall(2) multiplexer indirection.
        #[expect(clippy::cast_possible_truncation)]
        #[expect(clippy::cast_sign_loss)]
        #[expect(clippy::useless_conversion)]
        #[expect(deprecated)]
        if let Some(syscall) = SYS_SOCKET.map(|n| ScmpSyscall::from_raw_syscall(n as RawSyscall)) {
            // Deny based on socket type.
            if !flags.allow_unsafe_socket() {
                for ty in [SOCK_RAW as u64, SOCK_PACKET as u64] {
                    ctx.add_rule_conditional(
                        ScmpAction::Errno(EACCES),
                        syscall,
                        &[scmp_cmp!($arg1 == ty)],
                    )?;
                }
            }

            // Allow only specified socket domains.
            let domains = if !flags.allow_unsupp_socket() {
                let mut domains = vec![AF_UNIX, AF_INET, AF_INET6, AF_NETLINK];
                if flags.allow_safe_kcapi() {
                    domains.push(AF_ALG);
                }
                Some(domains)
            } else {
                None
            };

            // Secure using syscall argument cookies.
            let mut rules = vec![];
            if !flags.allow_unsafe_nocookie() {
                rules.extend(&[
                    scmp_cmp!($arg3 == SYSCOOKIE_POOL.get(CookieIdx::SocketArg3).into()),
                    scmp_cmp!($arg4 == SYSCOOKIE_POOL.get(CookieIdx::SocketArg4).into()),
                    scmp_cmp!($arg5 == SYSCOOKIE_POOL.get(CookieIdx::SocketArg5).into()),
                ]);
            }

            if let Some(domains) = domains {
                for domain in domains {
                    rules.push(scmp_cmp!($arg0 == domain as u64));
                    ctx.add_rule_conditional(ScmpAction::Allow, syscall, &rules)?;
                    rules.pop();
                }
            } else if rules.is_empty() {
                ctx.add_rule(ScmpAction::Allow, syscall)?;
            } else {
                ctx.add_rule_conditional(ScmpAction::Allow, syscall, &rules)?;
            }
        } else {
            match ScmpSyscall::from_name("socket") {
                Ok(syscall) => {
                    // Allow socketcall(2).
                    ctx.add_rule(ScmpAction::Allow, syscall)?;
                }
                Err(_) => {
                    info!("ctx": "confine", "op": "allow_emu_syscall",
                        "msg": "invalid or unsupported syscall socket");
                }
            }
        }

        // socketpair(2) may be used only with syscall argument cookies.
        //
        // We only enforce this on architectures where the system call is direct,
        // and there's no socketcall(2) multiplexer indirection.
        #[expect(clippy::cast_possible_truncation)]
        #[expect(clippy::cast_sign_loss)]
        #[expect(clippy::useless_conversion)]
        #[expect(deprecated)]
        if let Some(syscall) =
            SYS_SOCKETPAIR.map(|n| ScmpSyscall::from_raw_syscall(n as RawSyscall))
        {
            // Deny based on socket type.
            if !flags.allow_unsafe_socket() {
                for ty in [SOCK_RAW as u64, SOCK_PACKET as u64] {
                    ctx.add_rule_conditional(
                        ScmpAction::Errno(EACCES),
                        syscall,
                        &[scmp_cmp!($arg1 == ty)],
                    )?;
                }
            }

            // Allow only specified socket domains.
            let domains = if !flags.allow_unsupp_socket() {
                let mut domains = vec![AF_UNIX, AF_INET, AF_INET6, AF_NETLINK];
                if flags.allow_safe_kcapi() {
                    domains.push(AF_ALG);
                }
                Some(domains)
            } else {
                None
            };

            // Secure using syscall argument cookies.
            let mut rules = vec![];
            if !flags.allow_unsafe_nocookie() {
                rules.extend(&[
                    scmp_cmp!($arg4 == SYSCOOKIE_POOL.get(CookieIdx::SocketpairArg4).into()),
                    scmp_cmp!($arg5 == SYSCOOKIE_POOL.get(CookieIdx::SocketpairArg5).into()),
                ]);
            }

            if let Some(domains) = domains {
                for domain in domains {
                    rules.push(scmp_cmp!($arg0 == domain as u64));
                    ctx.add_rule_conditional(ScmpAction::Allow, syscall, &rules)?;
                    rules.pop();
                }
            } else if rules.is_empty() {
                ctx.add_rule(ScmpAction::Allow, syscall)?;
            } else {
                ctx.add_rule_conditional(ScmpAction::Allow, syscall, &rules)?;
            }
        } else {
            match ScmpSyscall::from_name("socketpair") {
                Ok(syscall) => {
                    // Allow socketcall(2).
                    ctx.add_rule(ScmpAction::Allow, syscall)?;
                }
                Err(_) => {
                    info!("ctx": "confine", "op": "allow_emu_syscall",
                        "msg": "invalid or unsupported syscall socketpair");
                }
            }
        }

        // accept4(2) may be used only with syscall argument cookies.
        //
        // We only enforce this on architectures where the system call is direct,
        // and there's no socketcall(2) multiplexer indirection.
        #[expect(clippy::cast_possible_truncation)]
        #[expect(clippy::useless_conversion)]
        if let Some(syscall) = SYS_ACCEPT4.map(|n| ScmpSyscall::from_raw_syscall(n as RawSyscall)) {
            // Secure using syscall argument cookies.
            let mut rules = vec![];
            if !flags.allow_unsafe_nocookie() {
                rules.extend(&[
                    scmp_cmp!($arg4 == SYSCOOKIE_POOL.get(CookieIdx::Accept4Arg4).into()),
                    scmp_cmp!($arg5 == SYSCOOKIE_POOL.get(CookieIdx::Accept4Arg5).into()),
                ]);
            }

            if rules.is_empty() {
                ctx.add_rule(ScmpAction::Allow, syscall)?;
            } else {
                ctx.add_rule_conditional(ScmpAction::Allow, syscall, &rules)?;
            }
        } else {
            match ScmpSyscall::from_name("accept4") {
                Ok(syscall) => {
                    // Allow socketcall(2).
                    ctx.add_rule(ScmpAction::Allow, syscall)?;
                }
                Err(_) => {
                    info!("ctx": "confine", "op": "allow_emu_syscall",
                        "msg": "invalid or unsupported syscall accept4");
                }
            }
        }

        // bind(2) may be used only with syscall argument cookies.
        //
        // We only enforce this on architectures where the system call is direct,
        // and there's no socketcall(2) multiplexer indirection.
        #[expect(clippy::cast_possible_truncation)]
        #[expect(clippy::useless_conversion)]
        if let Some(syscall) = SYS_BIND.map(|n| ScmpSyscall::from_raw_syscall(n as RawSyscall)) {
            // Secure using syscall argument cookies.
            let mut rules = vec![];
            if !flags.allow_unsafe_nocookie() {
                rules.extend(&[
                    scmp_cmp!($arg3 == SYSCOOKIE_POOL.get(CookieIdx::BindArg3).into()),
                    scmp_cmp!($arg4 == SYSCOOKIE_POOL.get(CookieIdx::BindArg4).into()),
                    scmp_cmp!($arg5 == SYSCOOKIE_POOL.get(CookieIdx::BindArg5).into()),
                ]);
            }

            if rules.is_empty() {
                ctx.add_rule(ScmpAction::Allow, syscall)?;
            } else {
                ctx.add_rule_conditional(ScmpAction::Allow, syscall, &rules)?;
            }
        } else {
            match ScmpSyscall::from_name("bind") {
                Ok(syscall) => {
                    // Allow socketcall(2).
                    ctx.add_rule(ScmpAction::Allow, syscall)?;
                }
                Err(_) => {
                    info!("ctx": "confine", "op": "allow_emu_syscall",
                        "msg": "invalid or unsupported syscall bind");
                }
            }
        }

        // connect(2) may be used only with syscall argument cookies.
        //
        // We only enforce this on architectures where the system call is direct,
        // and there's no socketcall(2) multiplexer indirection.
        #[expect(clippy::cast_possible_truncation)]
        #[expect(clippy::useless_conversion)]
        if let Some(syscall) = SYS_CONNECT.map(|n| ScmpSyscall::from_raw_syscall(n as RawSyscall)) {
            // Secure using syscall argument cookies.
            let mut rules = vec![];
            if !flags.allow_unsafe_nocookie() {
                rules.extend(&[
                    scmp_cmp!($arg3 == SYSCOOKIE_POOL.get(CookieIdx::ConnectArg3).into()),
                    scmp_cmp!($arg4 == SYSCOOKIE_POOL.get(CookieIdx::ConnectArg4).into()),
                    scmp_cmp!($arg5 == SYSCOOKIE_POOL.get(CookieIdx::ConnectArg5).into()),
                ]);
            }

            if rules.is_empty() {
                ctx.add_rule(ScmpAction::Allow, syscall)?;
            } else {
                ctx.add_rule_conditional(ScmpAction::Allow, syscall, &rules)?;
            }
        } else {
            match ScmpSyscall::from_name("connect") {
                Ok(syscall) => {
                    // Allow socketcall(2).
                    ctx.add_rule(ScmpAction::Allow, syscall)?;
                }
                Err(_) => {
                    info!("ctx": "confine", "op": "allow_emu_syscall",
                        "msg": "invalid or unsupported syscall connect");
                }
            }
        }

        // Allow futex system calls.
        for sysname in FUTEX_SYSCALLS {
            match ScmpSyscall::from_name(sysname) {
                Ok(syscall) => {
                    ctx.add_rule(ScmpAction::Allow, syscall)?;
                }
                Err(_) => {
                    info!("ctx": "confine", "op": "allow_emu_syscall",
                        "msg": format!("invalid or unsupported syscall {sysname}"));
                }
            }
        }

        // Allow getid system calls.
        for sysname in GET_ID_SYSCALLS {
            match ScmpSyscall::from_name(sysname) {
                Ok(syscall) => {
                    ctx.add_rule(ScmpAction::Allow, syscall)?;
                }
                Err(_) => {
                    info!("ctx": "confine", "op": "allow_emu_syscall",
                        "msg": format!("invalid or unsupported syscall {sysname}"));
                }
            }
        }

        // Allow execveat(2) with AT_EXECVE_CHECK for Linux>=6.14.
        if *HAVE_AT_EXECVE_CHECK {
            let sysname = "execveat";
            #[expect(clippy::cast_sign_loss)]
            let atcheck = AT_EXECVE_CHECK.bits() as u64;
            match ScmpSyscall::from_name(sysname) {
                Ok(syscall) => {
                    ctx.add_rule_conditional(
                        ScmpAction::Allow,
                        syscall,
                        &[scmp_cmp!($arg4 & atcheck == atcheck)],
                    )?;
                }
                Err(_) => {
                    info!("ctx": "confine", "op": "allow_emu_syscall",
                        "msg": format!("invalid or unsupported syscall {sysname}"));
                }
            }
        }

        // Allow UID/GID changing system calls as necessary.
        let safe_setuid = flags.allow_safe_setuid();
        let safe_setgid = flags.allow_safe_setgid();
        if safe_setuid || safe_setgid {
            scmp_add_setid_rules(
                "emu",
                &mut ctx,
                safe_setuid,
                safe_setgid,
                transit_uids,
                transit_gids,
            )?;

            // SAFETY:
            // 1. cap{g,s}et is necessary to drop CAP_SET{U,G}ID after changing {U,G}ID.
            // 2. Signal system calls are necessary to handle reserved signals.
            // Note, {rt_,}sigreturn is already allowed for emulators to handle SIGALRM.
            for sysname in ["capget", "capset", "sigaction", "rt_sigaction"] {
                match ScmpSyscall::from_name(sysname) {
                    Ok(syscall) => {
                        ctx.add_rule(ScmpAction::Allow, syscall)?;
                    }
                    Err(_) => {
                        info!("ctx": "confine", "op": "allow_emu_syscall",
                            "msg": format!("invalid or unsupported syscall {sysname}"));
                    }
                }
            }
        }

        Ok(ctx)
    }
}

/// Type that exists to manage worker exit on panic.
///
/// This type is constructed once per `Worker` and implements `Drop` to
/// handle proper worker exit in case the worker panics when executing
/// the current task or anywhere else in its work loop. If the
/// `Sentinel` is dropped at the end of the worker's work loop and the
/// current thread is panicking, handle worker exit the same way as if
/// the task completed normally (if the worker panicked while executing
/// a submitted task) then clone the worker and start it with an initial
/// task of `None`.
struct Sentinel<'a> {
    seccomp_id: Option<u64>,
    worker_ref: &'a Worker,
}

impl<'a> Sentinel<'a> {
    fn new(worker_ref: &'a Worker) -> Sentinel<'a> {
        Self {
            seccomp_id: None,
            worker_ref,
        }
    }

    #[expect(clippy::arithmetic_side_effects)]
    fn deny_syscall(&self, seccomp_id: u64, errno: Errno) {
        let response = seccomp_notif_resp {
            id: seccomp_id,
            val: 0,
            error: -(errno as i32),
            flags: 0,
        };

        // EAGAIN|EINTR is retried.
        // ENOENT means child died mid-way.
        // Nothing else we can do on errors here.
        let _ = seccomp_notify_respond(self.worker_ref.fd, std::ptr::addr_of!(response));
    }
}

impl Drop for Sentinel<'_> {
    fn drop(&mut self) {
        if thread::panicking() {
            if let Some(seccomp_id) = self.seccomp_id {
                // Busy thread panicked.
                // SAFETY: Deny syscall in progress!
                self.deny_syscall(seccomp_id, Errno::EACCES);
                self.worker_ref.worker_data.decrement_both();
            } else {
                // Idle thread panicked.
                self.worker_ref.worker_data.decrement_worker_total();
            }
        }
    }
}
