//
// Syd: rock-solid application kernel
// src/hook.rs: Secure computing hooks
//
// Copyright (c) 2023, 2024, 2025, 2026 Ali Polatel <alip@chesswob.org>
// Based in part upon greenhook which is under public domain.
// MDWE code is based in part upon systemd which is LGPL-2.1-or-later.
// Personality code is based on pacwrap which is GPL-3.0-only.
//
// SPDX-License-Identifier: GPL-3.0

use std::{
    borrow::Cow,
    env,
    ffi::{OsStr, OsString},
    fs::OpenOptions,
    io::Write,
    os::{
        fd::{AsRawFd, OwnedFd, RawFd},
        unix::{ffi::OsStrExt, fs::OpenOptionsExt},
    },
    str::FromStr,
    sync::{
        atomic::{AtomicBool, Ordering},
        Arc, Condvar, Mutex, RwLock,
    },
};

use btoi::btoi;
use libseccomp::{
    scmp_cmp, ScmpAction, ScmpArch, ScmpArgCompare, ScmpCompareOp, ScmpFilterContext,
    ScmpNotifResp, ScmpSyscall, ScmpVersion,
};
use libseccomp_sys::__NR_SCMP_ERROR;
use memchr::{
    arch::all::{is_equal, is_suffix},
    memmem,
};
use nix::{
    errno::Errno,
    fcntl::OFlag,
    sched::{unshare, CloneFlags},
    sys::{
        resource::{getrlimit, Resource},
        signal::Signal,
        stat::{umask, Mode},
    },
    unistd::{pipe2, read, Gid, Pid, Uid},
};

use crate::{
    caps,
    caps::{
        errors::CapsError,
        securebits::{get_securebits, set_securebits, SecureBits},
    },
    compat::{
        get_pdeathsig, set_child_subreaper, set_dumpable, set_name, set_no_new_privs, set_pdeathsig,
    },
    config::*,
    confine::{
        confine_executable_maps, confine_mdwe, confine_rlimit, confine_scmp_clone3,
        confine_scmp_msg_oob, confine_scmp_setsockopt, nsflag_name, scmp_add_mknod, scmp_arch_raw,
        seccomp_add_architectures, ExportMode, SydArch, SydPersona, Sydcall, NAMESPACE_FLAGS,
        NAMESPACE_FLAGS_ALL, NAMESPACE_NAMES, SCMP_ARCH,
    },
    err::{SydJoinHandle, SydResult},
    error,
    fd::{close, closeexcept, AT_EXECVE_CHECK},
    fs::{seccomp_export_pfc, seccomp_notify_set_flags, SECCOMP_USER_NOTIF_FD_SYNC_WAKE_UP},
    hash::{key_ring_validate, SydHashMap, SydHashSet},
    info,
    log::{log_init_main, log_set_panic_hook},
    log_enabled,
    lookup::FileType,
    namespace::ns_setup_mnt,
    path::XPathBuf,
    pool,
    proc::proc_fs_nr_open,
    req::UNotifyEventRequest,
    retry::retry_on_eintr,
    rwrite, rwriteln,
    sandbox::{Action, Capability, Flags, LockState, NetlinkFamily, Sandbox, SandboxGuard},
    sealbox::SealableSydHashMap,
    set_cpu_priority_idle, set_io_priority_idle,
    spec::{
        speculation_get, speculation_set, SpeculationControlStatus, SpeculationFeature,
        SpeculationStatus, PR_SPEC_FORCE_DISABLE,
    },
    syslog::LogLevel,
    workers::{aes::AesLock, gdb::Tracer, ipc::IpcWorker, WorkerCache},
};

const FD_MAX: u64 = i32::MAX as u64;

pub(crate) type Handler = Arc<Box<dyn Fn(UNotifyEventRequest) -> ScmpNotifResp + Send + Sync>>;
pub(crate) type HandlerMap = SealableSydHashMap<Sydcall, Handler>;
type AllowSet = SydHashSet<ScmpSyscall>;

/// Supervisor of a Syd sandbox.
pub struct Supervisor {
    handlers: Arc<HandlerMap>,
    sysallow: AllowSet,
    sandbox: Arc<RwLock<Sandbox>>,
    crypt_map: Option<AesLock>,
    pty_child: Option<OwnedFd>,
    ipc_worker: Option<IpcWorker>,
}

impl Supervisor {
    /// Create a new `Supervisor` object. You can specify the number of threads in the thread pool.
    /// This function will also check your kernel version and show warning or return error if necessary.
    pub(crate) fn new(
        sandbox: Sandbox,
        pty_child: Option<OwnedFd>,
        mut ipc_worker: Option<IpcWorker>,
    ) -> SydResult<Self> {
        let mut handlers = SealableSydHashMap::default();
        let mut sysallow = SydHashSet::default();
        Self::init(&sandbox, &mut handlers, &mut sysallow)?;

        // SAFETY: Seal handlers map to protect function pointers.
        handlers = handlers.seal(Some(c" Syd: syscall/hooks"))?;

        let crypt_map = if sandbox.enabled(Capability::CAP_CRYPT) {
            Some(Arc::new((
                Mutex::new(SydHashMap::default()),
                Condvar::new(),
            )))
        } else {
            None
        };

        let sandbox = Arc::new(RwLock::new(sandbox));
        if let Some(ref mut ipc_worker) = ipc_worker {
            ipc_worker.set_sandbox(Arc::clone(&sandbox));
        }

        let supervisor = Supervisor {
            pty_child,
            ipc_worker,
            sandbox,
            sysallow,
            handlers: Arc::new(handlers),
            crypt_map: crypt_map.as_ref().map(Arc::clone),
        };

        Ok(supervisor)
    }

    /// Initializes the supervisor by adding the system call handlers.
    #[expect(clippy::cognitive_complexity)]
    fn init(
        sandbox: &Sandbox,
        handlers: &mut HandlerMap,
        sysallow: &mut AllowSet,
    ) -> SydResult<()> {
        // For performance reasons, we apply Memory/Stat/Chdir/Ioctl
        // sandboxing at startup only. Disabling it after startup is
        // still possible though when the respective syscalls will not
        // do any access checks.
        //
        // If lock is None, it'll be set to Some(LockState::Set),
        // when the sandbox child starts executing.
        let is_lock_read = matches!(sandbox.lock, None | Some(LockState::Set));
        let is_lock = sandbox.locked_pre();
        let has_crwx = sandbox.enabled_any(
            Capability::CAP_CREATE
                | Capability::CAP_READ
                | Capability::CAP_WRITE
                | Capability::CAP_EXEC,
        );
        let has_stat = sandbox.enabled(Capability::CAP_STAT);
        let has_exec = sandbox
            .enabled_any(Capability::CAP_EXEC | Capability::CAP_FORCE | Capability::CAP_TPE)
            || !sandbox.flags.allow_unsafe_exec_ldso()
            || !sandbox.flags.allow_unsafe_exec_memory()
            || !sandbox.flags.allow_unsafe_exec_nopie()
            || !sandbox.flags.allow_unsafe_exec_stack()
            || sandbox.flags.deny_exec_script()
            || sandbox.flags.deny_exec_elf32()
            || sandbox.flags.deny_exec_elf_dynamic()
            || sandbox.flags.deny_exec_elf_static();
        let has_chdir = sandbox.enabled(Capability::CAP_CHDIR);
        let has_creat = sandbox.enabled_any(
            Capability::CAP_READ
                | Capability::CAP_WRITE
                | Capability::CAP_CREATE
                | Capability::CAP_TRUNCATE,
        );
        let has_open = sandbox.enabled_any(
            Capability::CAP_READ
                | Capability::CAP_READDIR
                | Capability::CAP_WRITE
                | Capability::CAP_CREATE
                | Capability::CAP_TRUNCATE
                | Capability::CAP_MKTEMP,
        ) || !sandbox.flags.allow_unsafe_magiclinks();
        let has_ioctl = sandbox.enabled(Capability::CAP_IOCTL);
        let has_rename = sandbox.enabled(Capability::CAP_RENAME);
        let has_symlink = sandbox.enabled(Capability::CAP_SYMLINK);
        let has_truncate = sandbox.enabled(Capability::CAP_TRUNCATE);
        let has_delete = sandbox.enabled(Capability::CAP_DELETE);
        let has_rmdir = sandbox.enabled(Capability::CAP_RMDIR);
        let has_mkdir = sandbox.enabled(Capability::CAP_MKDIR);
        let has_mknod = sandbox.enabled_any(
            Capability::CAP_CREATE
                | Capability::CAP_MKDIR
                | Capability::CAP_MKBDEV
                | Capability::CAP_MKCDEV
                | Capability::CAP_MKFIFO,
        );
        let has_chown = sandbox.enabled_any(Capability::CAP_CHOWN | Capability::CAP_CHGRP);
        let has_chmod = sandbox.enabled(Capability::CAP_CHMOD);
        let has_chattr = sandbox.enabled(Capability::CAP_CHATTR);
        let has_utime = sandbox.enabled(Capability::CAP_UTIME);
        let has_net = sandbox.enabled_any(Capability::CAP_NET);
        let has_socket = has_net
            || sandbox.flags.allow_safe_kcapi()
            || !sandbox.flags.allow_unsupp_socket()
            || !sandbox.flags.allow_unsafe_socket()
            || !sandbox.netlink_families.is_empty();
        let has_net_bind = sandbox.enabled(Capability::CAP_NET_BIND);
        let has_net_connect = sandbox.enabled(Capability::CAP_NET_CONNECT);
        let has_net_sendfd = sandbox.enabled(Capability::CAP_NET_SENDFD);
        let has_mem = sandbox.enabled(Capability::CAP_MEM);
        let has_block = sandbox.has_block();
        let has_safe_setuid = sandbox.has_uid_transit();
        let has_safe_setgid = sandbox.has_gid_transit();
        let has_scoped_signals = *HAVE_LANDLOCK_SCOPED_SIGNALS;
        let restrict_chroot = !sandbox.flags.allow_unsafe_chroot();
        let restrict_memfd = !sandbox.flags.allow_unsafe_memfd();
        let restrict_msgqueue = !sandbox.flags.allow_unsafe_msgqueue();
        let restrict_perm_msgqueue = !sandbox.flags.allow_unsafe_perm_msgqueue();
        let restrict_notify_bdev = !sandbox.flags.allow_unsafe_notify_bdev();
        let restrict_notify_cdev = !sandbox.flags.allow_unsafe_notify_cdev();
        let restrict_notify = restrict_notify_bdev || restrict_notify_cdev;
        let restrict_prctl = !sandbox.flags.allow_unsafe_prctl();
        let restrict_ptrace = !sandbox.flags.allow_unsafe_ptrace();
        let restrict_shm = !sandbox.flags.allow_unsafe_shm();
        let restrict_perm_shm = !sandbox.flags.allow_unsafe_perm_shm();
        let restrict_cap_fixup = !sandbox.flags.allow_unsafe_cap_fixup();
        let restrict_exec_script = !sandbox.flags.allow_unsafe_exec_script();
        let restrict_exec_interactive = !sandbox.flags.allow_unsafe_exec_interactive();
        let restrict_exec_speculative = !sandbox.flags.allow_unsafe_exec_speculative();
        let restrict_sysinfo = !sandbox.flags.allow_unsafe_sysinfo();
        let restrict_uname = !sandbox.flags.allow_unsafe_uname();
        let restrict_xattr = !sandbox.flags.allow_unsafe_xattr();
        let skip_append = sandbox.skip_append();
        let want_caps = sandbox.flags.want_caps();
        let flags = *sandbox.flags;

        let mut allow_calls = Vec::new();

        // PR_SET_NAME logging.
        if restrict_prctl {
            Self::insert_handler(handlers, "prctl", crate::kernel::prctl::sys_prctl);
        }

        // ptrace(2) detection prevention
        Self::insert_handler(handlers, "ptrace", crate::kernel::sys_ptrace::sys_ptrace);

        // F_SETFL O_APPEND unset prevention for appendonly files.
        //
        // Note, pwritev2(2) flag RWF_NOAPPEND is denied as part
        // of a separate filter because of its arch-specific nature.
        if !skip_append {
            Self::insert_handler(handlers, "fcntl", crate::kernel::fcntl::sys_fcntl);
            Self::insert_handler(handlers, "fcntl64", crate::kernel::fcntl::sys_fcntl);
        } else {
            allow_calls.extend(["fcntl", "fcntl64", "pwritev2"]);
        }

        if has_exec || !is_lock {
            if !restrict_ptrace {
                // Exec sandboxing, only used with trace/allow_unsafe_ptrace:1
                //
                // Because with seccomp there's no TOCTTOU-free way to
                // implement these system calls.
                //
                // See: https://bugzilla.kernel.org/show_bug.cgi?id=218501
                Self::insert_handler(handlers, "execve", crate::kernel::exec::sys_execve);
                Self::insert_handler(handlers, "execveat", crate::kernel::exec::sys_execveat);
            } // else: this is handled in setup_seccomp.
        } else {
            allow_calls.extend(["execve", "execveat"]);
        }

        // SA_RESTART tracking for syscall interruption.
        Self::insert_handler(
            handlers,
            "sigaction",
            crate::kernel::sigaction::sys_sigaction,
        );
        Self::insert_handler(
            handlers,
            "rt_sigaction",
            crate::kernel::sigaction::sys_sigaction,
        );

        // Ioctl sandboxing
        if has_ioctl || !skip_append {
            Self::insert_handler(handlers, "ioctl", crate::kernel::ioctl::sys_ioctl);
        } else {
            allow_calls.push("ioctl");
        }

        if has_mem {
            // Memory sandboxing
            Self::insert_handler(handlers, "brk", crate::kernel::mem::sys_brk);
            Self::insert_handler(handlers, "mremap", crate::kernel::mem::sys_mremap);
        } else {
            allow_calls.extend(["brk", "mremap"]);
        }

        // mmap{,2} are checked for Exec too!
        // We handle them specially in setup_seccomp,
        // as we only want to hook into PROT_EXEC and !MAP_ANONYMOUS.
        if has_mem || has_exec || !skip_append || !is_lock {
            Self::insert_handler(handlers, "mmap", crate::kernel::mem::sys_mmap);
            Self::insert_handler(handlers, "mmap2", crate::kernel::mem::sys_mmap2);
        } else {
            allow_calls.extend(["mmap", "mmap2"]);
        }

        // SafeSetID
        // SAFETY: We do not support diverging FsID from Effective ID.
        // SAFETY: We do not support setgroups (due to pointer deref -> TOCTOU vector)
        // The parent seccomp filter stops setfs*id and setgroups.
        // Parent filter also stops {U,G}ID to privileged user/groups.
        if has_safe_setuid {
            Self::insert_handler(handlers, "setuid", crate::kernel::setid::sys_setuid);
            Self::insert_handler(handlers, "setuid32", crate::kernel::setid::sys_setuid);
            Self::insert_handler(handlers, "setreuid", crate::kernel::setid::sys_setreuid);
            Self::insert_handler(handlers, "setreuid32", crate::kernel::setid::sys_setreuid);
            Self::insert_handler(handlers, "setresuid", crate::kernel::setid::sys_setresuid);
            Self::insert_handler(handlers, "setresuid32", crate::kernel::setid::sys_setresuid);
        } else {
            allow_calls.extend([
                "setuid",
                "setuid32",
                "setreuid",
                "setreuid32",
                "setresuid",
                "setresuid32",
            ]);
        }
        if has_safe_setgid {
            Self::insert_handler(handlers, "setgid", crate::kernel::setid::sys_setgid);
            Self::insert_handler(handlers, "setgid32", crate::kernel::setid::sys_setgid);
            Self::insert_handler(handlers, "setregid", crate::kernel::setid::sys_setregid);
            Self::insert_handler(handlers, "setregid32", crate::kernel::setid::sys_setregid);
            Self::insert_handler(handlers, "setresgid", crate::kernel::setid::sys_setresgid);
            Self::insert_handler(handlers, "setresgid32", crate::kernel::setid::sys_setresgid);
        } else {
            allow_calls.extend([
                "setgid",
                "setgid32",
                "setregid",
                "setregid32",
                "setresgid",
                "setresgid32",
            ]);
        }

        // SAFETY: sysinfo() is a vector of information leak as it
        // provides identical information with the files /proc/meminfo
        // and /proc/loadavg.
        // Since 3.32.4, this can be relaxed with trace/allow_unsafe_sysinfo:1.
        if restrict_sysinfo {
            Self::insert_handler(handlers, "sysinfo", crate::kernel::sysinfo::sys_sysinfo);
        } else {
            allow_calls.push("sysinfo");
        }

        // SAFETY: syslog(2) provides the syslog interface in case
        // the sandbox process has access to the sandbox lock.
        #[cfg(feature = "log")]
        Self::insert_handler(handlers, "syslog", crate::kernel::syslog::sys_syslog);

        // Sanitize uname(2) to protect against information leaks.
        // This is consistent with masking /proc/version.
        // Since 3.44.2, this can be relaxed with trace/allow_unsafe_uname:1.
        if restrict_uname {
            Self::insert_handler(handlers, "uname", crate::kernel::uname::sys_uname);
        } else {
            allow_calls.push("uname");
        }

        // Signal protection: use seccomp(2) if landlock(7) scoped signals are not available.
        if has_scoped_signals {
            allow_calls.extend(KILL_SYSCALLS);
        } else {
            Self::insert_handler(handlers, "kill", crate::kernel::signal::sys_kill);
            Self::insert_handler(handlers, "tkill", crate::kernel::signal::sys_tkill);
            Self::insert_handler(handlers, "tgkill", crate::kernel::signal::sys_tgkill);
            Self::insert_handler(
                handlers,
                "rt_sigqueueinfo",
                crate::kernel::signal::sys_rt_sigqueueinfo,
            );
            Self::insert_handler(
                handlers,
                "rt_tgsigqueueinfo",
                crate::kernel::signal::sys_rt_tgsigqueueinfo,
            );
            Self::insert_handler(
                handlers,
                "pidfd_open",
                crate::kernel::signal::sys_pidfd_open,
            );
        }

        // network sandboxing
        if has_socket || !is_lock {
            Self::insert_handler(handlers, "socketcall", crate::kernel::net::sys_socketcall);
            Self::insert_handler(handlers, "socket", crate::kernel::net::sys_socket);
            Self::insert_handler(handlers, "socketpair", crate::kernel::net::sys_socketpair);
        } else {
            allow_calls.extend(["socketcall", "socket", "socketpair"]);
        }
        if has_net || !is_lock {
            Self::insert_handler(handlers, "recvfrom", crate::kernel::net::sys_recvfrom);
            Self::insert_handler(handlers, "recvmsg", crate::kernel::net::sys_recvmsg);
            Self::insert_handler(handlers, "recvmmsg", crate::kernel::net::sys_recvmmsg);
            Self::insert_handler(
                handlers,
                "recvmmsg_time64",
                crate::kernel::net::sys_recvmmsg64,
            );
            Self::insert_handler(handlers, "getpeername", crate::kernel::net::sys_getpeername);
            Self::insert_handler(handlers, "getsockname", crate::kernel::net::sys_getsockname);
            Self::insert_handler(handlers, "getsockopt", crate::kernel::net::sys_getsockopt);
        } else {
            allow_calls.extend([
                "recvfrom",
                "recvmsg",
                "recvmmsg",
                "recvmmsg_time64",
                "getpeername",
                "getsockname",
                "getsockopt",
            ]);
        }

        if has_net_bind || !is_lock {
            Self::insert_handler(handlers, "bind", crate::kernel::net::sys_bind);
        } else {
            allow_calls.push("bind");
        }

        if has_net_connect || !is_lock {
            Self::insert_handler(handlers, "connect", crate::kernel::net::sys_connect);
            Self::insert_handler(handlers, "sendto", crate::kernel::net::sys_sendto);
        } else {
            allow_calls.extend(["connect", "sendto"]);
        }

        if has_net_connect || has_net_sendfd || !is_lock {
            Self::insert_handler(handlers, "sendmsg", crate::kernel::net::sys_sendmsg);
            Self::insert_handler(handlers, "sendmmsg", crate::kernel::net::sys_sendmmsg);
        } else {
            allow_calls.extend(["sendmsg", "sendmmsg"]);
        }

        if has_block || !is_lock {
            Self::insert_handler(handlers, "accept", crate::kernel::net::sys_accept);
            Self::insert_handler(handlers, "accept4", crate::kernel::net::sys_accept4);
        } else {
            allow_calls.extend(["accept", "accept4"]);
        }

        // chroot sandboxing
        if restrict_chroot {
            Self::insert_handler(handlers, "chroot", crate::kernel::chroot::sys_chroot);
        } // else trace/allow_unsafe_chroot:1

        // chdir sandboxing
        if has_chdir {
            if !restrict_ptrace {
                Self::insert_handler(handlers, "chdir", crate::kernel::chdir::sys_chdir);
                Self::insert_handler(handlers, "fchdir", crate::kernel::chdir::sys_fchdir);
            }
        } else {
            allow_calls.extend(["chdir", "fchdir"]);
        }

        // stat sandboxing
        if has_stat || !is_lock {
            Self::insert_handler(handlers, "stat", crate::kernel::stat::sys_stat);
            Self::insert_handler(handlers, "stat64", crate::kernel::stat::sys_stat64);
            Self::insert_handler(handlers, "statx", crate::kernel::stat::sys_statx);
            Self::insert_handler(handlers, "lstat", crate::kernel::stat::sys_lstat);
            Self::insert_handler(handlers, "lstat64", crate::kernel::stat::sys_lstat64);
            Self::insert_handler(handlers, "fstatat64", crate::kernel::stat::sys_newfstatat);
            Self::insert_handler(handlers, "newfstatat", crate::kernel::stat::sys_newfstatat);
        } else {
            allow_calls.extend([
                "stat",
                "stat64",
                "statx",
                "lstat",
                "lstat64",
                "fstatat64",
                "newfstatat",
            ]);
        }

        if has_stat || restrict_notify || !is_lock {
            Self::insert_handler(
                handlers,
                "fanotify_mark",
                crate::kernel::fanotify::sys_fanotify_mark,
            );
            Self::insert_handler(
                handlers,
                "inotify_add_watch",
                crate::kernel::inotify::sys_inotify_add_watch,
            );
        } else {
            allow_calls.extend(["fanotify_mark", "inotify_add_watch"]);
        }

        if has_stat || restrict_xattr || !is_lock {
            Self::insert_handler(handlers, "getxattr", crate::kernel::xattr::sys_getxattr);
            Self::insert_handler(handlers, "fgetxattr", crate::kernel::xattr::sys_fgetxattr);
            Self::insert_handler(handlers, "lgetxattr", crate::kernel::xattr::sys_lgetxattr);
            Self::insert_handler(handlers, "getxattrat", crate::kernel::xattr::sys_getxattrat);
            Self::insert_handler(handlers, "listxattr", crate::kernel::xattr::sys_listxattr);
            Self::insert_handler(handlers, "flistxattr", crate::kernel::xattr::sys_flistxattr);
            Self::insert_handler(handlers, "llistxattr", crate::kernel::xattr::sys_llistxattr);
            Self::insert_handler(
                handlers,
                "listxattrat",
                crate::kernel::xattr::sys_listxattrat,
            );
        } else {
            allow_calls.extend([
                "getxattr",
                "fgetxattr",
                "lgetxattr",
                "getxattrat",
                "listxattr",
                "flistxattr",
                "llistxattr",
                "listxattrat",
            ]);
        }

        if has_stat {
            Self::insert_handler(handlers, "access", crate::kernel::access::sys_access);
            Self::insert_handler(handlers, "faccessat", crate::kernel::access::sys_faccessat);
            Self::insert_handler(
                handlers,
                "faccessat2",
                crate::kernel::access::sys_faccessat2,
            );
            Self::insert_handler(
                handlers,
                "getdents64",
                crate::kernel::getdents::sys_getdents64,
            );
            Self::insert_handler(handlers, "readlink", crate::kernel::readlink::sys_readlink);
            Self::insert_handler(
                handlers,
                "readlinkat",
                crate::kernel::readlink::sys_readlinkat,
            );
            Self::insert_handler(handlers, "fstat", crate::kernel::stat::sys_fstat);
            Self::insert_handler(handlers, "fstat64", crate::kernel::stat::sys_fstat64);
            Self::insert_handler(handlers, "statfs", crate::kernel::statfs::sys_statfs);
            Self::insert_handler(handlers, "statfs64", crate::kernel::statfs::sys_statfs64);
            Self::insert_handler(handlers, "fstatfs", crate::kernel::statfs::sys_fstatfs);
            Self::insert_handler(handlers, "fstatfs64", crate::kernel::statfs::sys_fstatfs64);
        } else {
            allow_calls.extend([
                "access",
                "faccessat",
                "faccessat2",
                "getdents64",
                "readlink",
                "readlinkat",
                "fstat",
                "fstat64",
                "statfs",
                "statfs64",
                "fstatfs",
                "fstatfs64",
            ]);
        }

        // open(2) family: read/readdir/write/create/truncate/mktemp sandboxing
        if has_creat || !skip_append || !is_lock {
            Self::insert_handler(handlers, "creat", crate::kernel::open::sys_creat);
        } else {
            allow_calls.push("creat");
        }
        if has_open || !skip_append || !is_lock_read {
            Self::insert_handler(handlers, "open", crate::kernel::open::sys_open);
            Self::insert_handler(handlers, "openat", crate::kernel::open::sys_openat);
            Self::insert_handler(handlers, "openat2", crate::kernel::open::sys_openat2);
        } else {
            allow_calls.extend(["open", "openat", "openat2"]);
        }

        // rename sandboxing
        if has_rename || !skip_append || !is_lock {
            Self::insert_handler(handlers, "link", crate::kernel::link::sys_link);
            Self::insert_handler(handlers, "linkat", crate::kernel::link::sys_linkat);
            Self::insert_handler(handlers, "rename", crate::kernel::rename::sys_rename);
            Self::insert_handler(handlers, "renameat", crate::kernel::rename::sys_renameat);
            Self::insert_handler(handlers, "renameat2", crate::kernel::rename::sys_renameat2);
        } else {
            allow_calls.extend(["link", "linkat", "rename", "renameat", "renameat2"]);
        }

        // symlink sandboxing
        if has_symlink || !is_lock {
            Self::insert_handler(handlers, "symlink", crate::kernel::symlink::sys_symlink);
            Self::insert_handler(handlers, "symlinkat", crate::kernel::symlink::sys_symlinkat);
        } else {
            allow_calls.extend(["symlink", "symlinkat"]);
        }

        // delete/rmdir sandboxing
        if has_rmdir || !is_lock {
            Self::insert_handler(handlers, "rmdir", crate::kernel::unlink::sys_rmdir);
        } else {
            allow_calls.push("rmdir");
        }
        if has_delete || !skip_append || !is_lock {
            Self::insert_handler(handlers, "unlink", crate::kernel::unlink::sys_unlink);
        } else {
            allow_calls.push("unlink");
        }
        if has_delete || has_rmdir || !skip_append || !is_lock {
            Self::insert_handler(handlers, "unlinkat", crate::kernel::unlink::sys_unlinkat);
        } else {
            allow_calls.push("unlinkat");
        }

        // create/mkdir/mkbdev/mkcdev/mkfifo sandboxing
        if has_mkdir || !is_lock {
            Self::insert_handler(handlers, "mkdir", crate::kernel::mkdir::sys_mkdir);
            Self::insert_handler(handlers, "mkdirat", crate::kernel::mkdir::sys_mkdirat);
        } else {
            allow_calls.extend(["mkdir", "mkdirat"]);
        }
        if has_mknod || !is_lock {
            Self::insert_handler(handlers, "mknod", crate::kernel::mknod::sys_mknod);
            Self::insert_handler(handlers, "mknodat", crate::kernel::mknod::sys_mknodat);
        } else {
            allow_calls.extend(["mknod", "mknodat"]);
        }

        // chown/chgrp sandboxing
        if has_chown || !is_lock {
            Self::insert_handler(handlers, "chown", crate::kernel::chown::sys_chown);
            Self::insert_handler(handlers, "chown32", crate::kernel::chown::sys_chown);
            Self::insert_handler(handlers, "fchown", crate::kernel::chown::sys_fchown);
            Self::insert_handler(handlers, "fchown32", crate::kernel::chown::sys_fchown);
            Self::insert_handler(handlers, "lchown", crate::kernel::chown::sys_lchown);
            Self::insert_handler(handlers, "lchown32", crate::kernel::chown::sys_lchown);
            Self::insert_handler(handlers, "fchownat", crate::kernel::chown::sys_fchownat);
        } else {
            allow_calls.extend([
                "chown", "chown32", "fchown", "fchown32", "lchown", "lchown32", "fchownat",
            ]);
        }

        // chmod sandboxing
        if has_chmod || !is_lock {
            Self::insert_handler(handlers, "chmod", crate::kernel::chmod::sys_chmod);
            Self::insert_handler(handlers, "fchmod", crate::kernel::chmod::sys_fchmod);
            Self::insert_handler(handlers, "fchmodat", crate::kernel::chmod::sys_fchmodat);
            Self::insert_handler(handlers, "fchmodat2", crate::kernel::chmod::sys_fchmodat2);
        } else {
            allow_calls.extend(["chmod", "fchmod", "fchmodat", "fchmodat2"]);
        }

        // chattr sandboxing
        if has_chattr || restrict_xattr || !is_lock {
            Self::insert_handler(handlers, "setxattr", crate::kernel::xattr::sys_setxattr);
            Self::insert_handler(handlers, "fsetxattr", crate::kernel::xattr::sys_fsetxattr);
            Self::insert_handler(handlers, "lsetxattr", crate::kernel::xattr::sys_lsetxattr);
            Self::insert_handler(handlers, "setxattrat", crate::kernel::xattr::sys_setxattrat);
            Self::insert_handler(
                handlers,
                "removexattr",
                crate::kernel::xattr::sys_removexattr,
            );
            Self::insert_handler(
                handlers,
                "removexattrat",
                crate::kernel::xattr::sys_removexattrat,
            );
            Self::insert_handler(
                handlers,
                "fremovexattr",
                crate::kernel::xattr::sys_fremovexattr,
            );
            Self::insert_handler(
                handlers,
                "lremovexattr",
                crate::kernel::xattr::sys_lremovexattr,
            );
        } else {
            allow_calls.extend([
                "setxattr",
                "fsetxattr",
                "lsetxattr",
                "setxattrat",
                "removexattr",
                "removexattrat",
                "fremovexattr",
                "lremovexattr",
            ]);
        }

        // utime sandboxing
        if has_utime || !is_lock {
            Self::insert_handler(handlers, "utime", crate::kernel::utime::sys_utime);
            Self::insert_handler(handlers, "utimes", crate::kernel::utime::sys_utimes);
            Self::insert_handler(handlers, "futimesat", crate::kernel::utime::sys_futimesat);
            Self::insert_handler(handlers, "utimensat", crate::kernel::utime::sys_utimensat);
            Self::insert_handler(
                handlers,
                "utimensat_time64",
                crate::kernel::utime::sys_utimensat64,
            );
        } else {
            allow_calls.extend([
                "utime",
                "utimes",
                "futimesat",
                "utimensat",
                "utimensat_time64",
            ]);
        }

        // truncate sandboxing
        if has_truncate || !skip_append || !is_lock {
            Self::insert_handler(handlers, "truncate", crate::kernel::truncate::sys_truncate);
            Self::insert_handler(
                handlers,
                "truncate64",
                crate::kernel::truncate::sys_truncate64,
            );
            Self::insert_handler(
                handlers,
                "ftruncate",
                crate::kernel::truncate::sys_ftruncate,
            );
            Self::insert_handler(
                handlers,
                "ftruncate64",
                crate::kernel::truncate::sys_ftruncate64,
            );
            Self::insert_handler(
                handlers,
                "fallocate",
                crate::kernel::truncate::sys_fallocate,
            );
        } else {
            allow_calls.extend([
                "truncate",
                "truncate64",
                "ftruncate",
                "ftruncate64",
                "fallocate",
            ]);
        }

        // memfds have mode 777 by default, so
        // we check it for all of Create, Read, Write and Exec sandboxing.
        if has_crwx || !is_lock {
            Self::insert_handler(
                handlers,
                "memfd_create",
                crate::kernel::memfd::sys_memfd_create,
            );
        } else {
            allow_calls.push("memfd_create");
        }
        if !restrict_memfd {
            allow_calls.push("memfd_secret");
        }

        // Prevent unsafe shared memory permissions.
        // We need NOTIFY hook due to ipc(2) multiplexer.
        if !restrict_shm && restrict_perm_shm {
            const SHM_NOTIFY: &[&str] = &[
                "ipc", "msgctl", "msgget", "semctl", "semget", "shmat", "shmctl", "shmget",
            ]; // sorted
            Self::insert_handler(handlers, "ipc", crate::kernel::shm::sys_ipc);
            Self::insert_handler(handlers, "shmat", crate::kernel::shm::sys_shmat);
            Self::insert_handler(handlers, "msgctl", crate::kernel::shm::sys_msgctl);
            Self::insert_handler(handlers, "semctl", crate::kernel::shm::sys_semctl);
            Self::insert_handler(handlers, "shmctl", crate::kernel::shm::sys_shmctl);
            Self::insert_handler(handlers, "msgget", crate::kernel::shm::sys_msgget);
            Self::insert_handler(handlers, "semget", crate::kernel::shm::sys_semget);
            Self::insert_handler(handlers, "shmget", crate::kernel::shm::sys_shmget);
            allow_calls.extend(
                SHM_SYSCALLS
                    .iter()
                    .filter(|&&syscall| SHM_NOTIFY.binary_search(&syscall).is_err()),
            );
        } else if !restrict_shm {
            allow_calls.extend(SHM_SYSCALLS);
        } // else -> default: KillProcess

        // For consistency with SHM calls,
        // we also handle mq_open(2) in a NOTIFY hook.
        if !restrict_msgqueue && restrict_perm_msgqueue {
            Self::insert_handler(handlers, "mq_open", crate::kernel::shm::sys_mq_open);
            allow_calls.extend(
                MSGQUEUE_SYSCALLS
                    .iter()
                    .filter(|&&sysname| sysname != "mq_open"),
            );
        } else if !restrict_msgqueue {
            allow_calls.extend(MSGQUEUE_SYSCALLS);
        } // else -> default: KillProcess

        if log_enabled!(LogLevel::Info) {
            allow_calls.sort_unstable();
            info!("ctx": "confine", "op": "allow_syscall",
                "msg": format!("allowed {} system calls based on sandbox policy",
                    allow_calls.len()),
                "sys": &allow_calls);
        }

        // Allowlist safe system calls.
        for sysname in SAFE_SYSCALLS
            .iter()
            .chain(&allow_calls)
            .chain(FUTEX_SYSCALLS)
            .chain(VDSO_SYSCALLS)
        // SAFETY: get id syscalls are handled by `root/fake' as necessary.
        // .chain(GETID_SYSCALLS)
        {
            match ScmpSyscall::from_name(sysname) {
                Ok(syscall) => Self::allow_syscall(sysallow, syscall),
                Err(_) => {
                    info!("ctx": "confine", "op": "allow_syscall",
                        "msg": format!("invalid or unsupported syscall {sysname}"));
                }
            }
        }

        // SAFETY: Lock process secure bits.
        match get_securebits() {
            Ok(current_mask) => {
                // Attempt 1: Request the comprehensive policy (all bases + all locks),
                // while not attempting to flip a base bit that is already locked
                // in the opposite state.
                let mut requested_full_policy =
                    SecureBits::SECBIT_ALL_BASE_PRIV | SecureBits::SECBIT_ALL_LOCK_PRIV;
                if want_caps {
                    requested_full_policy
                        .remove(SecureBits::SECBIT_NOROOT | SecureBits::SECBIT_NOROOT_LOCKED);
                }
                if restrict_cap_fixup {
                    requested_full_policy.remove(SecureBits::SECBIT_KEEP_CAPS);
                } else {
                    requested_full_policy
                        .remove(SecureBits::SECBIT_KEEP_CAPS | SecureBits::SECBIT_KEEP_CAPS_LOCKED);
                    requested_full_policy.remove(
                        SecureBits::SECBIT_NO_SETUID_FIXUP
                            | SecureBits::SECBIT_NO_SETUID_FIXUP_LOCKED,
                    );
                }
                if restrict_exec_script && *HAVE_AT_EXECVE_CHECK {
                    requested_full_policy |= SecureBits::SECBIT_EXEC_RESTRICT_FILE
                        | SecureBits::SECBIT_EXEC_RESTRICT_FILE_LOCKED;
                }
                if restrict_exec_interactive && *HAVE_AT_EXECVE_CHECK {
                    requested_full_policy |= SecureBits::SECBIT_EXEC_DENY_INTERACTIVE
                        | SecureBits::SECBIT_EXEC_DENY_INTERACTIVE_LOCKED;
                }

                if current_mask.contains(SecureBits::SECBIT_NOROOT_LOCKED)
                    && !current_mask.contains(SecureBits::SECBIT_NOROOT)
                {
                    requested_full_policy.remove(SecureBits::SECBIT_NOROOT);
                }
                if current_mask.contains(SecureBits::SECBIT_NO_SETUID_FIXUP_LOCKED)
                    && !current_mask.contains(SecureBits::SECBIT_NO_SETUID_FIXUP)
                {
                    requested_full_policy.remove(SecureBits::SECBIT_NO_SETUID_FIXUP);
                }
                if current_mask.contains(SecureBits::SECBIT_KEEP_CAPS_LOCKED)
                    && !current_mask.contains(SecureBits::SECBIT_KEEP_CAPS)
                {
                    requested_full_policy.remove(SecureBits::SECBIT_KEEP_CAPS);
                }
                if current_mask.contains(SecureBits::SECBIT_NO_CAP_AMBIENT_RAISE_LOCKED)
                    && !current_mask.contains(SecureBits::SECBIT_NO_CAP_AMBIENT_RAISE)
                {
                    requested_full_policy.remove(SecureBits::SECBIT_NO_CAP_AMBIENT_RAISE);
                }
                if current_mask.contains(SecureBits::SECBIT_EXEC_RESTRICT_FILE_LOCKED)
                    && !current_mask.contains(SecureBits::SECBIT_EXEC_RESTRICT_FILE)
                {
                    requested_full_policy.remove(SecureBits::SECBIT_EXEC_RESTRICT_FILE);
                }
                if current_mask.contains(SecureBits::SECBIT_EXEC_DENY_INTERACTIVE_LOCKED)
                    && !current_mask.contains(SecureBits::SECBIT_EXEC_DENY_INTERACTIVE)
                {
                    requested_full_policy.remove(SecureBits::SECBIT_EXEC_DENY_INTERACTIVE);
                }

                let candidate_full_policy = current_mask | requested_full_policy;
                let mut resulting_mask = current_mask;

                match set_securebits(candidate_full_policy) {
                    Ok(()) => resulting_mask = candidate_full_policy,
                    Err(CapsError(Errno::EPERM)) => {
                        // Lacking CAP_SETPCAP or preexisting locks blocked parts of the request:
                        // fall back to unprivileged exec policy governed by the user's toggles.
                        let mut requested_unprivileged_policy = SecureBits::empty();

                        if restrict_exec_script && *HAVE_AT_EXECVE_CHECK {
                            requested_unprivileged_policy |= SecureBits::SECBIT_EXEC_RESTRICT_FILE
                                | SecureBits::SECBIT_EXEC_RESTRICT_FILE_LOCKED;
                        }
                        if restrict_exec_interactive && *HAVE_AT_EXECVE_CHECK {
                            requested_unprivileged_policy |=
                                SecureBits::SECBIT_EXEC_DENY_INTERACTIVE
                                    | SecureBits::SECBIT_EXEC_DENY_INTERACTIVE_LOCKED;
                        }

                        // Respect existing locks here too:
                        // Don't try to flip locked-off bases.
                        if current_mask.contains(SecureBits::SECBIT_EXEC_RESTRICT_FILE_LOCKED)
                            && !current_mask.contains(SecureBits::SECBIT_EXEC_RESTRICT_FILE)
                        {
                            requested_unprivileged_policy
                                .remove(SecureBits::SECBIT_EXEC_RESTRICT_FILE);
                        }
                        if current_mask.contains(SecureBits::SECBIT_EXEC_DENY_INTERACTIVE_LOCKED)
                            && !current_mask.contains(SecureBits::SECBIT_EXEC_DENY_INTERACTIVE)
                        {
                            requested_unprivileged_policy
                                .remove(SecureBits::SECBIT_EXEC_DENY_INTERACTIVE);
                        }

                        if !requested_unprivileged_policy.is_empty() {
                            let candidate_unprivileged_policy =
                                current_mask | requested_unprivileged_policy;
                            match set_securebits(candidate_unprivileged_policy) {
                                Ok(()) => {
                                    resulting_mask = candidate_unprivileged_policy;
                                }
                                Err(CapsError(errno)) => {
                                    info!("ctx": "set_securebits",
                                      "msg": "failed to set unprivileged exec policy (fallback)",
                                      "errno": format!("{errno}"));
                                }
                            }
                        }
                    }

                    // Kernel too old for some requested bits (e.g., exec policy bits):
                    // Try classic privileged subset.
                    Err(CapsError(Errno::EINVAL)) => {
                        let mut requested_classic_policy =
                            SecureBits::SECBIT_ALL_BASE_PRIV | SecureBits::SECBIT_ALL_LOCK_PRIV;
                        if want_caps {
                            requested_classic_policy.remove(
                                SecureBits::SECBIT_NOROOT | SecureBits::SECBIT_NOROOT_LOCKED,
                            );
                        }
                        if restrict_cap_fixup {
                            requested_classic_policy.remove(SecureBits::SECBIT_KEEP_CAPS);
                        } else {
                            requested_classic_policy.remove(
                                SecureBits::SECBIT_KEEP_CAPS | SecureBits::SECBIT_KEEP_CAPS_LOCKED,
                            );
                            requested_classic_policy.remove(
                                SecureBits::SECBIT_NO_SETUID_FIXUP
                                    | SecureBits::SECBIT_NO_SETUID_FIXUP_LOCKED,
                            );
                        }

                        if current_mask.contains(SecureBits::SECBIT_NOROOT_LOCKED)
                            && !current_mask.contains(SecureBits::SECBIT_NOROOT)
                        {
                            requested_classic_policy.remove(SecureBits::SECBIT_NOROOT);
                        }
                        if current_mask.contains(SecureBits::SECBIT_NO_SETUID_FIXUP_LOCKED)
                            && !current_mask.contains(SecureBits::SECBIT_NO_SETUID_FIXUP)
                        {
                            requested_classic_policy.remove(SecureBits::SECBIT_NO_SETUID_FIXUP);
                        }
                        if current_mask.contains(SecureBits::SECBIT_KEEP_CAPS_LOCKED)
                            && !current_mask.contains(SecureBits::SECBIT_KEEP_CAPS)
                        {
                            requested_classic_policy.remove(SecureBits::SECBIT_KEEP_CAPS);
                        }
                        if current_mask.contains(SecureBits::SECBIT_NO_CAP_AMBIENT_RAISE_LOCKED)
                            && !current_mask.contains(SecureBits::SECBIT_NO_CAP_AMBIENT_RAISE)
                        {
                            requested_classic_policy
                                .remove(SecureBits::SECBIT_NO_CAP_AMBIENT_RAISE);
                        }

                        let candidate_classic_policy = current_mask | requested_classic_policy;

                        match set_securebits(candidate_classic_policy) {
                            Ok(()) => resulting_mask = candidate_classic_policy,
                            Err(CapsError(errno)) => {
                                info!("ctx": "set_securebits",
                                    "msg": format!("set securebits error: {errno}"));
                            }
                        }
                    }
                    Err(CapsError(errno)) => {
                        info!("ctx": "set_securebits",
                            "msg": format!("set securebits error: {errno}"));
                    }
                }
                info!("ctx": "set_securebits",
                    "msg": "process secure bits applied",
                    "mask_old": current_mask,
                    "mask_new": resulting_mask);
            }
            Err(errno) => {
                info!("ctx": "set_securebits",
                    "msg": format!("get securebits error: {errno}"));
            }
        }

        // SAFETY: Set the no-new-privileges attribute.
        set_no_new_privs()?;
        info!("ctx": "set_no_new_privileges", "msg": "no-new-privileges attribute set");

        // SAFETY: Set Memory-Deny-Write-Execute protection mask.
        // REFUSE_EXEC_GAIN is available since Linux-6.3.
        // NO_INHERIT is available since Linux-6.6.
        // For older systems we also apply a mmap filter in load_seccomp_parent.
        let restrict_exec_memory = !flags.allow_unsafe_exec_memory();
        let mdwe_no_inherit = if restrict_exec_memory {
            // Apply restriction globally.
            false
        } else {
            // Apply restrictions only for syd process.
            true
        };

        #[cfg(not(any(
            target_arch = "mips",
            target_arch = "mips32r6",
            target_arch = "mips64",
            target_arch = "mips64r6"
        )))]
        // Set memory-deny-write-execute attribute.
        // Note, mips requires executable stack so we skip on this arch.
        if let Err(errno) = confine_mdwe(mdwe_no_inherit) {
            // EINVAL: MDWE is not supported (Required Linux 6.3 or newer)
            // EPERM: Permission denied (MDWE already enabled?)
            info!("ctx": "set_memory_deny_write_execute",
                "msg": format!("memory-deny-write-execute error: {errno}"));
        } else {
            info!("ctx": "set_memory_deny_write_execute",
                "msg": format!("memory-deny-write-execute set with inherit:{restrict_exec_memory}"));
        }

        if restrict_exec_speculative {
            // SAFETY: Set mitigations for speculation misfeatures.
            let nstatus = SpeculationStatus::from_raw(PR_SPEC_FORCE_DISABLE);
            for spec_feat in [
                SpeculationFeature::StoreBypass,
                SpeculationFeature::IndirectBranch,
                SpeculationFeature::L1DFlush,
            ] {
                match speculation_get(spec_feat) {
                    Ok(cstatus) if cstatus.status.can_prctl_set() => {
                        // Fall-through and attempt to set.
                    }
                    Ok(cstatus) => {
                        // prctl cannot set, nothing left to do.
                        info!("ctx": "set_speculative_execution_mitigation",
                            "msg": cstatus.to_string(),
                            "feature": spec_feat.to_string(),
                            "status": cstatus.status.0);
                        continue;
                    }
                    Err(errno) => {
                        info!("ctx": "set_speculative_execution_mitigations",
                            "msg": format!("speculation-get error: {errno}"),
                            "feature": spec_feat.to_string());
                        // Fall-through and attempt to set.
                    }
                }

                match speculation_set(spec_feat, nstatus) {
                    Ok(_) => {
                        let cstatus = SpeculationControlStatus {
                            status: nstatus,
                            feature: spec_feat,
                        };
                        info!("ctx": "set_speculative_execution_mitigation",
                            "msg": cstatus.to_string(),
                            "feature": spec_feat.to_string(),
                            "status": cstatus.status.0);
                    }
                    Err(errno) => {
                        info!("ctx": "set_speculative_execution_mitigations",
                            "msg": format!("speculation-set error: {errno}"),
                            "feature": spec_feat.to_string(),
                            "status": nstatus.0);
                    }
                }
            }
        }

        // SAFETY: Handle Linux capabilities.
        let unsafe_caps = flags.allow_unsafe_caps();
        let unsafe_ptrace = flags.allow_unsafe_ptrace();
        if !unsafe_caps {
            // Get current effective caps.
            let capset = caps::read(None, caps::CapSet::Effective).unwrap_or_default();

            // Build allow mask based on sandbox flags.
            //
            // SAFETY: CAP_SYS_PTRACE is special because
            // Syd process needs this capability for
            // pidfd_getfd(2), that's why we drop this
            // capability late in the child before exec,
            // see below.
            let mut allow = caps::Capabilities::CAP_SYS_PTRACE;
            if flags.allow_safe_setuid() {
                allow |= caps::Capabilities::CAP_SETUID;
            }
            if flags.allow_safe_setgid() {
                allow |= caps::Capabilities::CAP_SETGID;
            }
            if flags.allow_unsafe_bind() {
                allow |= caps::Capabilities::CAP_NET_BIND_SERVICE;
            }
            if flags.allow_unsafe_chown() {
                allow |= caps::Capabilities::CAP_CHOWN;
            }
            if flags.allow_unsafe_mkbdev() || flags.allow_unsafe_mkcdev() {
                allow |= caps::Capabilities::CAP_MKNOD;
            }
            if flags.allow_unsafe_socket() {
                allow |= caps::Capabilities::CAP_NET_RAW;
            }
            if flags.allow_unsafe_syslog() {
                allow |= caps::Capabilities::CAP_SYSLOG;
            }
            if flags.allow_unsafe_time() {
                allow |= caps::Capabilities::CAP_SYS_TIME;
            }

            // Calculate capabilities to keep.
            //
            // SAFETY: Drop CAP_SYS_PTRACE from Inheritable capabilities,
            // unless trace/allow_unsafe_ptrace:1 is set to allow ptrace(2).
            let capeff = capset & allow;
            let mut capinh = capeff;
            if !unsafe_ptrace {
                capinh.remove(caps::Capabilities::CAP_SYS_PTRACE);
            }

            // SAFETY: Drop CAP_CHOWN from Inheritable capabilities,
            // even if trace/allow_unsafe_chmod is set.
            // This ensures only syd-emulator threads can ever run
            // the privileged chmod(2) calls after access check.
            capinh.remove(caps::Capabilities::CAP_CHOWN);

            // SAFETY: Drop CAP_MKNOD from Inheritable capabilities,
            // even if trace/allow_unsafe_mk{b,c}dev is set.
            // This ensures only syd-emulator threads can ever run
            // these privileged mknod(2) calls after access check.
            capinh.remove(caps::Capabilities::CAP_MKNOD);

            // SAFETY: Drop CAP_NET_BIND_SERVICE from Inheritable capabilities,
            // even if trace/allow_unsafe_bind is set.
            // This ensures only syd-emulator threads can ever run
            // the privileged bind(2) calls after access check.
            capinh.remove(caps::Capabilities::CAP_NET_BIND_SERVICE);

            // SAFETY: Drop CAP_NET_RAW from Inheritable capabilities,
            // even if trace/allow_unsafe_socket is set.
            // This ensures only syd-emulator threads can ever run
            // the privileged socket(2) calls after access check.
            capinh.remove(caps::Capabilities::CAP_NET_RAW);

            // SAFETY:
            // 1. Set effective capabilities.
            // 2. Set permitted capabilities to ensure we cannot gain caps back.
            // 3. Set inheritable capabilities to ensure sandbox process inherits.
            //
            // Set at once using set_all to reduce syscalls.
            caps::set_all(None, capeff, capeff, capinh)?;

            if log_enabled!(LogLevel::Info) {
                let caps_rem = (capset & !capeff)
                    .iter()
                    .filter_map(|f| caps::Capability::try_from(f).ok())
                    .map(|c| c.to_string())
                    .collect::<Vec<_>>();
                let caps_set = capeff
                    .iter()
                    .filter_map(|f| caps::Capability::try_from(f).ok())
                    .map(|c| c.to_string())
                    .collect::<Vec<_>>();
                if caps_rem.is_empty() && caps_set.is_empty() {
                    info!("ctx": "restrict_linux_capabilities",
                        "msg": "no Linux capabilities to restrict");
                } else {
                    info!("ctx": "restrict_linux_capabilities",
                        "msg": "Linux capabilities restricted",
                        "caps_set": caps_set, "caps_rem": caps_rem);
                }
            }
        }

        // Register as a process subreaper if we're not already pid1.
        // This is important because otherwise processes will be
        // reparented to the actual pid1, after which we can no longer
        // access their /proc/pid/mem without ptrace rights.
        let pid = Pid::this().as_raw();
        if pid != 1 {
            set_child_subreaper(true)?;
            info!("ctx": "set_child_subreaper",
                "msg": "child-subreaper attribute set",
                "sub": pid);
        }

        // Apply seccomp hardening for the Syd process itself.
        // This also inherits to the child process, and
        // unshare, mount etc. restrictions happen here.
        Self::load_seccomp_parent(sandbox)?;

        Ok(())
    }

    /// Insert this system call to the list of allowed system calls.
    /// No filtering is done one these system calls and they're allowed at the kernel level.
    fn allow_syscall(sysallow: &mut AllowSet, syscall: ScmpSyscall) {
        sysallow.insert(syscall);
    }

    /// Insert a system call handler.
    #[expect(clippy::cognitive_complexity)]
    #[expect(clippy::disallowed_methods)]
    fn insert_handler(
        handlers: &mut HandlerMap,
        syscall_name: &'static str,
        handler: impl Fn(UNotifyEventRequest) -> ScmpNotifResp + Clone + Send + Sync + 'static,
    ) {
        for arch in SCMP_ARCH {
            if let Ok(sys) = ScmpSyscall::from_name_by_arch(syscall_name, *arch) {
                #[expect(clippy::disallowed_methods)]
                handlers
                    .insert(
                        Sydcall(sys, scmp_arch_raw(*arch)),
                        Arc::new(Box::new(handler.clone())),
                    )
                    .unwrap();
            } else {
                info!("ctx": "confine", "op": "hook_syscall",
                    "msg": format!("invalid or unsupported syscall {syscall_name}"));
            }

            // Support the new non-multiplexed ipc syscalls.
            if SydArch::from(arch).has_ipc() {
                let sys_ipc = match syscall_name {
                    "shmat" => Some(397),
                    "msgctl" => Some(402),
                    "semctl" => Some(394),
                    "shmctl" => Some(396),
                    "msgget" => Some(399),
                    "semget" => Some(393),
                    "shmget" => Some(395),
                    _ => None,
                };

                if let Some(sys) = sys_ipc {
                    #[expect(clippy::disallowed_methods)]
                    handlers
                        .insert(
                            Sydcall(ScmpSyscall::from(sys), scmp_arch_raw(*arch)),
                            Arc::new(Box::new(handler.clone())),
                        )
                        .unwrap();
                    continue;
                }
            }

            // Support the new non-multiplexed network syscalls on MIPS, PPC, S390 & X86.
            let sys = match *arch {
                ScmpArch::M68k => match syscall_name {
                    "socket" => 356,
                    "socketpair" => 357,
                    "bind" => 358,
                    // no accept on m68k.
                    "accept4" => 361,
                    "connect" => 359,
                    "getpeername" => 365,
                    "getsockname" => 364,
                    "getsockopt" => 362,
                    "recvfrom" => 368,
                    "recvmsg" => 369,
                    "recvmmsg" => 371,
                    "recvmmsg_time64" => 471,
                    "sendto" => 366,
                    "sendmsg" => 367,
                    "sendmmsg" => 372,
                    _ => continue,
                },
                ScmpArch::Mips | ScmpArch::Mipsel => match syscall_name {
                    "socket" => 183,
                    "socketpair" => 184,
                    "bind" => 169,
                    "accept" => 168,
                    "accept4" => 334,
                    "connect" => 170,
                    "getpeername" => 171,
                    "getsockname" => 172,
                    "getsockopt" => 173,
                    "recvfrom" => 176,
                    "recvmsg" => 177,
                    "recvmmsg" => 335,
                    "recvmmsg_time64" => 417,
                    "sendto" => 180,
                    "sendmsg" => 179,
                    "sendmmsg" => 343,
                    _ => continue,
                },
                ScmpArch::Ppc | ScmpArch::Ppc64 | ScmpArch::Ppc64Le => match syscall_name {
                    "socket" => 326,
                    "socketpair" => 333,
                    "bind" => 327,
                    "accept" => 330,
                    "accept4" => 344,
                    "connect" => 328,
                    "getpeername" => 332,
                    "getsockname" => 331,
                    "getsockopt" => 340,
                    "recvfrom" => 337,
                    "recvmsg" => 342,
                    "recvmmsg" => 343,
                    "recvmmsg_time64" => 417,
                    "sendto" => 335,
                    "sendmsg" => 341,
                    "sendmmsg" => 349,
                    _ => continue,
                },
                ScmpArch::S390X | ScmpArch::S390 => match syscall_name {
                    "socket" => 359,
                    "socketpair" => 360,
                    "bind" => 361,
                    // no accept on s390x.
                    "accept4" => 364,
                    "connect" => 362,
                    "getpeername" => 368,
                    "getsockname" => 367,
                    "getsockopt" => 365,
                    "recvfrom" => 371,
                    "recvmsg" => 372,
                    "recvmmsg" => 357,
                    "sendto" => 369,
                    "sendmsg" => 370,
                    "sendmmsg" => 358,
                    _ => continue,
                },
                ScmpArch::X86 => match syscall_name {
                    "socket" => 359,
                    "socketpair" => 360,
                    "bind" => 361,
                    // no accept on x86.
                    "accept4" => 364,
                    "connect" => 362,
                    "getpeername" => 368,
                    "getsockname" => 367,
                    "getsockopt" => 365,
                    "recvfrom" => 371,
                    "recvmsg" => 372,
                    "recvmmsg" => 337,
                    "recvmmsg_time64" => 417,
                    "sendto" => 369,
                    "sendmsg" => 370,
                    "sendmmsg" => 345,
                    _ => continue,
                },
                _ => continue,
            };

            handlers
                .insert(
                    Sydcall(ScmpSyscall::from(sys), scmp_arch_raw(*arch)),
                    Arc::new(Box::new(handler.clone())),
                )
                .unwrap();

            #[expect(clippy::arithmetic_side_effects)]
            if matches!(*arch, ScmpArch::Mips | ScmpArch::Mipsel) {
                // This is a libseccomp oddity,
                // it could be a bug in the syscall multiplexer.
                // TODO: Investigate and submit a bug report.
                handlers
                    .insert(
                        Sydcall(ScmpSyscall::from(sys + 4000), scmp_arch_raw(*arch)),
                        Arc::new(Box::new(handler.clone())),
                    )
                    .unwrap();
            }
        }
    }

    /// Run a command with seccomp filter.
    /// This method will fork a child process, do some preparations and run the command in it.
    #[expect(clippy::cognitive_complexity)]
    #[expect(clippy::type_complexity)]
    fn spawn(
        mut self,
        mut command: crate::unshare::Command,
    ) -> SydResult<(
        Arc<WorkerCache>,
        Arc<RwLock<Sandbox>>,
        SydJoinHandle<()>,
        Arc<AtomicBool>,
    )> {
        let mut sandbox = self.sandbox.write().unwrap_or_else(|err| err.into_inner());

        // SAFETY: Ensure Crypt sandboxing keys are wiped from memory
        // before sandbox process start to ensure there's no race with
        // the sandbox process.
        sandbox.set_crypt()?;

        if sandbox.lock.is_none() {
            // SAFETY: Set the sandbox lock if the state is unspecified.
            // This is safer than the previous default LockState::Exec.
            // We set this right before exec to ensure the initial configuration
            // passes through (ie config file and CLI options), however
            // we still do it pre-exec to ensure there's no race with
            // the sandbox process.
            //
            // !sandbox.is_running -> lock returns no errors.
            #[expect(clippy::disallowed_methods)]
            sandbox.lock(LockState::Set).expect("lock sandbox");
        }

        // SAFETY: Seal critical sandbox memory regions as read-only.
        // Tear down the sandbox if sealing is not possible.
        if sandbox.locked() {
            #[expect(clippy::disallowed_methods)]
            sandbox.seal().expect("seal sandbox");
        }

        let ssb = sandbox.flags.allow_unsafe_exec_speculative();
        let ioctl_denylist = sandbox.get_ioctl_deny();
        drop(sandbox); // release the write-lock.

        // Set command PTY as necessary.
        command.pty(self.pty_child.as_ref().map(|fd| fd.as_raw_fd()));

        // Set seccomp filter to be applied.
        let seccomp_filter = self.setup_seccomp(ssb)?;
        command.seccomp_filter(seccomp_filter);

        // Set ioctl denylist to be applied.
        command.ioctl_denylist(Some(ioctl_denylist));

        // Spawn child under sandbox.
        //
        // Ready, set, go!
        let child = command.spawn()?;
        let pid = child.id();
        let seccomp_fd = child.seccomp_fd;

        // Having passed the PTY child FD to the sandbox process,
        // it is now safe to close our instance of it.
        if let Some(fd) = self.pty_child.take() {
            let _ = close(fd);
        }

        // Attempt to set file-max to hard limit overriding the soft limit.
        // Since this is just an attempt for convenience, we log errors with info.
        // We do this late to access the static PROC_FILE() and to ensure the
        // sandbox process does _not_ inherit the file limits.
        let nr_open = proc_fs_nr_open().unwrap_or(0x100000);
        match getrlimit(Resource::RLIMIT_NOFILE)? {
            (soft_limit, hard_limit) if soft_limit < hard_limit => {
                // Careful on 32-bit, setrlimit expects an u32 not an u64!
                #[expect(clippy::useless_conversion)]
                let hard_limit = hard_limit.min(nr_open.try_into().unwrap_or(0x100000));
                match confine_rlimit(Resource::RLIMIT_NOFILE, Some(hard_limit)) {
                    Ok(_) => {
                        info!("ctx": "run", "op": "set_rlimit_nofile",
                            "msg": format!("file-max limit increased from {soft_limit} to {hard_limit}"));
                    }
                    Err(errno) => {
                        info!("ctx": "run", "op": "set_rlimit_nofile",
                            "msg": format!("setrlimit error: {errno}"),
                            "err": errno as i32);
                    }
                }
            }
            (_, hard_limit) => {
                info!("ctx": "run", "op": "set_rlimit_nofile",
                    "msg": format!("file-max limit is already set to hard limit {hard_limit}"));
            }
        };

        #[expect(clippy::cast_possible_wrap)]
        let pid = Pid::from_raw(pid as i32);
        let mut sandbox = self.sandbox.write().unwrap_or_else(|err| err.into_inner());
        sandbox.set_child(pid, child.pid_fd);
        let locked = sandbox.locked();
        let restrict_dumps = !sandbox.flags.allow_unsafe_dumpable();
        let rlimit_fsize = sandbox.rlimit_fsize;
        let sync_scmp = sandbox.flags.sync_scmp();
        let flags = *sandbox.flags;
        drop(sandbox);

        // Set stack size rlimit.
        match confine_rlimit(Resource::RLIMIT_STACK, Some(MAIN_STACK_SIZE)) {
            Ok(_) => {
                info!("ctx": "run", "op": "set_rlimit_stack",
                    "msg": format!("set stack resource limit for main thread to {MAIN_STACK_SIZE} bytes"),
                    "lim": MAIN_STACK_SIZE);
            }
            Err(errno) => {
                error!("ctx": "run", "op": "set_rlimit_stack",
                    "msg": format!("set stack resource limit for main thread: {errno}"),
                    "lim": MAIN_STACK_SIZE, "err": errno as i32);
                return Err(errno.into());
            }
        }

        // Set synchronous mode if requested and supported,
        // so each syscall handler thread wakes up
        // on the same CPU as the respective sandbox process.
        if sync_scmp {
            match seccomp_notify_set_flags(seccomp_fd, SECCOMP_USER_NOTIF_FD_SYNC_WAKE_UP) {
                Ok(_) => {
                    info!("ctx": "run", "op": "set_seccomp_synchronous_mode",
                        "msg": "set seccomp synchronous mode",
                        "fd": seccomp_fd);
                }
                Err(Errno::ENOSYS) => {
                    info!("ctx": "run", "op": "set_seccomp_synchronous_mode",
                        "msg": "seccomp synchronous mode isn't supported on this system",
                        "fd": seccomp_fd);
                }
                Err(errno) => {
                    error!("ctx": "run", "op": "set_seccomp_synchronous_mode",
                        "msg": format!("set seccomp synchronous mode error: {errno}"),
                        "fd": seccomp_fd, "err": errno as i32);
                    // Continue, as this mode is not significant to our use.
                }
            };
        }

        // SAFETY: Set Syd process dumpable attribute to false,
        // unless trace/allow_unsafe_dumpable:1 was passed at startup.
        // We do this after spawning the child but before spawning the
        // system call handler threads to ensure the sandbox process
        // does not inherit the attribute but cannot attach to Syd.
        if restrict_dumps {
            match set_dumpable(false) {
                Ok(_) => {
                    info!("ctx": "run", "op": "unset_dumpable",
                        "msg": "unset process dumpable attribute");
                }
                Err(errno) => {
                    error!("ctx": "run", "op": "unset_dumpable",
                        "msg": format!("error unsetting process dumpable attribute: {errno}"),
                        "err": errno as i32);
                    return Err(errno.into());
                }
            }

            match confine_rlimit(Resource::RLIMIT_CORE, None) {
                Ok(_) => {
                    info!("ctx": "run", "op": "set_rlimit_core",
                        "msg": "disabled coredump generation");
                }
                Err(errno) => {
                    error!("ctx": "run", "op": "set_rlimit_core",
                        "msg": format!("error disabling coredump generation: {errno}"),
                        "err": errno as i32);
                    return Err(errno.into());
                }
            }
        }

        // SAFETY: At this point Syd has successfully forked a new
        // process to execute the sandbox process. As such Syd no longer
        // needs the execve, and execveat system calls. Let's
        // disable these critical system calls here to ensure a
        // compromised Syd process cannot abuse them.
        // EXCEPTION: Sandbox is not locked and we need exec for cmd/exec.
        let mut ctx = ScmpFilterContext::new(ScmpAction::Allow)?;
        // 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(ssb)?;
        // Synchronize filter to all threads.
        ctx.set_ctl_tsync(true)?;
        // 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).map_err(|e| err2no(&e))?;

        // SAFETY: Mitigate ret2mprotect for a compromised Syd process.
        // Be swift and kill process as this attempt is most certainly
        // malicious and the kill action cannot be misused to DOS the
        // Syd process.
        // Note, mips requires executable stack so we skip on this arch.
        #[cfg(not(any(
            target_arch = "mips",
            target_arch = "mips32r6",
            target_arch = "mips64",
            target_arch = "mips64r6"
        )))]
        {
            const X: u64 = libc::PROT_EXEC as u64;
            for sysname in ["mprotect", "pkey_mprotect"] {
                #[expect(clippy::disallowed_methods)]
                let syscall = ScmpSyscall::from_name(sysname).unwrap();
                ctx.add_rule_conditional(
                    ScmpAction::KillProcess,
                    syscall,
                    &[scmp_cmp!($arg2 & X == X)],
                )?;
            }
        }

        // SAFETY:
        // Mitigate the following for a compromised Syd process:
        // 1. cachestat, mincore: Page-cache attacks
        // 2. sethostname, setdomainname: Change UTS host/domain name.
        // Be swift and kill the process as this attempt it most certainly
        // malicious and the kill action can't be misused to DOS the Syd process.
        for sysname in PAGE_CACHE_SYSCALLS.iter().chain(UTS_SYSCALLS) {
            match ScmpSyscall::from_name(sysname) {
                Ok(syscall) => {
                    ctx.add_rule(ScmpAction::KillProcess, syscall)?;
                }
                Err(_) => {
                    info!("ctx": "confine", "op": "kill_syscall",
                        "msg": format!("invalid or unsupported syscall {sysname}"));
                }
            }
        }

        if locked {
            // SAFETY:
            // 1. Deny exec(3) calls if locked.
            // 2. Allow execveat(2) with AT_EXECVE_CHECK for
            //    Linux>=6.14.
            // 3. Be swift and kill process as this attempt is most
            //    certainly malicious and the kill action cannot be misused
            //    to DOS the Syd process.
            #[expect(clippy::disallowed_methods)]
            let syscall = ScmpSyscall::from_name("execve").unwrap();
            ctx.add_rule(ScmpAction::KillProcess, syscall)?;

            #[expect(clippy::disallowed_methods)]
            let syscall = ScmpSyscall::from_name("execveat").unwrap();
            if *HAVE_AT_EXECVE_CHECK {
                #[expect(clippy::cast_sign_loss)]
                let atcheck = AT_EXECVE_CHECK.bits() as u64;
                ctx.add_rule_conditional(
                    ScmpAction::KillProcess,
                    syscall,
                    &[scmp_cmp!($arg4 & atcheck == 0)],
                )?;
            } else {
                ctx.add_rule(ScmpAction::KillProcess, syscall)?;
            }
        }

        // SAFETY: After this point we no longer need the PTRACE_SEIZE
        // operation. Since this is a powerful operation, we apply a
        // quick seccomp filter to disable it from ever happening again.
        // This way a compromised Syd process cannot attach to any other
        // process in the system.
        // SAFETY: PTRACE_ATTACH is most certainly malicious,
        // add to kill set.
        // SAFETY: We add these ptrace rules without checking the state
        // of allow_unsafe_ptrace, because if allow_unsafe_ptrace is off we
        // have already performed the initial PTRACE_SEIZE, and if
        // allow_unsafe_ptrace is on we never need PTRACE_SEIZE to begin
        // with.
        #[expect(clippy::disallowed_methods)]
        let syscall = ScmpSyscall::from_name("ptrace").unwrap();
        #[expect(clippy::cast_lossless)]
        for op in [libc::PTRACE_ATTACH, crate::compat::PTRACE_SEIZE] {
            ctx.add_rule_conditional(
                ScmpAction::KillProcess,
                syscall,
                &[scmp_cmp!($arg0 == op as u64)],
            )?;
        }

        // Export seccomp rules if requested.
        let export = match ExportMode::from_env() {
            Some(ExportMode::BerkeleyPacketFilter) => {
                #[expect(clippy::disallowed_methods)]
                let file = OpenOptions::new()
                    .write(true)
                    .create_new(true)
                    .mode(0o400)
                    .open("syd_process.bpf")?;
                ctx.export_bpf(file)?;
                true
            }
            Some(ExportMode::PseudoFiltercode) => {
                // Lock stdout to prevent concurrent access.
                let mut stdout = std::io::stdout().lock();

                rwriteln!(stdout, "# Syd process rules with seccomp_fd {seccomp_fd}")?;
                rwrite!(stdout, "{}", seccomp_export_pfc(&ctx)?)?;
                true
            }
            _ => false,
        };

        // SAFETY: Inherit RLIMIT_FSIZE from sandbox process as necessary.
        // This ensures the limit works correctly for truncate(2), fallocate(2) etc.
        if let Some(lim) = rlimit_fsize {
            match confine_rlimit(Resource::RLIMIT_FSIZE, Some(lim)) {
                Ok(_) => {
                    info!("ctx": "run", "op": "set_rlimit_fsize",
                        "msg": format!("set fsize resource limit to {lim}"),
                        "lim": lim);
                }
                Err(errno) => {
                    error!("ctx": "run", "op": "set_rlimit_fsize",
                        "msg": format!("error setting fsize resource limit to {lim}: {errno}"),
                        "lim": lim, "err": errno as i32);
                    return Err(errno.into());
                }
            }
        }

        // Load the seccomp filter unless:
        // a. We're running in debug mode with SYD_SKIP_SCMP=1.
        // b. We're exporting seccomp filters
        if !export && env::var_os(ENV_SKIP_SCMP).is_none() {
            ctx.load()?;
        }

        self.supervise(seccomp_fd, flags)
    }

    // Set up seccomp for the sandbox process.
    #[expect(clippy::cognitive_complexity)]
    fn setup_seccomp(&self, ssb: bool) -> SydResult<ScmpFilterContext> {
        let mut ctx = ScmpFilterContext::new(ScmpAction::Errno(libc::ENOSYS))?;
        // 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(ssb)?;
        // Synchronize filter to all threads.
        ctx.set_ctl_tsync(true)?;
        // Request wait killable semantics.
        #[cfg(libseccomp_v2_6)]
        ctx.set_ctl_waitkill(true)?;
        // We deny with ENOSYS for bad/unsupported system call,
        // and kill process for bad arch.
        ctx.set_act_badarch(ScmpAction::KillProcess)?;
        // Use a binary tree sorted by syscall number if possible.
        let _ = ctx.set_ctl_optimize(2);
        // We don't want ECANCELED, we want actual errnos.
        let _ = ctx.set_api_sysrawrc(true);

        seccomp_add_architectures(&mut ctx)?;

        // Acquire the read lock to sandbox configuration.
        let sandbox = self.sandbox.read().unwrap_or_else(|err| err.into_inner());
        // If lock is None, it'll be set to Some(LockState::Set),
        // when the sandbox child starts executing.
        let is_lock_read = matches!(sandbox.lock, None | Some(LockState::Set));
        let is_lock = sandbox.locked_pre();
        let deny_namespaces = sandbox.flags.denied_namespaces();
        let safe_syslog = sandbox.flags.allow_safe_syslog();
        let deny_tsc = sandbox.flags.deny_tsc();
        let fake_root = sandbox.flags.fake_root();
        let has_exec = sandbox
            .enabled_any(Capability::CAP_EXEC | Capability::CAP_FORCE | Capability::CAP_TPE)
            || !sandbox.flags.allow_unsafe_exec_ldso()
            || !sandbox.flags.allow_unsafe_exec_memory()
            || !sandbox.flags.allow_unsafe_exec_nopie()
            || !sandbox.flags.allow_unsafe_exec_stack()
            || sandbox.flags.deny_exec_script()
            || sandbox.flags.deny_exec_elf32()
            || sandbox.flags.deny_exec_elf_dynamic()
            || sandbox.flags.deny_exec_elf_static();
        let has_chdir = sandbox.enabled(Capability::CAP_CHDIR);
        let has_mem = sandbox.enabled(Capability::CAP_MEM);
        let has_scoped_signals = *HAVE_LANDLOCK_SCOPED_SIGNALS;
        let restrict_cbpf = !sandbox.flags.allow_unsafe_cbpf();
        let restrict_ebpf = !sandbox.flags.allow_unsafe_ebpf();
        let restrict_chroot = !sandbox.flags.allow_unsafe_chroot();
        let restrict_pivot_root = !sandbox.flags.allow_unsafe_pivot_root();
        let restrict_cpu = !sandbox.flags.allow_unsafe_cpu();
        let restrict_exec_speculative = !sandbox.flags.allow_unsafe_exec_speculative();
        let restrict_keyring = !sandbox.flags.allow_unsafe_keyring();
        let restrict_iouring = !sandbox.flags.allow_unsafe_iouring();
        let restrict_mount = !sandbox.flags.allow_unsafe_unshare_mount();
        let restrict_uts = !sandbox.flags.allow_unsafe_unshare_uts();
        let restrict_deprecated = !sandbox.flags.allow_unsafe_deprecated();
        let restrict_madvise = !sandbox.flags.allow_unsafe_madvise();
        let restrict_mbind = !sandbox.flags.allow_unsafe_mbind();
        let restrict_nice = !sandbox.flags.allow_unsafe_nice();
        let restrict_oob = !sandbox.flags.allow_unsafe_oob();
        let restrict_page_cache = !sandbox.flags.allow_unsafe_page_cache();
        let restrict_perf = !sandbox.flags.allow_unsafe_perf();
        let restrict_pkey = !sandbox.flags.allow_unsafe_pkey();
        let restrict_personality = !sandbox.flags.allow_unsafe_personality();
        let restrict_prctl = !sandbox.flags.allow_unsafe_prctl();
        let restrict_pipe = !sandbox.flags.allow_unsafe_pipe();
        let restrict_prlimit = !sandbox.flags.allow_unsafe_prlimit();
        let restrict_ptrace = !sandbox.flags.allow_unsafe_ptrace();
        let restrict_sigreturn = !sandbox.flags.allow_unsafe_sigreturn();
        let restrict_rseq = !sandbox.flags.allow_unsafe_rseq();
        let restrict_sync = !sandbox.flags.allow_unsafe_sync();
        let restrict_time = !sandbox.flags.allow_unsafe_time();
        let restrict_vmsplice = !sandbox.flags.allow_unsafe_vmsplice();
        let skip_append = sandbox.skip_append();
        drop(sandbox); // release the read lock.

        // Fakeroot
        let id_action = if fake_root {
            ScmpAction::Errno(0)
        } else {
            ScmpAction::Allow
        };
        for sysname in GETID_SYSCALLS {
            if let Ok(syscall) = ScmpSyscall::from_name(sysname) {
                ctx.add_rule(id_action, syscall)?;
            } else {
                info!("ctx": "confine", "op": "filter_syscall",
                    "msg": format!("invalid or unsupported syscall {sysname}"));
            }
        }

        // Add notify rules for system calls with handlers.
        // Collect names into a HashSet to ensure uniqueness across architectures.
        let mut sydset: SydHashSet<String> = SydHashSet::default();
        for (&sydcall, _) in &*self.handlers {
            if i32::from(sydcall.0) == __NR_SCMP_ERROR {
                // Unsupported system call for the given arch, move on.
                continue;
            }

            // ? -> unsupported, see Sydcall::Display.
            // We handle chroot specially below.
            // We handle mmap{,2}, and sendto specially below.
            // We handle prctl specially where we only hook PR_SET_NAME.
            // We handle fcntl{,64} specially where we only hook F_SETFL with O_APPEND unset.
            // We handle ptrace specially where we only hook PTRACE_TRACEME request.
            // We handle sendto/recvfrom specially where we only hook for non-NULL address.
            // We handle getsockopt(2) specially where we only hook for SO_PEERCRED.
            // We handle syslog(2) calls specially below.
            // We allow/hook sysinfo(2) based on trace/allow_unsafe_sysinfo:1 since 3.32.4
            const SYS_EXCEPT: &[&str] = &[
                "?",
                "chroot",
                "fcntl",
                "fcntl64",
                "getsockopt",
                "mmap",
                "mmap2",
                "prctl",
                "ptrace",
                "recvfrom",
                "rt_sigaction",
                "sendto",
                "sigaction",
                "syslog",
            ]; // sorted

            let name = sydcall.to_string();
            if SYS_EXCEPT.binary_search(&name.as_str()).is_ok() {
                continue;
            }

            // We do not hook into kill family system calls,
            // if the kernel supports landlock(7) ABI 6.
            if has_scoped_signals && KILL_SYSCALLS.binary_search(&name.as_str()).is_ok() {
                continue;
            } // else: allow is handled in init.

            let syscall = if sydset.insert(name.clone()) {
                if let Ok(syscall) = ScmpSyscall::from_name(&name) {
                    syscall
                } else {
                    info!("ctx": "confine", "op": "hook_box_syscall",
                        "msg": format!("invalid or unsupported syscall {name}"));
                    continue;
                }
            } else {
                continue;
            };

            ctx.add_rule(ScmpAction::Notify, syscall)?;
        }

        // Add allow rules for system calls in the default allow list.
        let syscall_allow: Vec<_> = self.sysallow.iter().copied().collect();
        for syscall in &syscall_allow {
            ctx.add_rule(ScmpAction::Allow, *syscall)?;
        }

        // Hook {rt_}sigaction(2) for SA_RESTART tracking.
        // Skip hooking into sigaction calls where the new action is NULL.
        for sysname in ["sigaction", "rt_sigaction"] {
            let syscall = ScmpSyscall::from_name(sysname)?;
            ctx.add_rule_conditional(ScmpAction::Allow, syscall, &[scmp_cmp!($arg1 == 0)])?;
            ctx.add_rule_conditional(ScmpAction::Notify, syscall, &[scmp_cmp!($arg1 != 0)])?;
        }

        // If every kind of namespace shall be allowed, then let's
        // allow clone3(2) system call because pointer dereference
        // is irrelevant.
        if deny_namespaces == 0 {
            let sysname = "clone3";
            if let Ok(syscall) = ScmpSyscall::from_name(sysname) {
                ctx.add_rule(ScmpAction::Allow, syscall)?;
            } else {
                info!("ctx": "confine", "op": "allow_box_syscall",
                    "msg": format!("invalid or unsupported syscall {sysname}"));
            }
        }

        // Skip hooking into syslog(2) syscalls if the log feature
        // is off or if the sandbox has already been locked.
        let sysname = "syslog";
        #[expect(clippy::disallowed_methods)]
        let syscall = ScmpSyscall::from_name(sysname).unwrap();
        if !cfg!(feature = "log") || (!safe_syslog && is_lock_read) {
            ctx.add_rule(ScmpAction::Errno(libc::EPERM), syscall)?;
        } else {
            ctx.add_rule(ScmpAction::Notify, syscall)?;
        }

        // Hook into getsockopt(2) only for SO_PEERCRED.
        let sysname = "getsockopt";
        if let Ok(syscall) = ScmpSyscall::from_name(sysname) {
            const SOL_SOCKET: u64 = libc::SOL_SOCKET as u64;
            const SO_PEERCRED: u64 = libc::SO_PEERCRED as u64;
            ctx.add_rule_conditional(
                ScmpAction::Notify,
                syscall,
                &[
                    scmp_cmp!($arg1 == SOL_SOCKET),
                    scmp_cmp!($arg2 == SO_PEERCRED),
                ],
            )?;
            ctx.add_rule_conditional(
                ScmpAction::Allow,
                syscall,
                &[scmp_cmp!($arg1 != SOL_SOCKET)],
            )?;
            ctx.add_rule_conditional(
                ScmpAction::Allow,
                syscall,
                &[scmp_cmp!($arg2 != SO_PEERCRED)],
            )?;
        } else {
            info!("ctx": "confine", "op": "notify_box_syscall",
                "msg": format!("invalid or unsupported syscall {sysname}"));
        }

        // Skip hooking into recvfrom/sendto syscalls which are called
        // on connection-mode sockets. This type of sendto calls have
        // 4th and 5th arguments set to 0.
        for sysname in ["recvfrom", "sendto"] {
            let syscall = if let Ok(syscall) = ScmpSyscall::from_name(sysname) {
                syscall
            } else {
                continue;
            };

            // Restrict MSG_OOB as necessary.
            // This must be applied here as well as per-socket filter,
            // otherwise 32-bits with newer socket syscalls can escape.
            let oob = libc::MSG_OOB as u64;
            let mut cmp = Vec::new();
            if restrict_oob {
                ctx.add_rule_conditional(
                    ScmpAction::Errno(Errno::EOPNOTSUPP as i32),
                    syscall,
                    &[scmp_cmp!($arg3 & oob == oob)],
                )?;
                cmp.push(scmp_cmp!($arg3 & oob == 0));
            }

            // NULL address with zero length: Connection-mode socket.
            cmp.push(scmp_cmp!($arg4 == 0));
            cmp.push(scmp_cmp!($arg5 == 0));
            ctx.add_rule_conditional(ScmpAction::Allow, syscall, &cmp)?;
            cmp.pop();
            cmp.pop();

            // Non-NULL address with positive length = Notify.
            cmp.push(scmp_cmp!($arg4 != 0));
            cmp.push(scmp_cmp!($arg5 != 0));
            ctx.add_rule_conditional(ScmpAction::Notify, syscall, &cmp)?;
            cmp.pop();
            cmp.pop();

            // NULL address with positive length = EFAULT.
            cmp.push(scmp_cmp!($arg4 == 0));
            cmp.push(scmp_cmp!($arg5 != 0));
            ctx.add_rule_conditional(ScmpAction::Errno(Errno::EFAULT as i32), syscall, &cmp)?;
            cmp.pop();
            cmp.pop();

            // Non-NULL address with zero length = EINVAL.
            cmp.push(scmp_cmp!($arg4 != 0));
            cmp.push(scmp_cmp!($arg5 == 0));
            ctx.add_rule_conditional(ScmpAction::Errno(Errno::EINVAL as i32), syscall, &cmp)?;
        }

        // mmap{,2} hooks vary based on options:
        // 0. If Memory sandboxing is on, hook without flag check.
        // 1. Hook into PROT_EXEC|!MAP_ANONYMOUS for exec check.
        // 2. Hook into MAP_SHARED for append-only check.
        let syscalls = [
            ("mmap", ScmpAction::Trace(PTRACE_DATA_MMAP)),
            ("mmap2", ScmpAction::Trace(PTRACE_DATA_MMAP2)),
        ];
        if has_mem {
            for (sysname, ptrace_action) in syscalls {
                let action = if restrict_ptrace {
                    ptrace_action
                } else {
                    ScmpAction::Notify
                };

                #[expect(clippy::disallowed_methods)]
                let syscall = ScmpSyscall::from_name(sysname).unwrap();
                ctx.add_rule(action, syscall)?;
            }
        } else if has_exec || !skip_append || !is_lock {
            const PROT_EXEC: u64 = libc::PROT_EXEC as u64;
            const MAP_ANONYMOUS: u64 = libc::MAP_ANONYMOUS as u64;
            const MAP_SHARED: u64 = libc::MAP_SHARED as u64;
            for (sysname, ptrace_action) in syscalls {
                let action = if restrict_ptrace {
                    ptrace_action
                } else {
                    ScmpAction::Notify
                };

                #[expect(clippy::disallowed_methods)]
                let syscall = ScmpSyscall::from_name(sysname).unwrap();

                // Hook into fd-based mappings with PROT_EXEC
                // but without MAP_ANONYMOUS for Exec sandboxing.
                let fdx_action = if has_exec || !is_lock {
                    action
                } else {
                    ScmpAction::Allow
                };
                ctx.add_rule_conditional(
                    fdx_action,
                    syscall,
                    &[
                        scmp_cmp!($arg2 & PROT_EXEC == PROT_EXEC),
                        scmp_cmp!($arg3 & MAP_ANONYMOUS == 0),
                        scmp_cmp!($arg4 <= FD_MAX),
                    ],
                )?;

                // Hook into fd-based mappings with MAP_SHARED.
                ctx.add_rule_conditional(
                    action,
                    syscall,
                    &[
                        scmp_cmp!($arg3 & MAP_SHARED == MAP_SHARED),
                        scmp_cmp!($arg4 <= FD_MAX),
                    ],
                )?;

                // Allow negations.
                ctx.add_rule_conditional(
                    ScmpAction::Allow,
                    syscall,
                    &[scmp_cmp!($arg2 & PROT_EXEC == 0)],
                )?;
                ctx.add_rule_conditional(
                    ScmpAction::Allow,
                    syscall,
                    &[scmp_cmp!($arg3 & MAP_ANONYMOUS == MAP_ANONYMOUS)],
                )?;
                ctx.add_rule_conditional(ScmpAction::Allow, syscall, &[scmp_cmp!($arg4 > FD_MAX)])?;
            }
        } // else: allow is handled in init.

        // Hook chdir(2) via ptrace(2) if Chdir sandboxing is on.
        if has_chdir && restrict_ptrace {
            const CHDIR_CALLS: &[(&str, u16)] =
                &[("chdir", PTRACE_DATA_CHDIR), ("fchdir", PTRACE_DATA_FCHDIR)];
            for (sysname, data) in CHDIR_CALLS {
                if let Ok(syscall) = ScmpSyscall::from_name(sysname) {
                    ctx.add_rule(ScmpAction::Trace(*data), syscall)?;
                } else {
                    info!("ctx": "confine", "op": "trace_box_syscall",
                        "msg": format!("invalid or unsupported syscall {sysname}"));
                }
            }
        }

        // Hook execve(2) and execveat(2) via ptrace(2).
        if (has_exec || !is_lock) && restrict_ptrace {
            #[expect(clippy::disallowed_methods)]
            let sys_execve = ScmpSyscall::from_name("execve").unwrap();
            ctx.add_rule(ScmpAction::Trace(PTRACE_DATA_EXECVE), sys_execve)?;

            #[expect(clippy::disallowed_methods)]
            let sys_execveat = ScmpSyscall::from_name("execveat").unwrap();
            ctx.add_rule(ScmpAction::Trace(PTRACE_DATA_EXECVEAT), sys_execveat)?;
        } // else: allow is handled in init.

        // Hook {rt_}sigreturn(2) via ptrace(2).
        #[expect(clippy::disallowed_methods)]
        let sys_sigreturn = ScmpSyscall::from_name("sigreturn").unwrap();
        #[expect(clippy::disallowed_methods)]
        let sys_rt_sigreturn = ScmpSyscall::from_name("rt_sigreturn").unwrap();
        if restrict_ptrace && restrict_sigreturn {
            ctx.add_rule(ScmpAction::Trace(PTRACE_DATA_SIGRETURN), sys_sigreturn)?;
            ctx.add_rule(
                ScmpAction::Trace(PTRACE_DATA_RT_SIGRETURN),
                sys_rt_sigreturn,
            )?;
        } else {
            // TODO: Research if something similar is doable with seccomp only.
            ctx.add_rule(ScmpAction::Allow, sys_sigreturn)?;
            ctx.add_rule(ScmpAction::Allow, sys_rt_sigreturn)?;
        }

        // Restriction 0: Handle no-op syscalls:
        // 1. Turn chroot(2) into no-op if trace/allow_unsafe_chroot:1.
        // 2. Turn pivot_root(2) into no-op if trace/allow_unsafe_pivot_root:1.
        let mut noop_syscalls = Vec::with_capacity(2);
        if restrict_chroot {
            let sysname = "chroot";
            if let Ok(syscall) = ScmpSyscall::from_name(sysname) {
                ctx.add_rule(ScmpAction::Notify, syscall)?;
            } else {
                info!("ctx": "confine", "op": "notify_box_syscall",
                    "msg": format!("invalid or unsupported syscall {sysname}"));
            }
        } else {
            noop_syscalls.push("chroot");
        }
        if restrict_pivot_root {
            let sysname = "pivot_root";
            if let Ok(syscall) = ScmpSyscall::from_name(sysname) {
                ctx.add_rule(ScmpAction::Errno(libc::EPERM), syscall)?;
            } else {
                info!("ctx": "confine", "op": "deny_box_syscall",
                    "msg": format!("invalid or unsupported syscall {sysname}"));
            }
        } else {
            noop_syscalls.push("pivot_root");
        }
        for sysname in noop_syscalls {
            if let Ok(syscall) = ScmpSyscall::from_name(sysname) {
                ctx.add_rule(ScmpAction::Errno(0), syscall)?;
            } else {
                info!("ctx": "confine", "op": "noop_box_syscall",
                    "msg": format!("invalid or unsupported syscall {sysname}"));
            }
        }

        // Restriction 1: Deny unsafe set-id system calls.
        // Deny with Errno=0 -> Turn the system calls into no-op.
        // This is for compatibility, e.g. postgres invokes
        // setgroups before setuid and aborts on failure.
        for sysname in UNSAFE_ID_SYSCALLS {
            if let Ok(syscall) = ScmpSyscall::from_name(sysname) {
                ctx.add_rule(ScmpAction::Errno(0), syscall)?;
            } else {
                info!("ctx": "confine", "op": "noop_box_syscall",
                    "msg": format!("invalid or unsupported syscall {sysname}"));
            }
        }

        // Restriction 2: Allowlist known-safe prctls.
        // Things like PR_SET_MM, PR_SET_PTRACER, and PR_SET_SPECULATION_CTRL are left out.
        // PR_SET_TSC is out if deny-tsc is set and allow_unsafe_prctl is unset.
        #[expect(clippy::disallowed_methods)]
        let sys_prctl = ScmpSyscall::from_name("prctl").unwrap();
        if restrict_prctl {
            for (name, opt) in ALLOW_PRCTL {
                if deny_tsc && is_equal(name.as_bytes(), b"PR_SET_TSC") {
                    continue;
                }

                let act = if is_equal(name.as_bytes(), b"PR_SET_NAME") {
                    // SAFETY: Warn on PR_SET_NAME calls.
                    ScmpAction::Notify
                } else if is_equal(name.as_bytes(), b"PR_SET_PTRACER") {
                    // SAFETY: Turn into noop to mitigate ptracer detection.
                    ScmpAction::Errno(0)
                } else if restrict_cbpf && is_suffix(name.as_bytes(), b"_SECCOMP") {
                    // SAFETY:
                    // Deny all seccomp(2) operations with EINVAL,
                    // _unless_ trace/allow_unsafe_cbpf:1 is passed at startup.
                    //
                    // Note, allowing strict mode here is going to make no difference,
                    // as the kernel will return `EINVAL` anyway because a secure
                    // computing mode is already set by Syd and strict mode differs
                    // from the current mode (filter).
                    ScmpAction::Errno(libc::EINVAL)
                } else if restrict_perf && memmem::find(name.as_bytes(), b"_PERF_").is_some() {
                    // SAFETY:
                    // Deny perf prctl(2)s with EINVAL,
                    // _unless_ trace/allow_unsafe_perf:1 is passed at startup.
                    ScmpAction::Errno(libc::EINVAL)
                } else if restrict_exec_speculative
                    && is_suffix(name.as_bytes(), b"_SPECULATION_CTRL")
                {
                    // SAFETY:
                    // Deny speculation prctl(2)s with EINVAL,
                    // _unless_ trace/allow_unsafe_exec_speculative:1 is passed at startup.
                    ScmpAction::Errno(libc::EINVAL)
                } else {
                    ScmpAction::Allow
                };

                let cmp = ScmpArgCompare::new(0, ScmpCompareOp::Equal, *opt);
                ctx.add_rule_conditional(act, sys_prctl, &[cmp])?;
            }
        } else {
            ctx.add_rule(ScmpAction::Allow, sys_prctl)?;
        }

        // Restriction 3: Disallow seccomp(2) operations with EINVAL
        // _unless_ trace/allow_unsafe_cbpf:1 is passed at startup.
        //
        // Note, allowing strict mode here is going to make no difference,
        // as the kernel will return `EINVAL` anyway because a secure
        // computing mode is already set by Syd and strict mode differs
        // from the current mode (filter).
        #[expect(clippy::disallowed_methods)]
        let sys_seccomp = ScmpSyscall::from_name("seccomp").unwrap();
        if restrict_cbpf {
            // 1. Allow SECCOMP_GET_ACTION_AVAIL & SECCOMP_GET_NOTIF_SIZES.
            // 2. Deny SECCOMP_SET_MODE_STRICT & SECCOMP_SET_MODE_FILTER with EINVAL.
            // 3. Deny all future seccomp(2) operations.
            for op in [
                libc::SECCOMP_GET_ACTION_AVAIL,
                libc::SECCOMP_GET_NOTIF_SIZES,
            ] {
                ctx.add_rule_conditional(
                    ScmpAction::Allow,
                    sys_seccomp,
                    &[scmp_cmp!($arg0 == u64::from(op))],
                )?;
            }

            for op in [libc::SECCOMP_SET_MODE_STRICT, libc::SECCOMP_SET_MODE_FILTER] {
                ctx.add_rule_conditional(
                    ScmpAction::Errno(libc::EINVAL),
                    sys_seccomp,
                    &[scmp_cmp!($arg0 == u64::from(op))],
                )?;
            }

            // Make the filter future-proof.
            const SECCOMP_OPERATION_MAX: u64 = libc::SECCOMP_GET_NOTIF_SIZES as u64;
            ctx.add_rule_conditional(
                ScmpAction::Errno(libc::EINVAL),
                sys_seccomp,
                &[scmp_cmp!($arg0 > SECCOMP_OPERATION_MAX)],
            )?;
        } else {
            ctx.add_rule(ScmpAction::Allow, sys_seccomp)?;
        }

        // Restriction 4: Disallow eBPF programs unless trace/allow_unsafe_ebpf:1
        if !restrict_ebpf {
            for sysname in EBPF_SYSCALLS {
                if let Ok(syscall) = ScmpSyscall::from_name(sysname) {
                    if *sysname == "bpf" {
                        // SAFETY: Even with trace/allow_unsafe_ebpf:1,
                        // do not allow the BPF commands
                        // BPF_MAP_CREATE and BPF_PROG_LOAD
                        // which are privileged and require CAP_BPF.
                        // See:
                        // https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/commit/?id=c8644cd0efe719608ddcb341bcf087d4bc0bf6b8
                        const BPF_MAP_CREATE: u8 = 0;
                        const BPF_PROG_LOAD: u8 = 5;
                        const MAX_BPF_CMD: u8 = 38; /* As of Linux>=6.15 */
                        for bpf_cmd in 0..MAX_BPF_CMD {
                            let action = if matches!(bpf_cmd, BPF_MAP_CREATE | BPF_PROG_LOAD) {
                                ScmpAction::Errno(libc::EPERM)
                            } else {
                                ScmpAction::Allow
                            };
                            ctx.add_rule_conditional(
                                action,
                                syscall,
                                &[scmp_cmp!($arg0 == bpf_cmd.into())],
                            )?;
                        }
                        // Ensure future compatibility.
                        ctx.add_rule_conditional(
                            ScmpAction::Errno(libc::EPERM),
                            syscall,
                            &[scmp_cmp!($arg0 >= MAX_BPF_CMD.into())],
                        )?;
                    } else {
                        ctx.add_rule(ScmpAction::Allow, syscall)?;
                    }
                } else {
                    info!("ctx": "confine", "op": "allow_box_syscall",
                        "msg": format!("invalid or unsupported syscall {sysname}"));
                }
            }
        }

        // Restriction 5:
        //
        // Disallow unsetting O_APPEND for append-only files.
        //
        // Note, pwritev2(2) flag RWF_NOAPPEND is denied as part
        // of a separate filter because of its arch-specific nature.
        if !skip_append {
            const F_SETFL: u64 = libc::F_SETFL as u64;
            const O_APPEND: u64 = libc::O_APPEND as u64;
            for sysname in ["fcntl", "fcntl64"] {
                if let Ok(syscall) = ScmpSyscall::from_name(sysname) {
                    ctx.add_rule_conditional(
                        ScmpAction::Allow,
                        syscall,
                        &[scmp_cmp!($arg1 != F_SETFL)],
                    )?;
                    ctx.add_rule_conditional(
                        ScmpAction::Allow,
                        syscall,
                        &[
                            scmp_cmp!($arg1 == F_SETFL),
                            scmp_cmp!($arg2 & O_APPEND == O_APPEND),
                        ],
                    )?;
                    ctx.add_rule_conditional(
                        ScmpAction::Notify,
                        syscall,
                        &[
                            scmp_cmp!($arg1 == F_SETFL),
                            scmp_cmp!($arg2 & O_APPEND == 0),
                        ],
                    )?;
                } else {
                    info!("ctx": "confine", "op": "allow_box_syscall",
                        "msg": format!("invalid or unsupported syscall {sysname}"));
                }
            }
        } else {
            // Allow fcntl and fcntl64 system calls.
            // This was already done in init,
            // so we don't have to repeat here.
        }

        // Restriction 6: Disallow prlimit from setting resources.
        #[expect(clippy::disallowed_methods)]
        let sys_prlimit = ScmpSyscall::from_name("prlimit64").unwrap();
        if restrict_prlimit {
            // prlimit(pid_t pid, int resource,
            //         const struct rlimit *_Nullable new_limit,
            //         struct rlimit *_Nullable old_limit);
            // SAFETY: new_limit==NULL is safe.
            ctx.add_rule_conditional(ScmpAction::Allow, sys_prlimit, &[scmp_cmp!($arg2 == 0)])?;
        } else {
            #[expect(clippy::disallowed_methods)]
            let sys_setrlimit = ScmpSyscall::from_name("setrlimit").unwrap();
            ctx.add_rule(ScmpAction::Allow, sys_prlimit)?;
            ctx.add_rule(ScmpAction::Allow, sys_setrlimit)?;
        }

        // Restriction 7: Disallow CPU emulation functionality.
        if !restrict_cpu {
            for sysname in CPU_SYSCALLS {
                if let Ok(syscall) = ScmpSyscall::from_name(sysname) {
                    ctx.add_rule(ScmpAction::Allow, syscall)?;
                } else {
                    info!("ctx": "confine", "op": "allow_box_syscall",
                        "msg": format!("invalid or unsupported syscall {sysname}"));
                }
            }
        }

        // Restriction 8: Disallow Kernel keyring access.
        if !restrict_keyring {
            for sysname in KEYRING_SYSCALLS {
                if let Ok(syscall) = ScmpSyscall::from_name(sysname) {
                    ctx.add_rule(ScmpAction::Allow, syscall)?;
                } else {
                    info!("ctx": "confine", "op": "allow_box_syscall",
                        "msg": format!("invalid or unsupported syscall {sysname}"));
                }
            }
        }

        // Restriction 9: Disallow adjusting system time.
        if !restrict_time {
            for sysname in TIME_SYSCALLS {
                if let Ok(syscall) = ScmpSyscall::from_name(sysname) {
                    ctx.add_rule(ScmpAction::Allow, syscall)?;
                } else {
                    info!("ctx": "confine", "op": "allow_box_syscall",
                        "msg": format!("invalid or unsupported syscall {sysname}"));
                }
            }
        }

        // Restriction 10: Disallow io_uring interface.
        if !restrict_iouring {
            for sysname in IOURING_SYSCALLS {
                if let Ok(syscall) = ScmpSyscall::from_name(sysname) {
                    ctx.add_rule(ScmpAction::Allow, syscall)?;
                } else {
                    info!("ctx": "confine", "op": "allow_box_syscall",
                        "msg": format!("invalid or unsupported syscall {sysname}"));
                }
            }
        }

        // Restriction 11: Disallow page cache system calls.
        if !restrict_page_cache {
            for sysname in PAGE_CACHE_SYSCALLS {
                if let Ok(syscall) = ScmpSyscall::from_name(sysname) {
                    ctx.add_rule(ScmpAction::Allow, syscall)?;
                } else {
                    info!("ctx": "confine", "op": "allow_box_syscall",
                        "msg": format!("invalid or unsupported syscall {sysname}"));
                }
            }
        }

        // Restriction 12: Disallow deprecated system calls.
        if !restrict_deprecated {
            for sysname in DEPRECATED_SYSCALLS {
                if let Ok(syscall) = ScmpSyscall::from_name(sysname) {
                    ctx.add_rule(ScmpAction::Allow, syscall)?;
                } else {
                    info!("ctx": "confine", "op": "allow_box_syscall",
                        "msg": format!("invalid or unsupported syscall {sysname}"));
                }
            }
        }

        // Restriction 12: Harden madvise(2) system call.
        if let Ok(syscall) = ScmpSyscall::from_name("madvise") {
            if restrict_madvise {
                // Allow safe madvise(2) advice.
                for (name, advice) in ALLOW_MADVISE {
                    let action =
                        if !*HAVE_MADV_GUARD && memmem::find(name.as_bytes(), b"GUARD").is_some() {
                            // MADV_GUARD_INSTALL/MADV_GUARD_REMOVE is Linux>=6.13.
                            ScmpAction::Errno(libc::EINVAL)
                        } else {
                            ScmpAction::Allow
                        };
                    ctx.add_rule_conditional(action, syscall, &[scmp_cmp!($arg2 == *advice)])?;
                }

                // No-op unprivileged madvise(2) advice.
                for advice in NOOP_MADVISE {
                    ctx.add_rule_conditional(
                        ScmpAction::Errno(0),
                        syscall,
                        &[scmp_cmp!($arg2 == *advice)],
                    )?;
                }

                // Deny privileged madvise(2) advice.
                for advice in DENY_MADVISE {
                    ctx.add_rule_conditional(
                        ScmpAction::Errno(libc::EPERM),
                        syscall,
                        &[scmp_cmp!($arg2 == *advice)],
                    )?;
                }
            } else {
                // trace/allow_unsafe_madvise:1
                // Allow madvise(2) without restriction.
                ctx.add_rule(ScmpAction::Allow, syscall)?;
            }
        } else {
            info!("ctx": "confine", "op": "allow_box_syscall",
                "msg": "invalid or unsupported syscall madvise");
        }

        // Restriction 13: Disallow mbind(2) system call.
        if !restrict_mbind {
            if let Ok(syscall) = ScmpSyscall::from_name("mbind") {
                ctx.add_rule(ScmpAction::Allow, syscall)?;
            } else {
                info!("ctx": "confine", "op": "allow_box_syscall",
                    "msg": "invalid or unsupported syscall mbind");
            }
        }

        // Restriction 14: Disallow sync(2) and syncfs(2) system calls.
        // Use trace/allow_unsafe_sync:1 to relax the restriction.
        let action = if restrict_sync {
            ScmpAction::Errno(0)
        } else {
            ScmpAction::Allow
        };
        for sysname in SYNC_SYSCALLS {
            if let Ok(syscall) = ScmpSyscall::from_name(sysname) {
                ctx.add_rule(action, syscall)?;
            } else {
                info!("ctx": "confine", "op": "noop_box_syscall",
                    "msg": format!("invalid or unsupported syscall {sysname}"));
            }
        }

        // Restriction 15: Provide stealth for PTRACE_TRACEME operation.
        // This ptrace operation is the single one that is allowed in
        // the tracee and therefore is quite often used to detect the
        // existence of a ptracer. Here we provide a best-effort
        // mitigation against this and turn PTRACE_TRACEME into a no-op
        // that always succeeds. This way a naive approach is going to
        // fail to detect a ptracer.
        // As of version 3.19.0, we turn all ptrace operations into
        // no-ops so as to provide a best-effort mitigation against
        // using requests such as PTRACE_ATTACH or PTRACE_SEIZE to
        // detect a ptracer.
        // As of version 3.25.2, we log ptrace(2) calls in case we're
        // allowing them to help with malware analysis.
        let action = if restrict_ptrace {
            ScmpAction::Errno(0)
        } else {
            ScmpAction::Allow
        };
        if action == ScmpAction::Allow {
            for sysname in PTRACE_SYSCALLS {
                if let Ok(syscall) = ScmpSyscall::from_name(sysname) {
                    ctx.add_rule(action, syscall)?;
                } else {
                    info!("ctx": "confine", "op": "noop_box_syscall",
                        "msg": format!("invalid or unsupported syscall {sysname}"));
                }
            }
        } else if let Ok(syscall) = ScmpSyscall::from_name("ptrace") {
            const PTRACE_TRACEME: u64 = libc::PTRACE_TRACEME as u64;
            ctx.add_rule_conditional(action, syscall, &[scmp_cmp!($arg0 != PTRACE_TRACEME)])?;
            ctx.add_rule_conditional(
                ScmpAction::Notify,
                syscall,
                &[scmp_cmp!($arg0 == PTRACE_TRACEME)],
            )?;
        } else {
            info!("ctx": "confine", "op": "noop_box_syscall",
                "msg": format!("invalid or unsupported syscall ptrace"));
        }

        // Restriction 16: Disallow perf.
        if !restrict_perf {
            for sysname in PERF_SYSCALLS {
                if let Ok(syscall) = ScmpSyscall::from_name(sysname) {
                    ctx.add_rule(ScmpAction::Allow, syscall)?;
                } else {
                    info!("ctx": "confine", "op": "allow_box_syscall",
                        "msg": format!("invalid or unsupported syscall {sysname}"));
                }
            }
        }

        // Restriction 17: Disallow memory protection keys.
        if !restrict_pkey {
            for sysname in PKEY_SYSCALLS {
                if let Ok(syscall) = ScmpSyscall::from_name(sysname) {
                    ctx.add_rule(ScmpAction::Allow, syscall)?;
                } else {
                    info!("ctx": "confine", "op": "allow_box_syscall",
                        "msg": format!("invalid or unsupported syscall {sysname}"));
                }
            }
        }

        // Restriction 18: Disallow mount family.
        if !restrict_mount {
            for sysname in MOUNT_SYSCALLS {
                if let Ok(syscall) = ScmpSyscall::from_name(sysname) {
                    ctx.add_rule(ScmpAction::Allow, syscall)?;
                } else {
                    info!("ctx": "confine", "op": "allow_box_syscall",
                        "msg": format!("invalid or unsupported syscall {sysname}"));
                }
            }
        }

        // Restriction 19: Disallow UTS family.
        if !restrict_uts {
            for sysname in UTS_SYSCALLS {
                if let Ok(syscall) = ScmpSyscall::from_name(sysname) {
                    ctx.add_rule(ScmpAction::Allow, syscall)?;
                } else {
                    info!("ctx": "confine", "op": "allow_box_syscall",
                        "msg": format!("invalid or unsupported syscall {sysname}"));
                }
            }
        }

        // Restriction 20: Disallow nice(2).
        if !restrict_nice {
            for sysname in NICE_SYSCALLS {
                if let Ok(syscall) = ScmpSyscall::from_name(sysname) {
                    ctx.add_rule(ScmpAction::Allow, syscall)?;
                } else {
                    info!("ctx": "confine", "op": "allow_box_syscall",
                        "msg": format!("invalid or unsupported syscall {sysname}"));
                }
            }
        }

        // Restriction 21: Disallow rseq(2) system call.
        if !restrict_rseq {
            let sysname = "rseq";
            if let Ok(syscall) = ScmpSyscall::from_name(sysname) {
                ctx.add_rule(ScmpAction::Allow, syscall)?;
            } else {
                info!("ctx": "confine", "op": "allow_box_syscall",
                    "msg": format!("invalid or unsupported syscall {sysname}"));
            }
        }

        // Restriction 22: Disallow vmsplice(2) system call.
        //
        // As of 3.41.4, we allow specifying trace/allow_unsafe_vmsplice:1
        // to allow vmsplice(2) system call. nix has a test for vmsplice(2).
        if !restrict_vmsplice {
            if let Ok(syscall) = ScmpSyscall::from_name("vmsplice") {
                ctx.add_rule(ScmpAction::Allow, syscall)?;
            } else {
                info!("ctx": "confine", "op": "allow_box_syscall",
                    "msg": "invalid or unsupported syscall vmsplice");
            }
        }

        // Restriction 23: Disallow unsafe personality(2) personas.
        let sysname = "personality";
        if let Ok(syscall) = ScmpSyscall::from_name(sysname) {
            if restrict_personality {
                for persona in UNSAFE_PERSONA {
                    let persona = persona.bits();
                    ctx.add_rule_conditional(
                        ScmpAction::KillProcess,
                        syscall,
                        &[scmp_cmp!($arg0 == persona)],
                    )?;
                }
                for &(_, persona) in SAFE_PERSONAS {
                    ctx.add_rule_conditional(
                        ScmpAction::Allow,
                        syscall,
                        &[scmp_cmp!($arg0 == persona)],
                    )?;
                }
            } else {
                ctx.add_rule(ScmpAction::Allow, syscall)?;
            }
        } else {
            info!("ctx": "confine", "op": "allow_box_syscall",
                "msg": format!("invalid or unsupported syscall {sysname}"));
        }

        // Restriction 24: Disallow O_NOTIFICATION_PIPE for pipe2(2).
        let sysname = "pipe2";
        #[expect(clippy::cast_sign_loss)]
        if let Ok(syscall) = ScmpSyscall::from_name(sysname) {
            if restrict_pipe {
                // O_NOTIFICATION_PIPE is equivalent to O_EXCL,
                // see: linux/watch_queue.h
                const O_NOTIFICATION_PIPE: u64 = OFlag::O_EXCL.bits() as u64;

                ctx.add_rule_conditional(
                    ScmpAction::Allow,
                    syscall,
                    &[scmp_cmp!($arg1 & O_NOTIFICATION_PIPE == 0)],
                )?;

                // pipe2(2) returns ENOPKG when CONFIG_WATCH_QUEUE
                // is not enabled in the host Linux kernel.
                ctx.add_rule_conditional(
                    ScmpAction::Errno(Errno::ENOPKG as i32),
                    syscall,
                    &[scmp_cmp!($arg1 & O_NOTIFICATION_PIPE == O_NOTIFICATION_PIPE)],
                )?;
            } else {
                ctx.add_rule(ScmpAction::Allow, syscall)?;
            }
        } else {
            info!("ctx": "confine", "op": "allow_box_syscall",
                "msg": format!("invalid or unsupported syscall {sysname}"));
        }

        // Export seccomp rules if requested.
        if let Some(mode) = ExportMode::from_env() {
            self.seccomp_export(&ctx, mode)?;
        }

        // Precompute seccomp rules which ensures:
        // 1. We fail early before spawning sandbox process on errors.
        // 2. We reduce number of memory {de,}allocations that are
        //    going to happen in the sandbox process after loading
        //    the seccomp filter but before passing the notification
        //    file descriptor back to Syd. This issue can become apparent
        //    when memory sandboxing is enabled whereby a memory
        //    {de,}allocation request can deadlock this process.
        // Note, we precompute after exporting the seccomp filter to
        // ease debugging in case of potential errors during
        // precomputation.
        #[cfg(libseccomp_v2_6)]
        ctx.precompute()?;

        Ok(ctx)
    }

    /// Set up seccomp for the Syd process which will be inherited by
    /// the child. this is important to restrict potential attack space
    /// in case Syd process is compromised somehow.
    #[expect(clippy::cognitive_complexity)]
    fn load_seccomp_parent(sandbox: &Sandbox) -> SydResult<()> {
        let mut ctx = ScmpFilterContext::new(ScmpAction::Allow)?;
        // 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
        let ssb = sandbox.flags.allow_unsafe_exec_speculative();
        ctx.set_ctl_ssb(ssb)?;
        // Synchronize filter to all threads.
        ctx.set_ctl_tsync(true)?;
        // We deny with ENOSYS for bad/unsupported system call, and kill process for bad arch.
        ctx.set_act_badarch(ScmpAction::KillProcess)?;
        // Use a binary tree sorted by syscall number if possible.
        let _ = ctx.set_ctl_optimize(2);
        // We don't want ECANCELED, we want actual errnos.
        let _ = ctx.set_api_sysrawrc(true);

        // Add supported architectures.
        seccomp_add_architectures(&mut ctx)?;

        // Determine restrictions based on sandbox flags.
        let restrict_exec_memory = !sandbox.flags.allow_unsafe_exec_memory();
        let restrict_exec_null = !sandbox.flags.allow_unsafe_exec_null();
        let restrict_kcapi = !sandbox.flags.allow_safe_kcapi();
        let restrict_socket = !sandbox.flags.allow_unsupp_socket();
        let restrict_setsockopt = !sandbox.flags.allow_unsafe_setsockopt();
        let restrict_mkbdev = !sandbox.flags.allow_unsafe_mkbdev();
        let restrict_mkcdev = !sandbox.flags.allow_unsafe_mkcdev();
        let restrict_oob = !sandbox.flags.allow_unsafe_oob();
        let restrict_msgqueue = !sandbox.flags.allow_unsafe_msgqueue();
        let restrict_shm = !sandbox.flags.allow_unsafe_shm();

        // Restriction 0: Disable list of compiled-in dead system calls.
        // These system calls are not used by Syd.
        for sysname in DEAD_SYSCALLS {
            if let Ok(syscall) = ScmpSyscall::from_name(sysname) {
                ctx.add_rule(ScmpAction::Errno(libc::ENOSYS), syscall)?;
            } else {
                info!("ctx": "confine", "op": "deny_syscall",
                    "msg": format!("invalid or unsupported syscall {sysname}"));
            }
        }

        // Restriction 1:
        // (a) Prevent execve where arg0==NULL||arg1==NULL||arg2==NULL
        // (b) Prevent execveat where arg1==NULL||arg2==NULL||arg3==NULL
        // On Linux, argv and envp can be specified as NULL. In
        // both cases, this has the same effect as specifying the
        // argument as a pointer to a list containing a single null
        // pointer. Do not take advantage of this nonstandard and
        // nonportable misfeature! On many other UNIX systems,
        // specifying argv as NULL will result in an error (EFAULT).
        // Some other UNIX systems treat the envp==NULL case the same as
        // Linux.
        // SAFETY: We kill the process rather than deny with EFAULT
        // because this call is most certainly malicious and this gives
        // the system administrator a notification via dmesg(1) about
        // the potentially malicious activity.
        //
        // This mitigation can be disabled with trace/allow_unsafe_exec_null:1.
        if restrict_exec_null {
            #[expect(clippy::disallowed_methods)]
            let sys_execve = ScmpSyscall::from_name("execve").unwrap();
            #[expect(clippy::disallowed_methods)]
            let sys_execveat = ScmpSyscall::from_name("execveat").unwrap();
            ctx.add_rule_conditional(
                ScmpAction::KillProcess,
                sys_execve,
                &[scmp_cmp!($arg0 == 0)],
            )?;
            ctx.add_rule_conditional(
                ScmpAction::KillProcess,
                sys_execve,
                &[scmp_cmp!($arg1 == 0)],
            )?;
            ctx.add_rule_conditional(
                ScmpAction::KillProcess,
                sys_execve,
                &[scmp_cmp!($arg2 == 0)],
            )?;
            ctx.add_rule_conditional(
                ScmpAction::KillProcess,
                sys_execveat,
                &[scmp_cmp!($arg1 == 0)],
            )?;
            ctx.add_rule_conditional(
                ScmpAction::KillProcess,
                sys_execveat,
                &[scmp_cmp!($arg2 == 0)],
            )?;
            ctx.add_rule_conditional(
                ScmpAction::KillProcess,
                sys_execveat,
                &[scmp_cmp!($arg3 == 0)],
            )?;
        }

        // Restriction 2: Prevent mmap(addr<${mmap_min_addr}, MAP_FIXED).
        // Arguably this does not give us much however ensuring mmap_min_addr
        // is constant after the start of the sandbox with zero-cost can't be bad.
        // In addition we kill the process directly rather than denying the call
        // like mmap_min_addr does, thereby giving the system administrator higher
        // chance to notice potentially malicious activity.
        if restrict_exec_memory {
            const MAP_FIXED: u64 = libc::MAP_FIXED as u64;
            const MAP_FIXED_NOREPLACE: u64 = crate::compat::MAP_FIXED_NOREPLACE as u64;
            for sysname in ["mmap", "mmap2"] {
                #[expect(clippy::disallowed_methods)]
                let syscall = ScmpSyscall::from_name(sysname).unwrap();
                ctx.add_rule_conditional(
                    ScmpAction::KillProcess,
                    syscall,
                    &[
                        scmp_cmp!($arg0 < *MMAP_MIN_ADDR),
                        scmp_cmp!($arg3 & MAP_FIXED == MAP_FIXED),
                    ],
                )?;
                ctx.add_rule_conditional(
                    ScmpAction::KillProcess,
                    syscall,
                    &[
                        scmp_cmp!($arg0 < *MMAP_MIN_ADDR),
                        scmp_cmp!($arg3 & MAP_FIXED_NOREPLACE == MAP_FIXED_NOREPLACE),
                    ],
                )?;
            }
        }

        // Restriction 3: Prohibit attempts to create memory mappings
        // that are writable and executable at the same time, or to
        // change existing memory mappings to become executable, or
        // mapping shared memory segments as executable.
        // Note, mips requires executable stack so we skip on this arch.
        #[cfg(not(any(
            target_arch = "mips",
            target_arch = "mips32r6",
            target_arch = "mips64",
            target_arch = "mips64r6"
        )))]
        if restrict_exec_memory {
            const W: u64 = libc::PROT_WRITE as u64;
            const X: u64 = libc::PROT_EXEC as u64;
            const WX: u64 = W | X;
            const MAP_A: u64 = libc::MAP_ANONYMOUS as u64;
            const MAP_S: u64 = libc::MAP_SHARED as u64;
            for sysname in ["mmap", "mmap2"] {
                // Prevent writable and executable memory.
                #[expect(clippy::disallowed_methods)]
                let syscall = ScmpSyscall::from_name(sysname).unwrap();
                ctx.add_rule_conditional(
                    ScmpAction::KillProcess,
                    syscall,
                    &[scmp_cmp!($arg2 & WX == WX)],
                )?;

                // Prevent executable anonymous memory.
                ctx.add_rule_conditional(
                    ScmpAction::KillProcess,
                    syscall,
                    &[scmp_cmp!($arg2 & X == X), scmp_cmp!($arg3 & MAP_A == MAP_A)],
                )?;

                // Prevent executable shared memory.
                ctx.add_rule_conditional(
                    ScmpAction::KillProcess,
                    syscall,
                    &[scmp_cmp!($arg2 & X == X), scmp_cmp!($arg3 & MAP_S == MAP_S)],
                )?;
            }

            for sysname in ["mprotect", "pkey_mprotect"] {
                #[expect(clippy::disallowed_methods)]
                let syscall = ScmpSyscall::from_name(sysname).unwrap();
                ctx.add_rule_conditional(
                    ScmpAction::KillProcess,
                    syscall,
                    &[scmp_cmp!($arg2 & X == X)],
                )?;
            }
        }

        // Restriction 4: Deny creation of block and character devices.
        // Terminate the process on match because legit use cases inside
        // the sandbox are rare. For those cases we provide the options
        // trace/allow_unsafe_mkbdev:1 and trace/allow_unsafe_mkcdev:1.
        if restrict_mkbdev {
            scmp_add_mknod(&mut ctx, ScmpAction::KillProcess, FileType::Blk)?;
        }
        if restrict_mkcdev {
            scmp_add_mknod(&mut ctx, ScmpAction::KillProcess, FileType::Chr)?;
        }

        // Restriction 5: Kill process on message queue syscalls.
        // Use trace/allow_unsafe_msgqueue:1 to disable.
        if restrict_msgqueue {
            for sysname in MSGQUEUE_SYSCALLS {
                if let Ok(syscall) = ScmpSyscall::from_name(sysname) {
                    ctx.add_rule(ScmpAction::KillProcess, syscall)?;
                } else {
                    info!("ctx": "confine", "op": "deny_syscall",
                        "msg": format!("invalid or unsupported syscall {sysname}"));
                }
            }
        }

        // Restriction 6: Kill process on shared memory syscalls.
        // Use trace/allow_unsafe_shm:1 to disable.
        if restrict_shm {
            for sysname in SHM_SYSCALLS {
                if let Ok(syscall) = ScmpSyscall::from_name(sysname) {
                    ctx.add_rule(ScmpAction::KillProcess, syscall)?;
                } else {
                    info!("ctx": "confine", "op": "deny_syscall",
                        "msg": format!("invalid or unsupported syscall {sysname}"));
                }
            }
        }

        // Restriction 7: Deny unsafe set-id system calls.
        // Deny with Errno=0 -> Turn the system calls into no-op.
        // This is for compatibility, e.g. postgres invokes
        // setgroups before setuid and aborts on failure.
        for sysname in UNSAFE_ID_SYSCALLS {
            if let Ok(syscall) = ScmpSyscall::from_name(sysname) {
                ctx.add_rule(ScmpAction::Errno(0), syscall)?;
            } else {
                info!("ctx": "confine", "op": "noop_syscall",
                    "msg": format!("invalid or unsupported syscall {sysname}"));
            }
        }

        // Restriction 8: Deny transition to privileged {U,G}IDs.
        // Step 1: arg0 for UIDs.
        for sysname in &[
            "setuid",
            "setuid32",
            "setreuid",
            "setreuid32",
            "setresuid",
            "setresuid32",
        ] {
            if let Ok(syscall) = ScmpSyscall::from_name(sysname) {
                ctx.add_rule_conditional(
                    ScmpAction::Errno(libc::EACCES),
                    syscall,
                    &[scmp_cmp!($arg0 <= u64::from(UID_MIN.as_raw()))],
                )?;
            } else {
                info!("ctx": "confine", "op": "deny_syscall",
                    "msg": format!("invalid or unsupported syscall {sysname}"));
            }
        }

        // Step 2: arg0 for GIDs.
        for sysname in &[
            "setgid",
            "setgid32",
            "setregid",
            "setregid32",
            "setresgid",
            "setresgid32",
        ] {
            if let Ok(syscall) = ScmpSyscall::from_name(sysname) {
                ctx.add_rule_conditional(
                    ScmpAction::Errno(libc::EACCES),
                    syscall,
                    &[scmp_cmp!($arg0 <= u64::from(GID_MIN.as_raw()))],
                )?;
            } else {
                info!("ctx": "confine", "op": "deny_syscall",
                    "msg": format!("invalid or unsupported syscall {sysname}"));
            }
        }

        // Step 3: arg1 for UIDs.
        for sysname in &["setreuid", "setreuid32", "setresuid", "setresuid32"] {
            if let Ok(syscall) = ScmpSyscall::from_name(sysname) {
                ctx.add_rule_conditional(
                    ScmpAction::Errno(libc::EACCES),
                    syscall,
                    &[scmp_cmp!($arg1 <= u64::from(UID_MIN.as_raw()))],
                )?;
            } else {
                info!("ctx": "confine", "op": "deny_syscall",
                    "msg": format!("invalid or unsupported syscall {sysname}"));
            }
        }

        // Step 4: arg1 for GIDs.
        for sysname in &["setregid", "setregid32", "setresgid", "setresgid32"] {
            if let Ok(syscall) = ScmpSyscall::from_name(sysname) {
                ctx.add_rule_conditional(
                    ScmpAction::Errno(libc::EACCES),
                    syscall,
                    &[scmp_cmp!($arg1 <= u64::from(GID_MIN.as_raw()))],
                )?;
            } else {
                info!("ctx": "confine", "op": "deny_syscall",
                    "msg": format!("invalid or unsupported syscall {sysname}"));
            }
        }

        // Step 5: arg2 for UIDS.
        for sysname in &["setresuid", "setresuid32"] {
            if let Ok(syscall) = ScmpSyscall::from_name(sysname) {
                ctx.add_rule_conditional(
                    ScmpAction::Errno(libc::EACCES),
                    syscall,
                    &[scmp_cmp!($arg2 <= u64::from(UID_MIN.as_raw()))],
                )?;
            } else {
                info!("ctx": "confine", "op": "deny_syscall",
                    "msg": format!("invalid or unsupported syscall {sysname}"));
            }
        }

        // Step 6: arg2 for GIDs.
        for sysname in &["setresgid", "setresgid32"] {
            if let Ok(syscall) = ScmpSyscall::from_name(sysname) {
                ctx.add_rule_conditional(
                    ScmpAction::Errno(libc::EACCES),
                    syscall,
                    &[scmp_cmp!($arg2 <= u64::from(GID_MIN.as_raw()))],
                )?;
            } else {
                info!("ctx": "confine", "op": "deny_syscall",
                    "msg": format!("invalid or unsupported syscall {sysname}"));
            }
        }

        // Restriction 9: Restrict sub namespace creation.
        let deny_namespaces = sandbox.flags.denied_namespaces();
        let (namespace_act, namespace_acl) = if deny_namespaces != 0 {
            // Deny clone3 with ENOSYS for compatibility.
            confine_scmp_clone3(&mut ctx)?;

            let sysname_ns = "setns";
            #[expect(clippy::disallowed_methods)]
            let syscall_ns = ScmpSyscall::from_name(sysname_ns).unwrap();
            let deny_all = deny_namespaces & NAMESPACE_FLAGS_ALL == NAMESPACE_FLAGS_ALL;
            let namespace_act = if deny_all {
                // If every single kind of namespace shall be
                // prohibited, then let's block the whole setns()
                // syscall altogether.
                ctx.add_rule(ScmpAction::Errno(libc::EPERM), syscall_ns)?;
                Action::Deny
            } else {
                // Otherwise, block only the invocations with the
                // appropriate flags in the loop below, but also the
                // special invocation with a zero flags argument, right
                // here.
                ctx.add_rule_conditional(
                    ScmpAction::Errno(libc::EPERM),
                    syscall_ns,
                    &[scmp_cmp!($arg1 == 0)],
                )?;
                Action::Filter
            };

            let sysname_cl = "clone";
            let sysname_un = "unshare";
            #[expect(clippy::disallowed_methods)]
            let syscall_cl = ScmpSyscall::from_name(sysname_cl).unwrap();
            #[expect(clippy::disallowed_methods)]
            let syscall_un = ScmpSyscall::from_name(sysname_un).unwrap();
            let mut ns_deny = vec![];
            let mut ns_allow = vec![];
            for flag in NAMESPACE_FLAGS {
                if deny_namespaces & flag == 0 {
                    ns_allow.push(nsflag_name(*flag));
                    continue;
                } else {
                    ns_deny.push(nsflag_name(*flag));
                }

                #[expect(clippy::cast_sign_loss)]
                let flag = *flag as u64;
                ctx.add_rule_conditional(
                    ScmpAction::Errno(libc::EPERM),
                    syscall_un,
                    &[scmp_cmp!($arg0 & flag == flag)],
                )?;

                // On s390/s390x the first two parameters to clone are switched.
                if !cfg!(target_arch = "s390x") {
                    ctx.add_rule_conditional(
                        ScmpAction::Errno(libc::EPERM),
                        syscall_cl,
                        &[scmp_cmp!($arg0 & flag == flag)],
                    )?;
                } else {
                    ctx.add_rule_conditional(
                        ScmpAction::Errno(libc::EPERM),
                        syscall_cl,
                        &[scmp_cmp!($arg1 & flag == flag)],
                    )?;
                }

                if !deny_all {
                    ctx.add_rule_conditional(
                        ScmpAction::Errno(libc::EPERM),
                        syscall_ns,
                        &[scmp_cmp!($arg1 & flag == flag)],
                    )?;
                }
            }

            if namespace_act == Action::Deny {
                (namespace_act, None)
            } else {
                (namespace_act, Some((ns_deny, ns_allow)))
            }
        } else {
            (Action::Allow, None)
        };

        match (namespace_act, namespace_acl) {
            (Action::Allow, _) => info!(
                "ctx": "restrict_namespaces",
                "msg": "namespace creation allowed",
                "ns_allow": NAMESPACE_NAMES),
            (Action::Deny, _) => info!(
                "ctx": "restrict_namespaces",
                "msg": "namespace creation denied",
                "ns_deny": NAMESPACE_NAMES),
            (_, Some((acl_deny, acl_allow))) => info!(
                "ctx": "restrict_namespaces",
                "msg": "namespace creation filtered",
                "ns_deny": acl_deny,
                "ns_allow": acl_allow),
            _ => unreachable!(),
        };

        // Export seccomp rules if requested.
        let export = match ExportMode::from_env() {
            Some(ExportMode::BerkeleyPacketFilter) => {
                #[expect(clippy::disallowed_methods)]
                let file = OpenOptions::new()
                    .write(true)
                    .create_new(true)
                    .mode(0o400)
                    .open("syd_parent.bpf")?;
                ctx.export_bpf(file)?;
                true
            }
            Some(ExportMode::PseudoFiltercode) => {
                // Lock stdout to prevent concurrent access.
                let mut stdout = std::io::stdout().lock();

                rwriteln!(stdout, "# Syd parent rules")?;
                rwrite!(stdout, "{}", seccomp_export_pfc(&ctx)?)?;
                true
            }
            _ => false,
        };

        // Load the seccomp filter unless:
        // a. We're running in debug mode with SYD_SKIP_SCMP=1.
        // b. We're exporting seccomp filters
        if !export && env::var_os(ENV_SKIP_SCMP).is_none() {
            ctx.load()?;
        }

        // Restriction 10: Networking restrictions:
        // 1. Restrict socket domains based on flags.
        // 2. Restrict MSG_OOB for recv(2) and send(2) family.
        //
        // SAFETY: socket may not exist on every architecture.
        // On some arches such as x86 there's the socketcall
        // system call which involves a pointer indirection
        // for the domain argument therefore on these arches
        // we rely on our socketcall seccomp-notify hook to
        // achieve the same effect.
        if !restrict_socket && !restrict_kcapi && !restrict_oob {
            return Ok(()); // No need for an additional socket filter.
        } else if SydArch::has_native_socketcall() {
            // Export seccomp rules if requested.
            if matches!(ExportMode::from_env(), Some(ExportMode::PseudoFiltercode)) {
                // Lock stdout to prevent concurrent access.
                let mut stdout = std::io::stdout().lock();

                rwriteln!(stdout, "# Syd socket rules")?;
                rwriteln!(stdout, "# Not applicable on this architecture!")?;
            }
        } else {
            let mut ctx = ScmpFilterContext::new(ScmpAction::Allow)?;
            // 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(ssb)?;
            // Synchronize filter to all threads.
            ctx.set_ctl_tsync(true)?;
            // SAFETY: We do allow bad architecture, see the comment above.
            ctx.set_act_badarch(ScmpAction::Allow)?;
            // Use a binary tree sorted by syscall number if possible.
            let _ = ctx.set_ctl_optimize(2);
            // SAFETY: Do not add supported architectures, this filter
            // is for the native architecture only.
            // seccomp_add_architectures(&mut ctx)?;
            // We don't want ECANCELED, we want actual errnos.
            let _ = ctx.set_api_sysrawrc(true);

            #[expect(clippy::disallowed_methods)]
            let syscall = ScmpSyscall::from_name("socket").unwrap();
            if restrict_socket {
                // TODO: libc:: should define this!
                const AF_MAX: libc::c_int = 45;
                // Only allow AF_{UNIX,INET,INET6,NETLINK} by default
                let mut allow_domains: SydHashSet<libc::c_int> = SydHashSet::from_iter([
                    libc::AF_UNIX,
                    libc::AF_INET,
                    libc::AF_INET6,
                    libc::AF_NETLINK,
                ]);
                if !restrict_kcapi {
                    // Allow KCAPI as well.
                    allow_domains.insert(libc::AF_ALG);
                }

                for domain in 0..AF_MAX {
                    if allow_domains.contains(&domain) {
                        continue;
                    }
                    #[expect(clippy::cast_sign_loss)]
                    ctx.add_rule_conditional(
                        ScmpAction::Errno(libc::EAFNOSUPPORT),
                        syscall,
                        &[scmp_cmp!($arg0 == domain as u64)],
                    )?;
                }

                // SAFETY: Guard against new AF_* that may be added in the future.
                ctx.add_rule_conditional(
                    ScmpAction::Errno(libc::EINVAL),
                    syscall,
                    &[scmp_cmp!($arg0 >= AF_MAX as u64)],
                )?;

                // SAFETY: Restrict AF_NETLINK families.
                //
                // Include NETLINK_SOCK_DIAG into the set by default,
                // which is required by syd::fs::peer_inode at getsockopt(2)
                // boundary.
                let mut allow_netlink_families = sandbox.netlink_families;
                allow_netlink_families.insert(NetlinkFamily::NETLINK_SOCK_DIAG);
                if allow_netlink_families.is_empty() {
                    // No netlink families were allowed, deny all of AF_NETLINK.
                    // See comment above on the usage of _exact.
                    ctx.add_rule_conditional(
                        ScmpAction::Errno(libc::EAFNOSUPPORT),
                        syscall,
                        &[scmp_cmp!($arg0 == libc::AF_NETLINK as u64)],
                    )?;
                } else {
                    let allow_netlink_families = allow_netlink_families.to_vec();
                    let netlink_family_max = NetlinkFamily::max();
                    for netlink_family in 0..netlink_family_max {
                        if allow_netlink_families.contains(&netlink_family) {
                            continue;
                        }
                        // See comment above on the usage of _exact.
                        #[expect(clippy::cast_sign_loss)]
                        ctx.add_rule_conditional(
                            ScmpAction::Errno(libc::EAFNOSUPPORT),
                            syscall,
                            &[
                                scmp_cmp!($arg0 == libc::AF_NETLINK as u64),
                                scmp_cmp!($arg2 == netlink_family as u64),
                            ],
                        )?;
                    }
                    // SAFETY: Guard against new netlink families that may be added in the future.
                    #[expect(clippy::cast_sign_loss)]
                    ctx.add_rule_conditional(
                        ScmpAction::Errno(libc::EINVAL),
                        syscall,
                        &[
                            scmp_cmp!($arg0 == libc::AF_NETLINK as u64),
                            scmp_cmp!($arg2 > netlink_family_max as u64),
                        ],
                    )?;
                }
            } else if restrict_kcapi {
                ctx.add_rule_conditional(
                    ScmpAction::Errno(libc::EAFNOSUPPORT),
                    syscall,
                    &[scmp_cmp!($arg0 == libc::AF_ALG as u64)],
                )?;
            }

            if restrict_oob {
                confine_scmp_msg_oob(&mut ctx)?;
            }

            if restrict_setsockopt {
                confine_scmp_setsockopt(&mut ctx)?;
            }

            // Export seccomp rules if requested.
            let export = match ExportMode::from_env() {
                Some(ExportMode::BerkeleyPacketFilter) => {
                    #[expect(clippy::disallowed_methods)]
                    let file = OpenOptions::new()
                        .write(true)
                        .create_new(true)
                        .mode(0o400)
                        .open("syd_socket.bpf")?;
                    ctx.export_bpf(file)?;
                    true
                }
                Some(ExportMode::PseudoFiltercode) => {
                    // Lock stdout to prevent concurrent access.
                    let mut stdout = std::io::stdout().lock();

                    rwriteln!(stdout, "# Syd socket rules")?;
                    rwrite!(stdout, "{}", seccomp_export_pfc(&ctx)?)?;
                    true
                }
                _ => false,
            };

            // Load the seccomp filter unless:
            // a. We're running in debug mode with SYD_SKIP_SCMP=1.
            // b. We're exporting seccomp filters
            if !export && env::var_os(ENV_SKIP_SCMP).is_none() {
                ctx.load()?;
            }
        }

        Ok(())
    }

    /// Export a seccomp context as bpf or pfc.
    fn seccomp_export(&self, ctx: &ScmpFilterContext, mode: ExportMode) -> SydResult<()> {
        match mode {
            ExportMode::BerkeleyPacketFilter => {
                #[expect(clippy::disallowed_methods)]
                let file = OpenOptions::new()
                    .write(true)
                    .create_new(true)
                    .mode(0o400)
                    .open("syd_exec.bpf")?;
                Ok(ctx.export_bpf(file)?)
            }
            ExportMode::PseudoFiltercode => {
                // Lock stdout to prevent concurrent access.
                let mut stdout = std::io::stdout().lock();

                let libver = ScmpVersion::current()?;
                rwriteln!(
                    stdout,
                    "# Syd v{} seccomp rules generated by libseccomp v{}.{}.{}",
                    env!("CARGO_PKG_VERSION"),
                    libver.major,
                    libver.minor,
                    libver.micro
                )?;
                rwriteln!(
                    stdout,
                    "# API Version: {API_MAJOR_VERSION}.{API_MINOR_VERSION}"
                )?;

                #[expect(clippy::disallowed_methods)]
                let mut syscall_allow: Vec<_> = self
                    .sysallow
                    .iter()
                    .copied()
                    .map(|sys| sys.get_name().unwrap())
                    .collect();
                let syscall_notif: SydHashSet<String> = self.handlers
                    .into_iter()
                    .map(|(key, _)| key.0.to_string()) // Extract the name from keys
                    .collect(); // Collect names into a HashSet to ensure uniqueness
                let mut syscall_notif: Vec<String> = syscall_notif.into_iter().collect();
                syscall_allow.sort();
                syscall_notif.sort();

                rwriteln!(
                    stdout,
                    "# System calls with Action=ALLOW: {}",
                    syscall_allow.len()
                )?;
                rwriteln!(
                    stdout,
                    "# System calls with Action=NOTIF: {}",
                    syscall_notif.len()
                )?;

                let uidcall = GETID_SYSCALLS.to_vec().join(", ");
                let sandbox = self.sandbox.read().unwrap_or_else(|err| err.into_inner());
                rwriteln!(
                    stdout,
                    "# Fake Root: {} ( {uidcall} )",
                    if sandbox.flags.fake_root() {
                        "yes"
                    } else {
                        "no"
                    }
                )?;
                rwriteln!(
                    stdout,
                    "{}",
                    sandbox
                        .to_string()
                        .lines()
                        .map(|line| format!("# {line}"))
                        .collect::<Vec<_>>()
                        .join("\n")
                )?;
                drop(sandbox);

                rwriteln!(stdout, "# Action=NOTIF: {}", syscall_notif.len())?;
                for name in &syscall_notif {
                    rwriteln!(stdout, "#    - {name}")?;
                }
                rwriteln!(stdout, "# Action=ALLOW: {}", syscall_allow.len())?;
                for name in &syscall_allow {
                    rwriteln!(stdout, "#    - {name}")?;
                }

                // Finally, export PFC.
                rwrite!(stdout, "{}", seccomp_export_pfc(ctx)?)?;

                Ok(())
            }
        }
    }

    /// Logic for the supervise child thread.
    #[expect(clippy::type_complexity)]
    fn supervise(
        mut self,
        seccomp_fd: RawFd,
        flags: Flags,
    ) -> SydResult<(
        Arc<WorkerCache>,
        Arc<RwLock<Sandbox>>,
        SydJoinHandle<()>,
        Arc<AtomicBool>,
    )> {
        // Spawn the IPC thread.
        // We have already setup the socket pre-startup.
        if let Some(ipc_worker) = self.ipc_worker.take() {
            // Unwrap is fine because IPC setup is already done.
            #[expect(clippy::disallowed_methods)]
            let ipc_poll = ipc_worker
                .epoll
                .as_ref()
                .map(|fd| fd.0.as_raw_fd())
                .unwrap();
            #[expect(clippy::disallowed_methods)]
            let ipc_sock = ipc_worker.sock.as_ref().map(|fd| fd.as_raw_fd()).unwrap();

            // Set up a notification pipe and wait for
            // the IPC worker to start and unshare CLONE_F{ILE,}S.
            let (pipe_rd, pipe_wr) = pipe2(OFlag::O_CLOEXEC)?;

            // Spawn the IPC worker.
            ipc_worker.try_spawn((pipe_rd.as_raw_fd(), pipe_wr.as_raw_fd()))?;

            // Wait for startup notification.
            let mut buf = [0u8; 1];
            match retry_on_eintr(|| read(&pipe_rd, &mut buf[..]))? {
                0 => {
                    // IPC thread died before unshare.
                    // This should ideally never happen.
                    return Err(Errno::EIO.into());
                }
                1 if buf[0] == 42 => {
                    // IPC thread unshared successfully.
                    // We can go ahead and close our copies now.
                }
                _ => unreachable!("BUG: The meaning of life is not {:#x}!", buf[0]),
            }

            let _ = close(ipc_poll);
            let _ = close(ipc_sock);
        }

        // Set (process-wide) umask to 0.
        let _ = umask(Mode::empty());

        // Set main thread ID to decide in panic hook.
        log_init_main()?;

        // Set panic hook that plays well with the log-fd.
        if env::var_os(ENV_SKIP_SCMP).is_none() {
            log_set_panic_hook();
        }

        // Spawn the syscall handler pool.
        let should_exit = Arc::new(AtomicBool::new(false));
        let syshandler_pool = pool::ThreadPool::new(
            seccomp_fd,
            flags,
            *EMU_POOL_SIZE,
            EMU_KEEP_ALIVE,
            Arc::clone(&self.sandbox),
            Arc::clone(&self.handlers),
            Arc::clone(&should_exit),
            self.crypt_map.as_ref().map(Arc::clone),
        );

        // Clone the WorkerCache instance to pass to the main thread.
        let cache = Arc::clone(&syshandler_pool.cache);

        // Boot the thread pool!
        let monitor_handle = syshandler_pool.boot()?;

        // We return a clone of the cache and the sandbox to the caller.
        // exec-TOCTOU-mitigator uses this instance in the wait loop.
        Ok((
            cache,
            Arc::clone(&self.sandbox),
            monitor_handle,
            should_exit,
        ))
    }

    /// Wait for the child process to exit.
    /// It returns the exit code of the process.
    #[expect(clippy::cognitive_complexity)]
    fn wait(
        cache: Arc<WorkerCache>,
        sandbox: Arc<RwLock<Sandbox>>,
        monitor_handle: SydJoinHandle<()>,
        should_exit: Arc<AtomicBool>,
    ) -> SydResult<u8> {
        let my_sandbox = SandboxGuard::Read(sandbox.read().unwrap_or_else(|err| err.into_inner()));
        let child = my_sandbox.get_child_pid();
        let wait_all = my_sandbox.flags.exit_wait_all();
        let safe_setuid = my_sandbox.flags.allow_safe_setuid();
        let safe_setgid = my_sandbox.flags.allow_safe_setgid();
        let safe_setid = safe_setuid || safe_setgid;
        let ctx = Tracer::prepare_confine(
            *my_sandbox.flags,
            &my_sandbox.transit_uids,
            &my_sandbox.transit_gids,
        )?;
        drop(my_sandbox); // release the read lock.

        // SAFETY: Confine the main thread.
        // Honour dry-run when exporting.
        let dry_run = if env::var_os(ENV_SKIP_SCMP).is_some() || ExportMode::from_env().is_some() {
            error!("ctx": "confine", "op": "confine_main_thread",
                "msg": "main thread is running unconfined in debug mode");
            true
        } else {
            false
        };

        // Export seccomp rules if requested.
        match ExportMode::from_env() {
            Some(ExportMode::BerkeleyPacketFilter) => {
                #[expect(clippy::disallowed_methods)]
                let file = OpenOptions::new()
                    .write(true)
                    .create_new(true)
                    .mode(0o400)
                    .open("syd_main.bpf")?;
                ctx.export_bpf(file)?;
            }
            Some(ExportMode::PseudoFiltercode) => {
                // Lock stdout to prevent concurrent access.
                let mut stdout = std::io::stdout().lock();

                rwriteln!(stdout, "# Syd waiter rules")?;
                rwrite!(stdout, "{}", seccomp_export_pfc(&ctx)?)?;
            }
            _ => {}
        }

        // Unshare CLONE_FS|CLONE_FILES for isolation.
        unshare(CloneFlags::CLONE_FS | CloneFlags::CLONE_FILES)?;

        // SAFETY: The main (ptrace) worker needs to inherit:
        // 1. Static file descriptors.
        // 2. Log file descriptor.
        // We have to sort the set as the FDs are randomized.
        #[expect(clippy::cast_sign_loss)]
        let mut set = vec![
            ROOT_FD() as libc::c_uint,
            PROC_FD() as libc::c_uint,
            NULL_FD() as libc::c_uint,
            crate::log::LOG_FD.load(Ordering::Relaxed) as libc::c_uint,
        ];
        set.sort_unstable();
        closeexcept(&set)?;
        drop(set);

        // All set, load the filter!
        if !dry_run {
            ctx.load()?;

            info!("ctx": "confine", "op": "confine_main_thread",
                "msg": format!("main thread confined with{} SROP mitigation",
                    if safe_setid { "out" } else { "" }));
        }
        drop(ctx);

        // Run ptrace(2) main loop.
        let tracer = Tracer::new(cache, Arc::clone(&sandbox), Arc::clone(&should_exit));
        let exit_code = tracer.run(child, wait_all)?;

        // Wait for the syd-mon thread, which in turn waits for syd-aes threads,
        // for a clean, graceful, group-exit.
        monitor_handle.join().or(Err(Errno::EAGAIN))??;

        info!("ctx": "wait", "op": "exit",
            "msg": format!("return code {exit_code}, sandboxing ended!"),
            "code": exit_code);

        Ok(exit_code)
    }

    /// Run the supervisor, main entry point.
    #[expect(clippy::cognitive_complexity)]
    pub fn run(
        mut sandbox: Sandbox,
        pty_child: Option<OwnedFd>,
        argv0: &OsStr,
        argv: Vec<OsString>,
        arg0: Option<OsString>,
    ) -> SydResult<u8> {
        let (major, minor) = *KERNEL_VERSION;
        if major < 5 {
            error!("ctx": "run", "op": "check_kernel_version",
                "msg": "Your kernel version is too old.");
            return Err(Errno::ENOSYS.into());
        } else if major == 5 && minor < 5 {
            error!("ctx": "run", "op": "check_kernel_version",
                "msg": "Your kernel version is too old: Does not support SECCOMP_USER_NOTIF_FLAG_CONTINUE, ...");
            return Err(Errno::ENOSYS.into());
        } else if major == 5 && minor < 6 {
            error!("ctx": "run", "op": "check_kernel_version",
                "msg": "Your kernel version is too old: Does not support pidfd_getfd(2) and SECCOMP_IOCTL_NOTIF_ADDFD.");
            return Err(Errno::ENOSYS.into());
        } else if major == 5 && minor < 9 {
            error!("ctx": "run", "op": "check_kernel_version",
                "msg": "Your kernel version is too old: Does not support SECCOMP_IOCTL_NOTIF_ADDFD.");
            return Err(Errno::ENOSYS.into());
        } else if major == 5 && minor < 19 {
            error!("ctx": "run", "op": "check_kernel_version",
                "msg": "Your kernel version is too old: Does not support SECCOMP_FILTER_FLAG_WAIT_KILLABLE_RECV.");
            return Err(Errno::ENOSYS.into());
        }
        // Crypt sandboxing requires Linux>=6.2.
        if sandbox.enabled(Capability::CAP_CRYPT) && (major < 6 || (major == 6 && minor < 2)) {
            error!("ctx": "run", "op": "check_kernel_version",
                "msg": "Your kernel version is too old: Does not support ALG_SET_KEY_BY_KEY_SERIAL.",
                "tip": "turn Crypt sandboxing off with `sandbox/crypt:off'");
            return Err(Errno::ENOSYS.into());
        }
        info!("ctx": "run", "op": "check_kernel_version",
            "msg": "kernel version is compatible",
            "major": major, "minor": minor,
            "pidfd_thread": *HAVE_PIDFD_THREAD,
            "seccomp_sync": *HAVE_SECCOMP_USER_NOTIF_FD_SYNC_WAKE_UP,
            "mountid_uniq": *HAVE_STATX_MNT_ID_UNIQUE);

        // Set process name, ignore errors.
        let _ = set_name(c"syd_main");

        // Restore parent-death signal as necessary.
        let pds_old = get_pdeathsig().unwrap_or(None);
        let pds_new = if let Some(pds_new) = env::var_os(ENV_PDS) {
            let pds = if let Ok(pds_new) = btoi::<i32>(pds_new.as_bytes()) {
                Signal::try_from(pds_new).ok()
            } else if let Ok(pds_new) = std::str::from_utf8(pds_new.as_bytes()) {
                Signal::from_str(pds_new).ok()
            } else {
                None
            };

            if pds != pds_old {
                Some(pds)
            } else {
                None
            }
        } else {
            None
        };

        let pds_old = pds_old.map(|sig| sig as i32).unwrap_or(0);
        if let Some(pds_new) = pds_new {
            set_pdeathsig(pds_new)?;

            let pds_new = pds_new.map(|sig| sig as i32).unwrap_or(0);
            info!("ctx": "run", "op": "set_parent_death_signal",
                "msg": format!("set parent-death signal from {pds_old} to {pds_new}"),
                "sig": pds_new,
                "old": pds_old);
        } else {
            info!("ctx": "run", "op": "get_parent_death_signal",
                "msg": format!("parent-death signal is {pds_old}"),
                "sig": pds_old);
        }

        // SAFETY: Strip unsafe personality(2) flags.
        if !sandbox.flags.allow_unsafe_personality() {
            let mut pers = SydPersona::get()?;
            let unsafe_pers = pers.0 & UNSAFE_PERSONA;
            if unsafe_pers.is_empty() {
                info!("ctx": "run", "op": "strip_unsafe_personalities",
                    "msg": "found no unsafe personalities to strip",
                    "pers": pers.bits(), "pers_cur": pers);
            } else {
                pers.0.remove(UNSAFE_PERSONA);
                pers.set()?;
                let unsafe_pers = SydPersona(unsafe_pers);
                info!("ctx": "run", "op": "strip_unsafe_personalities",
                    "msg": format!("stripped unsafe personalities {unsafe_pers}"),
                    "pers": pers.bits(), "pers_cur": pers,
                    "pers_fix": unsafe_pers);
            }
        }

        // SAFETY:
        // 1. Set trace/allow_safe_set{u,g}id to true,
        //    if at least one {U,G}ID transit was defined.
        // 2. Return ENODATA if trace/allow_safe_set{u,g}id
        //    was set to true but no {U,G}ID transit was defined.
        // 3. Return ENODATA if trace/allow_safe_set{u,g}id
        //    was set to true but no {U,G}ID transit was defined
        //    for the current user/group.
        if !sandbox.transit_uids.is_empty() {
            sandbox.flags.insert(Flags::FL_ALLOW_SAFE_SETUID);
        } else if sandbox.flags.allow_safe_setuid() {
            return Err(Errno::ENODATA.into());
        }
        if !sandbox.transit_gids.is_empty() {
            sandbox.flags.insert(Flags::FL_ALLOW_SAFE_SETGID);
        } else if sandbox.flags.allow_safe_setgid() {
            return Err(Errno::ENODATA.into());
        }
        let uid = Uid::current();
        if sandbox.flags.allow_safe_setuid()
            && !sandbox.transit_uids.iter().any(|(src, _)| *src == uid)
        {
            return Err(Errno::ENODATA.into());
        }
        let gid = Gid::current();
        if sandbox.flags.allow_safe_setgid()
            && !sandbox.transit_gids.iter().any(|(src, _)| *src == gid)
        {
            return Err(Errno::ENODATA.into());
        }

        // SAFETY: Set crypt sandboxing on if:
        // a. Encryption key was supplied.
        // b. Crypt sandboxing list was populated.
        //
        // This avoids the surprising case where a user
        // supplies a key/acl but forgets to turn Crypt
        // sandboxing on in which case the files marked
        // for encryption would silently be written
        // unencrypted.
        if sandbox.must_crypt() {
            sandbox.state.insert(Capability::CAP_CRYPT);
        }

        // SAFETY: Set memory sandboxing on if:
        // a. mem/max was set to a non-zero value.
        // b. mem/vm_max was set to a non-zero value.
        if sandbox.mem_max > 0 || sandbox.mem_vm_max > 0 {
            sandbox.state.insert(Capability::CAP_MEM);
        }

        // SAFETY: Set pid sandboxing on if:
        // pid/max was set to a non-zero value.
        if sandbox.pid_max > 0 {
            sandbox.state.insert(Capability::CAP_PID);
        }

        // If Crypt Sandboxing is on.
        // 1. Validate session keyring is attached to the user keyring.
        // 2. Set allow_safe_kcapi and exit_wait_all flags.
        if sandbox.enabled(Capability::CAP_CRYPT) {
            if let Err(errno) = key_ring_validate() {
                error!("ctx": "run", "op": "check_keyrings", "err": errno as i32,
                    "msg": format!("session keyring isn't attached to the user keyring: {errno}"),
                    "tip": "reconnect or start a fresh login session");
                return Err(errno.into());
            }
            sandbox.flags.insert(Flags::FL_ALLOW_SAFE_KCAPI);
            sandbox.flags.insert(Flags::FL_EXIT_WAIT_ALL);
        }

        // Check MMAP_MIN_ADDR.
        // We must initialize this LazyLock before sandbox is locked.
        let mmap_min_addr = *MMAP_MIN_ADDR;
        info!("ctx": "run", "op": "check_mmap_min_addr", "min_addr": mmap_min_addr,
            "msg": format!("set mmap minimum address to {mmap_min_addr} bytes"));

        // Set process and i/o prorities.
        // See the "Process Priority and Resource Management" section of the syd(7) manual page.
        let restrict_nice = !sandbox.flags.allow_unsafe_nice();
        if restrict_nice {
            // SAFETY: Step 1: Set thread priority to a low value.
            match Errno::result(unsafe { libc::setpriority(libc::PRIO_PROCESS, 0, 20) }) {
                Ok(_) => {
                    info!("ctx": "run", "op": "set_program_scheduling_priority",
                        "msg": "set program scheduling priority to 20",
                        "val": 20);
                }
                Err(errno @ Errno::ENOSYS) => {
                    info!("ctx": "run", "op": "set_program_scheduling_priority",
                        "msg": format!("setpriority error: {errno}"),
                        "err": errno as i32);
                }
                Err(errno) => {
                    error!("ctx": "run", "op": "set_program_scheduling_priority",
                        "msg": format!("setpriority error: {errno}"),
                        "err": errno as i32);
                }
            }

            // SAFETY: Step 2: Set CPU scheduling priority to idle.
            match set_cpu_priority_idle() {
                Ok(_) => {
                    info!("ctx": "run", "op": "set_cpu_scheduling_priority",
                        "msg": "set CPU scheduling priority to idle",
                        "val": "idle");
                }
                Err(errno @ Errno::ENOSYS) => {
                    info!("ctx": "run", "op": "set_cpu_scheduling_priority",
                        "msg": format!("sched_setscheduler error: {errno}"),
                        "err": errno as i32);
                }
                Err(errno) => {
                    error!("ctx": "run", "op": "set_cpu_scheduling_priority",
                        "msg": format!("sched_setscheduler error: {errno}"),
                        "err": errno as i32);
                }
            }

            // SAFETY: Step 3: Set I/O priority to idle.
            match set_io_priority_idle() {
                Ok(_) => {
                    info!("ctx": "run", "op": "set_io_scheduling_priority",
                        "msg": "set i/o scheduling priority to idle",
                        "val": "idle");
                }
                Err(errno @ Errno::ENOSYS) => {
                    info!("ctx": "run", "op": "set_io_scheduling_priority",
                        "msg": format!("ioprio_set error: {errno}"),
                        "err": errno as i32);
                }
                Err(errno) => {
                    error!("ctx": "run", "op": "set_io_scheduling_priority",
                        "msg": format!("ioprio_set error: {errno}"),
                        "err": errno as i32);
                }
            }
        }

        // Create an IPC worker if requested.
        // We do it late here to ensure syd_ipc shares namespaces,
        // however we also do it before bind mounts and Landlock
        // to give privileged access to syd_ipc which will confine
        // itself.
        env::remove_var(ENV_IPC_POLL_FD);
        env::remove_var(ENV_IPC_UNIX_FD);
        let ipc_worker = if sandbox.locked() {
            // Sandbox is locked, no need to spawn IPC.
            None
        } else if let Some(ipc_path) = sandbox
            .ipc
            .as_ref()
            .map(Cow::Borrowed)
            .or_else(|| env::var_os(ENV_IPC).map(XPathBuf::from).map(Cow::Owned))
        {
            // `ipc` command implies `lock:ipc`, unless the lock
            // is set to `exec` or `off` explicitly.
            if sandbox.lock.is_none() {
                sandbox.lock = Some(LockState::Ipc);
            }

            // Initialize IPC worker.
            let mut ipc = IpcWorker::new(
                &ipc_path,
                sandbox.ipc_uid,
                sandbox.ipc_gid,
                *sandbox.flags,
                &sandbox.transit_uids,
                &sandbox.transit_gids,
            );

            // Update sandbox IPC socket from environment.
            if let Cow::Owned(ipc_path) = ipc_path {
                sandbox.ipc = Some(ipc_path);
            }

            // Setup IPC worker.
            ipc.setup()?;

            Some(ipc)
        } else {
            None
        };

        // Prepare the command to execute.
        // We create it early here so dynamic library loading
        // works even if we mount the owning fs noexec later.
        let mut command = match crate::unshare::Command::new(argv0) {
            Ok(command) => command,
            Err(errno) => return Ok(u8::try_from(errno as i32).unwrap_or(127)),
        };
        if sandbox.flags.allow_unsafe_caps() || sandbox.flags.allow_unsafe_ptrace() {
            // Keep CAP_SYS_PTRACE in the sandbox process.
            command.keep(true);
        }
        if !sandbox.flags.allow_unsafe_ptrace() {
            // Exec TOCTOU mitigation.
            command.stop(true);
        }
        if sandbox.flags.allow_unsafe_exec_speculative() {
            // Disable Speculative Store Bypass mitigations
            // for seccomp(2) filters with trace/allow_unsafe_exec_speculative:1
            command.ssb(true);
        }
        if !sandbox.skip_append() {
            // Enable append-only mitigations.
            command.append_only(true);
        }
        if !sandbox.flags.allow_unsafe_kptr() {
            // Enable kernel pointer in syscall arguments mitigations.
            command.restrict_kptr(true);
        }
        if !sandbox.flags.allow_unsafe_prlimit() {
            // Hint prlimit(2) is enabled by seccomp(2),
            // so that resource limits can be applied late.
            command.restrict_prlimit(true);
        }

        // Set command line arguments and process name.
        command.args(&argv);
        if let Some(ref arg0) = arg0 {
            command.arg0(arg0);
        }

        // Deny access to timestamp counter as necessary.
        command.deny_tsc(sandbox.flags.deny_tsc());

        // Make group leader for signal safety as necessary.
        command.make_group_leader(!sandbox.flags.allow_unsafe_pgrp());

        // Apply resource limits as necessary.
        command.rlimit_as(sandbox.rlimit_as);
        command.rlimit_core(sandbox.rlimit_core);
        command.rlimit_cpu(sandbox.rlimit_cpu);
        command.rlimit_data(sandbox.rlimit_data);
        command.rlimit_fsize(sandbox.rlimit_fsize);
        command.rlimit_memlock(sandbox.rlimit_memlock);
        command.rlimit_msgqueue(sandbox.rlimit_msgqueue);
        command.rlimit_nice(sandbox.rlimit_nice);
        command.rlimit_nofile(sandbox.rlimit_nofile);
        command.rlimit_nproc(sandbox.rlimit_nproc);
        command.rlimit_rtprio(sandbox.rlimit_rtprio);
        command.rlimit_rttime(sandbox.rlimit_rttime);
        command.rlimit_sigpending(sandbox.rlimit_sigpending);
        command.rlimit_stack(sandbox.rlimit_stack);

        // Set up mount namespace (after fork).
        if sandbox.flags.unshare_mount() {
            let bind_mounts = sandbox.collect_bind_mounts();
            ns_setup_mnt(
                sandbox.root.as_deref(),
                bind_mounts.as_deref(),
                !sandbox.flags.allow_unsafe_proc_files(),
            )?;
        }

        // Clean up the environment as necessary.
        sandbox.filter_env();

        // SAFETY: Ensure randomized timer is initialized as necessary.
        let restrict_sysinfo = !sandbox.flags.allow_unsafe_sysinfo();
        if restrict_sysinfo {
            timer_init(sandbox.flags.unshare_time())?;
        }

        // SAFETY: Ensure the static file descriptors are open
        // before sandboxing starts but after the mounts are
        // processed. This comes with two limitations:
        // (1) `proc_init` is called before Landlock sandbox is applied.
        // (2) `proc_init` is called before sandbox process startup.
        //
        // (1) is necessary because otherwise we'd require
        //     ReadDir access to `/`, aka rootfs, from Landlock
        //     thereby rendering the ReadDir category effectively
        //     useless.
        // (2) Syd is included into the same Landlock sandbox
        //     as the sandbox process therefore to accomplish (1)
        //     we must open static file descriptors exactly here.
        //
        // The rationale behind pre-opening rootfs is to prevent
        // root escapes utilizing RESOLVE_BENEATH within the path
        // canonicalizer. See syd::fs::safe_canonicalize for more
        // information.
        proc_init().inspect_err(|errno| {
            error!("ctx": "run", "op": "open_procfs",
                "msg": format!("failed to open /proc at startup: {errno}"),
                "err": *errno as i32);
        })?;

        // SAFETY: Make system mappings xonly and seal them.
        // This must run after proc_init.
        // This is best effort because it's not supported on all arches.
        if !sandbox.flags.allow_unsafe_noxom() {
            match confine_executable_maps() {
                Ok(()) => {
                    info!("ctx": "run", "op": "seal_executable_maps",
                        "msg": "sealed executable system mappings");
                }
                Err(errno) => {
                    info!("ctx": "run", "op": "seal_executable_maps", "err": errno as i32,
                        "msg": format!("seal executable system mappings failed: {errno}"));
                }
            }
        }

        // Set up the Landlock sandbox if requested. Note,
        // we set it up here before spawning the child so as to
        // include the Syd sandbox threads into the sandbox as
        // well. This is done for added security.
        //
        // Landlock errors are not fatal without default/lock:kill.
        sandbox.apply_landlock()?;

        // Initialize sandbox supervisor.
        let supervisor = Supervisor::new(sandbox, pty_child, ipc_worker)?;

        // Start profiling if requested.
        #[cfg(feature = "prof")]
        if let Some(val) = env::var_os("SYD_PROF") {
            match val.as_bytes() {
                b"cpu" => crate::start_cpu_profile("main"),
                b"mem" => crate::start_mem_profile("main"),
                _ => {}
            }
        };

        // Spawn the program under sandbox.
        let log = if log_enabled!(LogLevel::Info) {
            let cmd = arg0
                .map(XPathBuf::from)
                .unwrap_or_else(|| XPathBuf::from(argv0.to_os_string()));
            let args = argv.into_iter().map(XPathBuf::from).collect::<Vec<_>>();
            Some((cmd, args))
        } else {
            None
        };
        let (cache, sandbox, crypt_handle, should_exit) = match supervisor.spawn(command) {
            Ok(result) => {
                if let Some((cmd, args)) = log {
                    info!("ctx": "run", "op": "run_command",
                        "msg": format!("spawned `{cmd}' with arguments {args:?}"),
                        "cmd": cmd, "argv": args);
                }
                result
            }
            Err(error) => {
                let errno = Errno::last();
                if let Some((cmd, args)) = log {
                    info!("ctx": "run", "op": "run_command",
                        "msg": format!("spawn error executing `{cmd}': {errno}"),
                        "err": errno as i32, "cmd": cmd, "argv": args);
                }
                errno.set();
                return Err(error);
            }
        };

        // Wait for the process to exit and return the same error code.
        let result =
            Supervisor::wait(cache, sandbox, crypt_handle, should_exit).inspect_err(|error| {
                Errno::set_raw(error.errno().map(|e| e as i32).unwrap_or(libc::ENOSYS));
            });

        // End profiling if requested.
        #[cfg(feature = "prof")]
        if let Some(val) = env::var_os("SYD_PROF") {
            match val.as_bytes() {
                b"cpu" => crate::stop_cpu_profile(),
                b"mem" => {
                    crate::dump_mem_profile("main");
                    crate::stop_mem_profile();
                }
                _ => {}
            }
        }

        // Finally return the result to the caller.
        if ExportMode::from_env().is_some() {
            // We're exporting, not sandboxing: return 0.
            Ok(0)
        } else {
            result
        }
    }
}
