//! The **sentry**: supermachine's no-virt execution backend (Linux/x86_64).
//!
//! Design: `docs/design/no-virt-sentry-2026-06-12.md`. Used where there is no
//! usable hardware virtualization ([`kvm_usable`] is false) — nested cloud guests,
//! customer sandboxes, CI runners — so KVM/HVF can't be used. This is the same
//! isolation posture as a microVM, built from process primitives:
//!
//!   * A sealed **CELL** runs the (hostile) guest natively under Syscall User
//!     Dispatch. It no longer calls the host kernel for privileged work — it is
//!     pinned by a seccomp allowlist to cell-LOCAL syscalls (memory/TLS/futex/exit)
//!     only; everything that touches a host resource is forbidden (the wall).
//!   * A per-tenant **SUPERVISOR** (Layer 2) in a separate address space behind the
//!     hardware page-table wall services the cell's delegated syscalls over a
//!     shared-memory ring, moving guest buffers with `process_vm_readv/writev`, and
//!     confining all paths to the guest's rootfs via `openat2(RESOLVE_IN_ROOT)`.
//!
//! Capabilities (validated end-to-end in `spikes/sentry` and ported here): static
//! and dynamic glibc binaries, threads, JIT (V8/node), file-backed mmap, mediated
//! networking, rootfs confinement, and `fork()`-from-zygote restore. Entry point:
//! [`run`]; backend selection: [`kvm_usable`].
//!
//! Linux/x86_64-only (raw syscall ABI + `ucontext` layout), gated in `lib.rs` like
//! [`crate::kvm`]. `SENTRY_TRACE=1` logs every delegated syscall.

#![allow(unsafe_op_in_unsafe_fn, unused_unsafe)]

use std::collections::{BTreeMap, HashMap};
use std::os::raw::{c_char, c_int, c_void};
use std::sync::atomic::{AtomicBool, AtomicI32, AtomicU32, AtomicU64, Ordering};
use std::sync::{Mutex, OnceLock};

// `state_snap` (the Stage-1 "C0" state-snapshot serialization spine: versioned,
// little-endian, bounds-checked on-disk container + shared identity/fd types),
// `proctree` (the C2a supervisor-owned vpid process-tree model), and `netstack`
// (the owned loopback snapshot codec) are PURE — std + `crate::snapshot_frame`,
// with NO host-syscall / SUD / libc dependency. So they live in the UN-GATED
// `crate::sentry_portable` module and compile + unit-test on EVERY host (mac/CI),
// not just the Linux box — which lets the NEV multi-process (#36) and
// disk-persistence (#30/C4) logic be built with fast local feedback. They are
// re-exported here so all in-sentry paths (`super::state_snap`,
// `crate::sentry::proctree`, …) are unchanged on Linux. See
// `docs/design/native-execution-vm-2026-06-15.md`.
pub use crate::sentry_portable::{netstack, proctree, state_snap};

/// C3 — the cell's address space captured as a recorded-VA memory dump (the
/// recorded-VA + `MAP_FIXED` restore mechanism warm-restore needs). Pure module;
/// the capture/restore orchestration that drives it is C4. See `memimage.rs`.
#[cfg(all(target_os = "linux", target_arch = "x86_64"))]
pub mod memimage;

// ─── SUD / layout ────────────────────────────────────────────────────────────
const PR_SET_SYSCALL_USER_DISPATCH: c_int = 59;
const PR_SYS_DISPATCH_ON: libc::c_ulong = 1;
// prctl(2) options — the subop allowlist for the cell-local SYS_PRCTL arm.
const PR_SET_PDEATHSIG: u64 = 1;
const PR_GET_DUMPABLE: u64 = 3;
const PR_SET_DUMPABLE: u64 = 4;
const PR_SET_NAME: u64 = 15;
const PR_GET_NAME: u64 = 16;
const PR_SET_SECCOMP: u64 = 22;
const PR_SET_CHILD_SUBREAPER_SUPERVISOR: libc::c_int = 36;
const PR_CAPBSET_READ: u64 = 23;
const PR_SET_NO_NEW_PRIVS: u64 = 38;
const PR_SET_PTRACER: u64 = 0x5961_6d61;
const PR_SET_VMA: u64 = 0x5356_4d41;
const PR_SET_VMA_ANON_NAME: u64 = 0;
const FILTER_BLOCK: u8 = 1;
static mut SELECTOR: u8 = FILTER_BLOCK;
// 128 GiB floor so large JIT/browser runtimes can reserve aligned code/cage
// ranges below the SUD-exempt host region after the loader has consumed its own
// high guest mappings. Guest code/data below this floor still traps through SUD;
// the loader's own code/libc map high (exempt).
const WINDOW_FLOOR: u64 = 0x20_0000_0000;
const USER_TOP: u64 = 0x0000_7fff_ffff_f000;
const EXE_DYN_BASE: u64 = 0x0040_0000; // load base for an ET_DYN main exe
                                       // ld.so base — MUST sit above the main exe's top. The old 0x0800_0000 (128 MiB)
                                       // assumed every exe fit below it; Chromium's exe alone spans ~224 MiB (its .text
                                       // runs past 0x0800_0000), so ld.so was mapped ON TOP OF Chromium code and a direct
                                       // `call` into a function above 128 MiB landed in ld.so's r-- header → SEGV_ACCERR.
                                       // 1 GiB gives the exe a 4× margin over Chromium; a still-larger exe is caught loud
                                       // by the EXE_SPAN_HI >= INTERP_BASE guard at load time rather than silently clobbered.
const INTERP_BASE: u64 = 0x4000_0000; // ld.so base — above the main exe (1 GiB)
const ARENA_TOP: u64 = WINDOW_FLOOR; // guest mmap arena grows DOWN from here
const ARENA_LIMIT: u64 = 0x4100_0000; // …down to here (above INTERP_BASE + ld.so)

// gregs indices (x86_64)
const REG_R8: usize = 0;
const REG_R9: usize = 1;
const REG_R10: usize = 2;
const REG_R11: usize = 3;
const REG_R12: usize = 4;
const REG_R13: usize = 5;
const REG_R14: usize = 6;
const REG_R15: usize = 7;
const REG_RDI: usize = 8;
const REG_RSI: usize = 9;
const REG_RBP: usize = 10;
const REG_RBX: usize = 11;
const REG_RDX: usize = 12;
const REG_RAX: usize = 13;
const REG_RCX: usize = 14;
const REG_RSP: usize = 15;
const REG_RIP: usize = 16;
const ARCH_SET_FS: u64 = 0x1002;
const ARCH_GET_FS: u64 = 0x1003;

// ─── syscall numbers ─────────────────────────────────────────────────────────
const SYS_READ: i64 = 0;
const SYS_WRITE: i64 = 1;
const SYS_OPEN: i64 = 2;
const SYS_CLOSE: i64 = 3;
const SYS_STAT: i64 = 4;
const SYS_FSTAT: i64 = 5;
const SYS_LSTAT: i64 = 6;
const SYS_LSEEK: i64 = 8;
const SYS_MMAP: i64 = 9;
const SYS_MPROTECT: i64 = 10;
const SYS_MUNMAP: i64 = 11;
const SYS_MSYNC: i64 = 26;
const SYS_BRK: i64 = 12;
const SYS_RT_SIGACTION: i64 = 13;
const SYS_RT_SIGPROCMASK: i64 = 14;
const SYS_RT_SIGRETURN: i64 = 15;
const SYS_PAUSE: i64 = 34;
const SYS_RT_SIGPENDING: i64 = 127;
const SYS_RT_SIGTIMEDWAIT: i64 = 128;
const SYS_RT_SIGSUSPEND: i64 = 130;
const SYS_IOCTL: i64 = 16;
const SYS_SHMGET: i64 = 29;
const SYS_SHMAT: i64 = 30;
const SYS_SHMCTL: i64 = 31;
const SYS_GETITIMER: i64 = 36;
const SYS_ALARM: i64 = 37;
const SYS_SETITIMER: i64 = 38;
const SYS_KILL: i64 = 62;
const SYS_CREAT: i64 = 85;
const SYS_PREAD64: i64 = 17;
const SYS_WRITEV: i64 = 20;
const SYS_ACCESS: i64 = 21;
const SYS_MREMAP: i64 = 25;
const SYS_MINCORE: i64 = 27;
const SYS_MADVISE: i64 = 28;
const SYS_CLONE: i64 = 56;
const SYS_CLONE3: i64 = 435;
const SYS_FORK: i64 = 57;
const SYS_VFORK: i64 = 58;
const SYS_EXECVE: i64 = 59;
const SYS_WAIT4: i64 = 61;
const SYS_SHMDT: i64 = 67;
const SIGCHLD_FLAG: u64 = 17;
const CLONE_VM: u64 = 0x0000_0100;
const CLONE_PIDFD: u64 = 0x0000_1000;
const CLONE_VFORK: u64 = 0x0000_4000;
const CLONE_THREAD: u64 = 0x0001_0000;
const CLONE_CLEAR_SIGHAND: u64 = 0x1_0000_0000;
const SYS_GETPID: i64 = 39;
const SYS_SENDFILE: i64 = 40;
const SYS_EXIT: i64 = 60;
const SYS_UNAME: i64 = 63;
const SYS_FCNTL: i64 = 72;
const SYS_FLOCK: i64 = 73;
// fcntl record-lock cmds whose arg is a GUEST pointer to `struct flock` (32B on
// x86_64). *GETLK is in/out; *SETLK[W] is in-only. F_SETLKW / F_OFD_SETLKW block.
const F_GETLK: u64 = 5;
const F_SETLK: u64 = 6;
const F_SETLKW: u64 = 7;
const F_OFD_GETLK: u64 = 36;
const F_OFD_SETLK: u64 = 37;
const F_OFD_SETLKW: u64 = 38;
const F_GETFD: u64 = 1;
const F_SETFD: u64 = 2;
const F_DUPFD_CLOEXEC: u64 = 1030;
const FD_CLOEXEC: i64 = 1;
/// `struct flock` size on x86_64 (short,short,pad,off_t,off_t,pid_t,pad).
const FLOCK_SZ: usize = 32;
const SYS_CHDIR: i64 = 80;
const SYS_FCHDIR: i64 = 81;
const SYS_GETCWD: i64 = 79;
const SYS_PRCTL: i64 = 157;
const SYS_READLINK: i64 = 89;
const SYS_UMASK: i64 = 95;
const SYS_GETRUSAGE: i64 = 98;
const SYS_SYSINFO: i64 = 99;
// times(tms* OUT): process CPU clock-ticks (utime/stime + children). 32B struct OUT;
// return value is clock_t (jiffies since an arbitrary epoch) — delegated like SYSINFO.
const SYS_TIMES: i64 = 100;
// waitid(idtype, id, siginfo* OUT, options, rusage* OUT?): the modern reaper that
// Go/Python/Node-on-modern-kernels use instead of wait4. CELL-LOCAL (not serviced):
// the child was forked IN the cell's own address space (see SYS_FORK in
// dispatch_simple), so only the cell is its parent — a host(waitid) from the
// supervisor would get -ECHILD. Mirrors the SYS_WAIT4 cell-local arm: direct host
// call fills the cell's own siginfo/rusage buffers, then delegate CTL_REAP for the
// reaped pid to free its supervisor-side slot + fd table. Reaped pid is read back
// from the siginfo buffer's si_pid field (offset 16 on x86_64), since waitid
// returns 0 (not the pid) on success.
const SYS_WAITID: i64 = 247;
/// `struct siginfo_t` size on x86_64 (the waitid OUT buffer the guest provides).
const SIGINFO_SZ: usize = 128;
/// Byte offset of `si_pid` within a SIGCHLD `siginfo_t` on x86_64 (after the
/// signo/errno/code header at 0..12 + 4 pad). Used to recover the reaped pid.
const SIGINFO_SI_PID_OFF: usize = 16;
// The si_pid read below stays within the guest-provided 128B siginfo buffer.
const _: () = assert!(SIGINFO_SI_PID_OFF + 4 <= SIGINFO_SZ);
/// `struct rusage` size on x86_64 (the getrusage / optional-waitid OUT buffer).
const RUSAGE_SZ: usize = 144;
/// waitid `options`: WNOWAIT (poll without reaping — must NOT send CTL_REAP).
const WNOWAIT: u64 = 0x0100_0000;
const SYS_GETUID: i64 = 102;
const SYS_GETGID: i64 = 104;
const SYS_SETUID: i64 = 105;
const SYS_SETGID: i64 = 106;
const SYS_GETGROUPS: i64 = 115;
const SYS_SETGROUPS: i64 = 116;
const SYS_SETREUID: i64 = 113;
const SYS_SETREGID: i64 = 114;
const SYS_SETRESUID: i64 = 117;
const SYS_GETRESUID: i64 = 118;
const SYS_SETRESGID: i64 = 119;
const SYS_GETRESGID: i64 = 120;
const SYS_SETFSUID: i64 = 122;
const SYS_SETFSGID: i64 = 123;
const SYS_GETPPID: i64 = 110;
const SYS_GETEUID: i64 = 107;
const SYS_GETEGID: i64 = 108;
// process-group / session: shells (job control), tini/dumb-init/s6, npm/yarn
// detached children, and the postgres postmaster all rely on these. They CHANGE
// or READ the CALLING process's pgrp/session, so they're serviced CELL-LOCAL
// (in dispatch_simple, which runs in the cell's own process) — never in
// service(), which would mutate the supervisor instead of the cell.
const SYS_SETPGID: i64 = 109;
const SYS_GETPGRP: i64 = 111;
const SYS_SETSID: i64 = 112;
const SYS_GETPGID: i64 = 121;
const SYS_GETSID: i64 = 124;
const SYS_GETTIMEOFDAY: i64 = 96;
const SYS_STATFS: i64 = 137;
const SYS_GETPRIORITY: i64 = 140;
const SYS_SETPRIORITY: i64 = 141;
const SYS_ARCH_PRCTL: i64 = 158;
const SYS_GETTID: i64 = 186;
const SYS_GETXATTR: i64 = 191;
const SYS_LGETXATTR: i64 = 192;
const SYS_FGETXATTR: i64 = 193;
const SYS_CAPGET: i64 = 125;
const SYS_CAPSET: i64 = 126;
const SYS_FUTEX: i64 = 202;
const SYS_TGKILL: i64 = 234;
const SYS_TKILL: i64 = 200;
const SYS_GETDENTS64: i64 = 217;
// event loop / timers / misc (libuv + V8)
const SYS_POLL: i64 = 7;
// ppoll/pselect6/select: glibc poll() routes through ppoll with NO fallback, and
// select() is still common; all three are converted to a host poll() here.
const SYS_PPOLL: i64 = 271;
const SYS_PSELECT6: i64 = 270;
const SYS_SELECT: i64 = 23;
const SYS_SCHED_YIELD: i64 = 24;
const SYS_SCHED_SETPARAM: i64 = 142;
const SYS_SCHED_GETPARAM: i64 = 143;
const SYS_SCHED_SETSCHEDULER: i64 = 144;
const SYS_SCHED_GETSCHEDULER: i64 = 145;
const SYS_SCHED_GET_PRIORITY_MAX: i64 = 146;
const SYS_SCHED_GET_PRIORITY_MIN: i64 = 147;
const SYS_SCHED_RR_GET_INTERVAL: i64 = 148;
const SYS_DUP: i64 = 32;
const SYS_DUP2: i64 = 33;
const SYS_NANOSLEEP: i64 = 35;
const SYS_FSYNC: i64 = 74;
const SYS_FDATASYNC: i64 = 75;
const SYS_FTRUNCATE: i64 = 77;
const SYS_SIGALTSTACK: i64 = 131;
const SYS_PIPE: i64 = 22;
const SYS_PIPE2: i64 = 293;
const SYS_TIME: i64 = 201;
const SYS_SCHED_SETAFFINITY: i64 = 203;
const SYS_SCHED_GETAFFINITY: i64 = 204;
const SYS_FADVISE64: i64 = 221;
const SYS_EPOLL_WAIT: i64 = 232;
const SYS_EPOLL_CTL: i64 = 233;
const SYS_CLOCK_GETRES: i64 = 229;
const SYS_CLOCK_NANOSLEEP: i64 = 230;
const SYS_EPOLL_PWAIT: i64 = 281;
const SYS_TIMERFD_CREATE: i64 = 283;
const SYS_TIMERFD_SETTIME: i64 = 286;
const SYS_TIMERFD_GETTIME: i64 = 287;
const SYS_EVENTFD2: i64 = 290;
const SYS_EPOLL_CREATE1: i64 = 291;
const SYS_GETCPU: i64 = 309;
const SYS_MEMBARRIER: i64 = 324;
const SYS_PKEY_MPROTECT: i64 = 329;
const SYS_PKEY_ALLOC: i64 = 330;
const SYS_PKEY_FREE: i64 = 331;
// file/IO accelerators (preallocate, positioned-vector I/O, zero-copy moves)
const SYS_PWRITE64: i64 = 18;
const SYS_FALLOCATE: i64 = 285;
const SYS_PREADV: i64 = 295;
const SYS_PWRITEV: i64 = 296;
const SYS_PREADV2: i64 = 327;
const SYS_PWRITEV2: i64 = 328;
const SYS_COPY_FILE_RANGE: i64 = 326;
const SYS_SPLICE: i64 = 275;
const SYS_TEE: i64 = 276;
const SYS_VMSPLICE: i64 = 278;
// networking — mediated by the supervisor (the TSI egress-policy control point)
const SYS_READV: i64 = 19;
const SYS_SOCKET: i64 = 41;
const SYS_CONNECT: i64 = 42;
const SYS_ACCEPT: i64 = 43;
const SYS_SENDTO: i64 = 44;
const SYS_RECVFROM: i64 = 45;
const SYS_SENDMSG: i64 = 46;
const SYS_RECVMSG: i64 = 47;
// vectored datagram batches: glibc DNS sends A+AAAA via sendmmsg, and recvmmsg
// is the matching scatter path. Each is the msg-arm looped over an mmsghdr array.
const SYS_RECVMMSG: i64 = 299;
const SYS_SENDMMSG: i64 = 307;
const SYS_SHUTDOWN: i64 = 48;
const SYS_BIND: i64 = 49;
const SYS_LISTEN: i64 = 50;
const SYS_GETSOCKNAME: i64 = 51;
const SYS_GETPEERNAME: i64 = 52;
// socketpair(domain, type, protocol, sv*): a CONNECTED AF_UNIX/AF_LOCAL pair, OUT
// int[2]. Stdlib readiness/IPC primitives (Python's socket.socketpair, libuv's
// self-pipe-via-socketpair) need this served, else the cell gets ENOSYS on the
// pair's creation and never reaches its poll/select readiness path.
const SYS_SOCKETPAIR: i64 = 53;
const SYS_SETSOCKOPT: i64 = 54;
const SYS_GETSOCKOPT: i64 = 55;
const SYS_ACCEPT4: i64 = 288;
const SYS_UNSHARE: i64 = 272;
const SYS_SETNS: i64 = 308;
const SYS_SET_TID_ADDRESS: i64 = 218;
const SYS_CLOCK_GETTIME: i64 = 228;
const SYS_EXIT_GROUP: i64 = 231;
const SYS_OPENAT: i64 = 257;
const SYS_NEWFSTATAT: i64 = 262;
const SYS_READLINKAT: i64 = 267;
// Directory / path-mutating syscalls (rootfs-confined in `service` via a
// parent-dir resolve + the `*at` form). Legacy non-`at` numbers + their modern
// `*at` counterparts; glibc/musl route most through the `*at` form (AT_FDCWD).
const SYS_TRUNCATE: i64 = 76;
const SYS_RENAME: i64 = 82;
const SYS_MKDIR: i64 = 83;
const SYS_RMDIR: i64 = 84;
const SYS_LINK: i64 = 86;
const SYS_UNLINK: i64 = 87;
const SYS_SYMLINK: i64 = 88;
const SYS_CHMOD: i64 = 90;
const SYS_CHOWN: i64 = 92;
const SYS_LCHOWN: i64 = 94;
const SYS_UTIMENSAT: i64 = 280;
const SYS_FCHOWNAT: i64 = 260;
const SYS_RENAMEAT: i64 = 264;
const SYS_LINKAT: i64 = 265;
const SYS_SYMLINKAT: i64 = 266;
const SYS_FCHMODAT: i64 = 268;
const SYS_FCHMOD: i64 = 91;
const SYS_FCHOWN: i64 = 93;
const SYS_MKDIRAT: i64 = 258;
const SYS_UNLINKAT: i64 = 263;
const SYS_RENAMEAT2: i64 = 316;
/// `AT_FDCWD`: the `*at` dirfd value meaning "resolve relative to cwd".
const AT_FDCWD: i32 = -100;
/// `AT_REMOVEDIR`: `unlinkat` flag to rmdir instead of unlink.
const AT_REMOVEDIR: u64 = 0x200;
/// `RESOLVE_BENEATH`: confine `openat2` resolution beneath the dirfd (no `..` /
/// absolute escape) — used for `*at` ops against a real (already-in-root) dirfd.
const RESOLVE_BENEATH: u64 = 0x08;
const SYS_FACCESSAT: i64 = 269;
const SYS_FACCESSAT2: i64 = 439;
const SYS_SET_ROBUST_LIST: i64 = 273;
const SYS_PRLIMIT64: i64 = 302;
const SYS_GETRANDOM: i64 = 318;
const SYS_STATX: i64 = 332;
const SYS_RSEQ: i64 = 334;
const SYS_PROCESS_VM_READV: i64 = 310;
const SYS_PROCESS_VM_WRITEV: i64 = 311;
const SYS_DUP3: i64 = 292;
// fd-object families (file watchers, legacy eventfd/epoll aliases, FIFO/socket
// nodes, bulk fd close). All delegated to `service` (new fds installed into the
// caller's virtual fd table; node creation rootfs-confined like mkdir).
// inotify: Node HMR / Go fsnotify use it with NO polling fallback. *init1 carries
// flags (IN_NONBLOCK/IN_CLOEXEC); the legacy *init takes none. add_watch's `path`
// is rootfs-confined like every other path op; events are read back via SYS_READ.
const SYS_INOTIFY_INIT: i64 = 253;
const SYS_INOTIFY_ADD_WATCH: i64 = 254;
const SYS_INOTIFY_RM_WATCH: i64 = 255;
const SYS_INOTIFY_INIT1: i64 = 294;
// eventfd (legacy, no flags) → eventfd2(initval, 0); epoll_create(size, legacy)
// → epoll_create1(0). Both just create+install a host fd like their modern forms.
const SYS_EVENTFD: i64 = 284;
const SYS_EPOLL_CREATE: i64 = 213;
const SYS_SIGNALFD: i64 = 282;
const SYS_SIGNALFD4: i64 = 289;
// mknod[at]: allow S_IFIFO/S_IFSOCK/S_IFREG nodes (mkfifo, AF_UNIX bind via a
// node, touch); REFUSE S_IFCHR/S_IFBLK with -EPERM (no device creation in the
// cell). Rootfs-confined via confined_parent + mknodat, like mkdir.
const SYS_MKNOD: i64 = 133;
const SYS_MKNODAT: i64 = 259;
// close_range(first, last, flags): close every guest fd in [first,last] in the
// caller's virtual fd table (forwarding each to the host close); used by libc's
// closefrom and runtimes pruning inherited fds.
const SYS_CLOSE_RANGE: i64 = 436;
const CLOSE_RANGE_UNSHARE: u64 = 1 << 1;
const CLOSE_RANGE_CLOEXEC: u64 = 1 << 2;
/// `struct file mode` type bits (st_mode & S_IFMT) for mknod node-type policy.
const S_IFMT: u32 = 0o170000;
const S_IFREG: u32 = 0o100000;
const S_IFDIR: u32 = 0o040000;
const S_IFCHR: u32 = 0o020000;
const S_IFBLK: u32 = 0o060000;
const S_IFIFO: u32 = 0o010000;
const S_IFSOCK: u32 = 0o140000;

// Control ops (cell → supervisor over the ring; NOT real syscall numbers).
// CTL_REAP(pid): that cell process is gone — close its host fds, drop its fd
// table. Sent from exit_group (prompt: a pipe reader must see EOF before the
// parent reaps) and from wait4 (backstop: covers children killed by a signal,
// e.g. the seccomp wall, that never ran exit_group).
const CTL_REAP: i64 = 0x53_0001;
// CTL_FORK_TABLE(slot): snapshot the CALLER's fd table for the child about to
// claim ring `slot`. Sent synchronously BEFORE forking — snapshotting lazily at
// the child's first delegated call would race the parent's own post-fork
// closes (sh closes its pipe ends immediately after fork).
const CTL_FORK_TABLE: i64 = 0x53_0002;
// CTL_FORK_CANCEL(slot): the fork/clone FAILED after CTL_FORK_TABLE — drop the
// pending snapshot (closing its dup'd fds) and release the slot.
const CTL_FORK_CANCEL: i64 = 0x53_0003;
// CTL_ENSURE_SERVICER(slot): spawn the supervisor servicer thread for ring
// `slot` if it doesn't exist yet. Sent by the cell right after it claims a slot
// (clone/fork), BEFORE the new thread/process runs — servicers are now spawned
// LAZILY (slot 0 = the supervisor's main thread; the rest on first use), so a
// single-threaded guest costs ZERO extra threads instead of MAX_SLOTS-1.
const CTL_ENSURE_SERVICER: i64 = 0x53_0004;
// CTL_BIND_SLOT(slot, child_pid): the fork parent knows the child's host pid as
// soon as fork/clone returns. Bind the already-allocated vpid immediately so
// grandchildren parent correctly even if the child does not delegate before it
// forks again; fd adoption remains lazy on the child's first serviced syscall.
const CTL_BIND_SLOT: i64 = 0x53_000a;
// CTL_SET_EXE(ptr, len): the CALLER just emulated an execve in-cell and updated
// its OWN `guest_exe()`. But `/proc/self/exe` readlink/open is served by the
// SUPERVISOR (a different process whose `guest_exe()` is the stale spawn-time
// value), so a guest that derives its resource dir or re-execs from
// `/proc/self/exe` (Chromium: icudtl.dat lookup + renderer/gpu child launch)
// would see the original `/bin/sh`, not its real binary. The cell forwards its
// new exe path here so the supervisor records it per-pid (read by `exe_for`).
const CTL_SET_EXE: i64 = 0x53_0005;
// CTL_SET_CMDLINE(ptr, len): like CTL_SET_EXE, but carries the NUL-separated guest
// argv the cell just execve'd. The supervisor serves `/proc/<pid>/cmdline` (and
// `/proc/self/cmdline`); without this it would expose the sentry binary's own argv
// (load_elf is not a real execve). Recorded per-pid in `proc_cmdline()` and read by
// `cmdline_for`, so `pgrep -f nginx` / `ps` running INSIDE the guest match the
// real workload command rather than the supervisor.
const CTL_SET_CMDLINE: i64 = 0x53_0006;
// CTL_CLOSE_CLOEXEC: emulate execve's fd-table step for the caller by closing
// every virtual host fd whose descriptor flag carries FD_CLOEXEC.
const CTL_CLOSE_CLOEXEC: i64 = 0x53_0007;
// CTL_DETHREAD_FOR_EXEC(slot, tid, tgid): emulate the kernel's de_thread phase for
// in-cell execve. The supervisor, not the sealed cell, enumerates sibling host
// TIDs, sends sentry's private teardown signal, waits for them to exit, and
// reclaims only their ring slots. Process-owned state (fd table, cwd, proc metadata)
// stays alive because the execing thread continues as the same guest process.
const CTL_DETHREAD_FOR_EXEC: i64 = 0x53_0008;
// CTL_SHM_FLUSH(addr, len, remove): write dirty shadow MAP_SHARED bytes for this
// guest pid back to their backing fds. `addr=0,len=u64::MAX` means all mappings
// for the pid. `remove!=0` forgets the mapping after flushing (munmap/exec/reap).
const CTL_SHM_FLUSH: i64 = 0x53_0009;
// CTL_SHM_ALIAS(pool_addr, visible_addr, len): after the cell aliases a shared-pool
// slice with mremap(old_size=0), update supervisor bookkeeping to the address the
// guest actually sees so munmap/msync can hit the right map record.
const CTL_SHM_ALIAS: i64 = 0x53_000e;
// CTL_SHM_PROTECT(addr, len, prot): keep the supervisor's writable-bit metadata in
// sync with a cell-local mprotect on a shared alias.
const CTL_SHM_PROTECT: i64 = 0x53_0010;
// CTL_SHM_MOVE(old_addr, old_len, new_addr, new_len): keep the supervisor's map
// record in sync with a cell-local mremap on a shared alias.
const CTL_SHM_MOVE: i64 = 0x53_0011;
// CTL_ENVDBG(ptr, len): DIAGNOSTIC — the cell forwards a DATABASE_URL trace line on each
// in-cell execve so the supervisor (where openat/ipc_logf works) can log it box-readable.
const CTL_ENVDBG: i64 = 0x53_000b;
// CTL_COREDUMP(): DIAGNOSTIC — the cell asks the supervisor to gcore it. The cell is
// blocked in this delegate call (quiescent), so the core is a consistent snapshot of the
// process that just built a truncated spawn-env block — its heap holds BOTH the corrupted
// 59-byte block and its own full process.env (the corruption's crime scene). One-shot
// (supervisor caps the count) to bound disk + only capture the chain origin.
const CTL_COREDUMP: i64 = 0x53_000c;
// CTL_WATCHADDR(addr, orig): DIAGNOSTIC — the cell asks the supervisor to externally watch
// guest byte `addr` (currently == `orig`) via cross-process vm_read polling, layout-neutrally
// (no in-cell instrumentation that would perturb the OOB-write Heisenbug). On change the
// supervisor logs the cell pid + every thread's current syscall/PC (/proc/<pid>/task/*/syscall).
const CTL_WATCHADDR: i64 = 0x53_000d;
// CTL_PROCEXE_EXEC_FAIL(ptr, len): DIAGNOSTIC — report a `/proc/self/exe` exec
// emulation failure from the sealed cell to the supervisor. The cell must not call
// `ipc_logf_raw` directly: that opens files from a context where SUD/seccomp are
// active and can kill a Chromium worker with SIGSYS.
const CTL_PROCEXE_EXEC_FAIL: i64 = 0x53_000f;
// CTL_SET_CREDS(uid-pair0, uid-pair1, gid-pair0, gid-pair1): mirror the cell-local
// virtual credential state into the supervisor so delegated filesystem syscalls
// can apply guest DAC and ownership semantics.
const CTL_SET_CREDS: i64 = 0x53_0012;
// CTL_SET_UMASK(mask): mirror the cell-local virtual umask into the supervisor so
// delegated create syscalls can apply guest process semantics without touching the
// supervisor process-global host umask.
const CTL_SET_UMASK: i64 = 0x53_0013;

// ─── race-free cancel-on-reap stranded-servicer reclamation ──────────────────
//
// free_slots_of frees a slot only when request == response. A child that died
// (SIGKILL'd by `timeout`, the straggler sweep, or its own crash) mid-delegated-
// syscall leaves request != response, so its servicer is parked in a blocking host
// call against a now-DEAD peer and its slot LEAKS — after ~MAX_SLOTS leaks fork
// returns -EAGAIN and the sandbox wedges. CTL_REAP for such a child arrives AFTER the
// cell host(wait4)'d it (or after its exit_group), so the pid is DEFINITIVELY DEAD at
// reap time. Reclamation is EVENTFD-driven and race-free: every blocking host fd-wait
// the servicer performs is a `poll()` that ALSO watches the servicer's own CANCEL
// EVENTFD (ring.cancel_efd); free_slots_of writes a token to that eventfd, which —
// being level-triggered — makes the servicer's current OR next poll return at once
// (no missed-wakeup window, unlike the old tgkill(SIGURG) which could land between
// the cancel-check and the blocking call). The servicer returns CANCEL_SENTINEL and
// tears its OWN slot down (fd_drop + free) in its OWN thread, with NO cross-thread
// response store and NO signals (so no async-signal hazards, no SA_RESTART subtleties,
// no stale-tid mis-delivery). The peer is dead by construction, so a late real result
// can never race in.
// Sentinel a cancel-aware blocking arm returns on cancellation. -255: out of the
// errno range, never a real syscall return, recognized only by servicer_loop.
const CANCEL_SENTINEL: i64 = -255;
const EINTR: i64 = -4;
const EAGAIN: i64 = -11;
const CANCEL_REAP_PROCESS: u8 = 0;
const CANCEL_SLOT_ONLY: u8 = 1;
// poll(2) revents/events bits we use for the cancel-aware blocking waits.
const POLLIN_BIT: i16 = 0x001;
const POLLPRI_BIT: i16 = 0x002;
// eventfd2 flags (== O_NONBLOCK | O_CLOEXEC) for the per-servicer cancel eventfd.
const EFD_NONBLOCK_CLOEXEC: u64 = 0o4000 | 0o2000000;

// futex ops (SHARED — no PRIVATE flag, the ring lives in MAP_SHARED memory)
const FUTEX_WAIT: u64 = 0;
const FUTEX_WAKE: u64 = 1;
const FUTEX_PRIVATE_FLAG: u64 = 128;
const FUTEX_CLOCK_REALTIME: u64 = 256;
const FUTEX_WAIT_BITSET: u64 = 9;
const FUTEX_BITSET_MATCH_ANY: u64 = 0xffff_ffff;

// Adaptive-spin budget for the delegation ring: both the cell (waiting for a
// response) and the servicer (waiting for a request) poll the shared counter
// this many times (PAUSE between) BEFORE falling back to a blocking `futex`.
// Under back-to-back load the round-trip completes within the spin, so neither
// side sleeps — eliminating the ~13 µs context-switch round-trip (the dominant
// delegated-syscall cost, see the max-parity plan's baseline). When idle, both
// sides exhaust the budget once and then block, so an idle sandbox costs ~0 CPU.
// The spin is purely additive over the (correct) futex protocol: on timeout it
// falls through to the exact same `futex` wait, so there's no lost-wakeup risk.
const RING_SPIN: u32 = 4096;

// ─── raw host syscall (TLS-free; usable from the signal handler) ─────────────
#[inline(always)]
unsafe fn host(nr: i64, a: u64, b: u64, c: u64, d: u64, e: u64, f: u64) -> i64 {
    let ret: i64;
    std::arch::asm!(
        "syscall",
        inlateout("rax") nr => ret,
        in("rdi") a, in("rsi") b, in("rdx") c,
        in("r10") d, in("r8") e, in("r9") f,
        lateout("rcx") _, lateout("r11") _,
        options(nostack),
    );
    ret
}

fn raw_write(fd: c_int, b: &[u8]) {
    unsafe {
        host(
            SYS_WRITE,
            fd as u64,
            b.as_ptr() as u64,
            b.len() as u64,
            0,
            0,
            0,
        )
    };
}
fn log(b: &[u8]) {
    raw_write(2, b);
}
fn fmt_i64(n: i64, buf: &mut [u8; 24]) -> usize {
    let neg = n < 0;
    let mut m = (n as i128).unsigned_abs();
    let mut i = 24;
    if m == 0 {
        i -= 1;
        buf[i] = b'0';
    }
    while m > 0 {
        i -= 1;
        buf[i] = b'0' + (m % 10) as u8;
        m /= 10;
    }
    if neg {
        i -= 1;
        buf[i] = b'-';
    }
    i
}
fn logn(prefix: &[u8], n: i64, suffix: &[u8]) {
    let mut b = [0u8; 24];
    let i = fmt_i64(n, &mut b);
    log(prefix);
    log(&b[i..]);
    log(suffix);
}
/// Append a diagnostic line to `/tmp/sentry_ipc.log` (the supervisor's stderr is
/// not captured by the test harness, so IPC tracing goes to a file). Each entry
/// is `key1=v1 key2=v2 …\n`. Gated by the caller on `IPCTRACE`. Best-effort.
fn ipc_logf(parts: &[(&[u8], i64)], hex: &[u8]) {
    // Build the line ONCE, then best-effort write to BOTH sinks: the host
    // /tmp/sentry_ipc.log (AT_FDCWD) AND — crucially when the supervisor runs in a
    // private mount namespace where that /tmp is invisible to the box — the GUEST
    // ROOTFS via ROOT_FD (i.e. <rootfs>/tmp/sentry_ipc.log, which IS box-readable as
    // the shared snapshot rootfs dir). Either may fail; we never early-return so the
    // other still gets the line.
    let mut line: Vec<u8> = Vec::with_capacity(128);
    for (k, v) in parts {
        line.extend_from_slice(k);
        let mut nb = [0u8; 24];
        let i = fmt_i64(*v, &mut nb);
        line.extend_from_slice(&nb[i..]);
        line.push(b' ');
    }
    if !hex.is_empty() {
        line.extend_from_slice(b"hex=");
        const H: &[u8; 16] = b"0123456789abcdef";
        for &byte in hex.iter().take(80) {
            line.push(H[(byte >> 4) as usize]);
            line.push(H[(byte & 0xf) as usize]);
        }
        line.push(b' ');
    }
    line.push(b'\n');
    let flags = (libc::O_WRONLY | libc::O_CREAT | libc::O_APPEND) as u64;
    let write_to = |dirfd: i64, path: &[u8]| {
        let fd = unsafe {
            host(
                SYS_OPENAT,
                dirfd as u64,
                path.as_ptr() as u64,
                flags,
                0o644,
                0,
                0,
            )
        };
        if fd < 0 {
            return;
        }
        unsafe {
            host(
                SYS_WRITE,
                fd as u64,
                line.as_ptr() as u64,
                line.len() as u64,
                0,
                0,
                0,
            );
            host(SYS_CLOSE, fd as u64, 0, 0, 0, 0, 0);
        }
    };
    write_to(-100, b"/tmp/sentry_ipc.log\0"); // AT_FDCWD (host /tmp; may be a private ns)
    let rfd = ROOT_FD.load(Ordering::Relaxed);
    if rfd >= 0 {
        write_to(rfd as i64, b"tmp/sentry_ipc.log\0"); // <rootfs>/tmp — box-readable
    }
}
/// Append a raw line (a trailing newline is added) to `sentry_ipc.log`. Writes to BOTH
/// sinks like [`ipc_logf`]: AT_FDCWD `/tmp` (host /tmp; the cell sees the box /tmp) AND the
/// guest ROOTFS via ROOT_FD (`<rootfs>/tmp`, box-readable) — crucial for the SUPERVISOR,
/// which runs in a private mount namespace where its AT_FDCWD `/tmp` is invisible to the box.
/// (Before this, every supervisor-side raw diagnostic vanished into the private /tmp.)
fn ipc_logf_raw(bytes: &[u8]) {
    let mut line = bytes.to_vec();
    line.push(b'\n');
    let flags = (libc::O_WRONLY | libc::O_CREAT | libc::O_APPEND) as u64;
    let write_to = |dirfd: i64, path: &[u8]| {
        let fd = unsafe {
            host(
                SYS_OPENAT,
                dirfd as u64,
                path.as_ptr() as u64,
                flags,
                0o644,
                0,
                0,
            )
        };
        if fd < 0 {
            return;
        }
        unsafe {
            host(
                SYS_WRITE,
                fd as u64,
                line.as_ptr() as u64,
                line.len() as u64,
                0,
                0,
                0,
            );
            host(SYS_CLOSE, fd as u64, 0, 0, 0, 0, 0);
        }
    };
    write_to(-100, b"/tmp/sentry_ipc.log\0"); // AT_FDCWD (host /tmp; may be a private ns)
    let rfd = ROOT_FD.load(Ordering::Relaxed);
    if rfd >= 0 {
        write_to(rfd as i64, b"tmp/sentry_ipc.log\0"); // <rootfs>/tmp — box-readable
    }
}
fn emfile_reserve_open() {
    if EMFILE_LOG_RESERVE.load(Ordering::Relaxed) >= 0 {
        return;
    }
    let fd = unsafe {
        host(
            SYS_OPENAT,
            (-100i64) as u64,
            b"/dev/null\0".as_ptr() as u64,
            (libc::O_RDONLY | libc::O_CLOEXEC) as u64,
            0,
            0,
            0,
        )
    };
    if fd >= 0 {
        if EMFILE_LOG_RESERVE
            .compare_exchange(-1, fd as i32, Ordering::AcqRel, Ordering::Relaxed)
            .is_err()
        {
            unsafe {
                host(SYS_CLOSE, fd as u64, 0, 0, 0, 0, 0);
            }
        }
    }
}

fn emfile_reserve_close_one() {
    let fd = EMFILE_LOG_RESERVE.swap(-1, Ordering::AcqRel);
    if fd >= 0 {
        unsafe {
            host(SYS_CLOSE, fd as u64, 0, 0, 0, 0, 0);
        }
    }
}

fn fd_table_stats(pid: i32) -> (usize, usize, i32) {
    let t = fdt().lock().unwrap();
    let proc_fds = t.get(&pid).map(|m| m.len()).unwrap_or(0);
    let total_fds = t.values().map(|m| m.len()).sum();
    let max_guest = t
        .get(&pid)
        .and_then(|m| m.keys().next_back().copied())
        .unwrap_or(-1);
    (proc_fds, total_fds, max_guest)
}

fn pending_fd_count() -> usize {
    pending().lock().unwrap().values().map(|m| m.len()).sum()
}

fn proc_self_fd_count() -> i64 {
    std::fs::read_dir("/proc/self/fd")
        .map(|it| it.count() as i64)
        .unwrap_or(-1)
}

fn log_open_emfile(pid: i32, nr: i64, path: &[u8]) {
    emfile_reserve_close_one();
    let (proc_fds, total_fds, max_guest) = fd_table_stats(pid);
    let pending_fds = pending_fd_count();
    let host_fds = proc_self_fd_count();
    let mut cur = -1i64;
    let mut max = -1i64;
    unsafe {
        let mut lim: libc::rlimit = std::mem::zeroed();
        if libc::getrlimit(libc::RLIMIT_NOFILE, &mut lim) == 0 {
            cur = lim.rlim_cur as i64;
            max = lim.rlim_max as i64;
        }
    }
    let mut line: Vec<u8> = Vec::with_capacity(320);
    macro_rules! field {
        ($name:expr, $value:expr) => {{
            let mut nb = [0u8; 24];
            let i = fmt_i64($value as i64, &mut nb);
            line.extend_from_slice($name);
            line.extend_from_slice(&nb[i..]);
            line.push(b' ');
        }};
    }
    line.extend_from_slice(b"EMFILE_OPEN ");
    field!(b"pid=", pid);
    field!(b"nr=", nr);
    field!(b"proc_fds=", proc_fds);
    field!(b"total_fds=", total_fds);
    field!(b"pending_fds=", pending_fds);
    field!(b"max_guest=", max_guest);
    field!(b"host_fds=", host_fds);
    field!(b"rlim_cur=", cur);
    field!(b"rlim_max=", max);
    line.extend_from_slice(b"cmd=");
    let cmd = cmdline_for(pid);
    for &b in cmd.iter().take(80) {
        line.push(if b == 0 { b' ' } else { b });
    }
    line.extend_from_slice(b" path=");
    let bare = match path.split_last() {
        Some((0, head)) => head,
        _ => path,
    };
    line.extend_from_slice(&bare[..bare.len().min(160)]);
    ipc_logf_raw(&line);
    emfile_reserve_open();
}
fn die(msg: &[u8]) -> ! {
    log(msg);
    unsafe { libc::_exit(1) }
}
/// Like [`die`] but exits with a caller-chosen code. Used by `cell_main` to exit
/// 127 (the shell's "command not found / not executable" convention) when the
/// guest ELF or its interpreter can't be loaded — so the exec server's EXIT frame
/// carries 127 (which the host decodes cleanly) instead of a generic 1.
fn die_code(msg: &[u8], code: c_int) -> ! {
    log(msg);
    unsafe { libc::_exit(code) }
}

static mut TRACE: bool = false;
/// `SENTRY_IPCTRACE=1`: log every delegated sendmsg/recvmsg on the channel
/// (pid, host fd, byte count, SCM_RIGHTS fd count) — the Chromium Mojo bootstrap
/// diagnostic (does the browser's invitation, w/ its passed fds, reach the child).
static mut IPCTRACE: bool = false;
/// `SENTRY_REGDIFF=1`: DIAGNOSTIC — in the SIGSYS handler, snapshot the guest GP
/// registers on entry and diff them just before returning to the guest. A SUD trap MUST
/// preserve every register except RAX (the return value); RIP is not advanced (kernel
/// already did) and the trapped syscall insn never executed (so RCX/R11 aren't clobbered).
/// Any other register changing = the handler corrupted a guest register — the suspected
/// root of node's env-build writing through a clobbered pointer (DATABASE_URL→59).
/// AtomicBool, NOT `static mut bool`: the optimizer const-folds a `static mut bool` read to
/// its `false` initializer (it can't see the env-init write across the fn boundary), which
/// made `regdiff_snap` provably `None` and DCE'd the entire diff block out of the binary.
static REGDIFF: std::sync::atomic::AtomicBool = std::sync::atomic::AtomicBool::new(false);
static ENVDBG: std::sync::atomic::AtomicBool = std::sync::atomic::AtomicBool::new(false);
static ENVSCAN: std::sync::atomic::AtomicBool = std::sync::atomic::AtomicBool::new(false);
/// End-to-end SCM_RIGHTS fd-conservation counters (gated on IPCTRACE). Chromium's
/// ipcz hands its children every shared-memory buffer (the AddBlockBuffer /
/// ProvideMemory memfds that back a parcel's data fragment) as an SCM_RIGHTS fd on
/// the AF_UNIX SOCK_STREAM Mojo channel. If even ONE such memfd is dropped on the
/// delegated send/recv, the consumer's `ipcz::BufferPool` never registers that
/// `BufferId`, so `NodeLink::OnAcceptParcel` sees `GetFragment().is_pending()` and
/// parks the parcel forever in `WaitForParcelFragmentToResolve` — the parcel is
/// accepted-but-never-delivered, `ChildProcessHost::Ping`'s reply never dispatches,
/// and Chromium's 15s "no connection" watchdog kills the child. These counters make
/// such a drop OBSERVABLE: across a run the total fds the supervisor SENT on behalf
/// of cells must equal the total it RECEIVED-and-installed for cells (every passed
/// fd has exactly one sender and one receiver among our cells). A persistent skew is
/// a smoking gun. (Diagnostic only — never gates control flow.)
static SCM_FDS_SENT: AtomicU64 = AtomicU64::new(0);
static SCM_FDS_RECVD: AtomicU64 = AtomicU64::new(0);
static EMFILE_LOG_RESERVE: AtomicI32 = AtomicI32::new(-1);
static SUPERVISOR_SELF_PID: AtomicI32 = AtomicI32::new(0);
static GUEST_NOFILE_DEFAULT_CUR: AtomicU64 = AtomicU64::new(1024);
static GUEST_NOFILE_DEFAULT_MAX: AtomicU64 = AtomicU64::new(1024);
/// Count of recvmsg deliveries where the guest's ancillary buffer was too small to
/// carry the fds the host already delivered (kernel-faithful MSG_CTRUNC). Must stay
/// 0 for the Chromium channel (its cmsg buffer is sized for `kMaxSendmsgHandles`);
/// any nonzero value means fds were dropped end-to-end → a parked-parcel hang.
static SCM_RECV_TRUNC: AtomicU64 = AtomicU64::new(0);
/// `SENTRY_WAITTRACE=1`: log every delegated epoll_wait/poll/ppoll RETURN (pid,
/// epfd/nfds, timeout, ready-count) — pinpoints a process whose event loop parks
/// forever (the Chromium "no connection" watchdog = main thread never woken).
static mut WAITTRACE: bool = false;
/// `SENTRY_SYSCALLTRACE=1`: log EVERY delegated (servicer-path) syscall (pid, nr,
/// a, b, c, ret). Firehose — only for short pinpoint runs. Reveals the syscall
/// pattern of a process spinning/waiting on a never-satisfied condition.
static mut SYSCALLTRACE: bool = false;
/// `SENTRY_FDTRACE=1`: narrow Chromium launch diagnostics for exact fd remaps
/// around the pseudonymization salt handle. Avoids the heavy IPC/mmap tracing.
static mut FDTRACE: bool = false;
/// `SENTRY_SLOTDUMP=1`: enable the cheap dbg_rip/dbg_caller stamping (WITHOUT the
/// per-syscall DSYS firehose) so the slot-dump watchdog can attribute a stuck slot's
/// parked guest call-site. Decoupled from SYSCALLTRACE on purpose: the firehose's
/// per-syscall file I/O perturbs timing and would mask the MT race we're chasing.
static mut SLOTDUMP: bool = false;

// ─── per-thread shared-memory rings ──────────────────────────────────────────
//
// One ring per cell thread, so a blocking delegated syscall (e.g. `epoll_wait`)
// on one thread doesn't stall the others. Each cell thread owns a ring SLOT; the
// supervisor pre-spawns one servicer thread per slot. A cell thread finds its slot
// from its `%gs` base (the guest uses `%fs` for TLS; `%gs` is free), set by the
// clone trampoline and read here with `rdgsbase` — no syscall.
// 512 slots = the max CONCURRENT cell threads+processes per tenant (one shared
// SLOT_BM across the whole fork tree). Chromium's multi-process tree (browser
// ~60 threads + zygote + each renderer ~30 + gpu/utility ~15 — all fork
// descendants sharing this pool) blows far past the old 64: a child's
// `pthread_create` then returned EAGAIN (handle_clone → alloc_slot full) and the
// child CHECK-crashed, hanging Target.createTarget. 512 gives generous headroom
// for a multi-renderer browser; the per-slot cost is lazy (ALT_STACKS/rings are
// demand-zero BSS / MAP_SHARED, only touched slots commit pages).
const MAX_SLOTS: usize = 512;
// SLOT_BM is now SLOT_WORDS u64 words (was a single u64 — capped MAX_SLOTS at 64).
const SLOT_WORDS: usize = MAX_SLOTS / 64;
// The top K slots are RESERVED for nested-delegation (DELEG) re-entry (see the
// per-depth ring-slot lease below), so heavy reentrancy can never starve the
// PROCESS slots a fork/clone draws from, and vice-versa. K is a one-constant
// tunable; with K=8 the PROCESS pool is the low MAX_SLOTS-K=504 slots, the DELEG
// pool the top 8. Slot 0 (bit 0 preset) is the main cell's PROCESS slot.
const DELEG_SLOTS: usize = 8;
const PROCESS_SLOTS: usize = MAX_SLOTS - DELEG_SLOTS;
// rseq registration is per thread and a real execve(2) clears it. Sentry
// emulates execve in-place, so record the live registration for each ring slot
// and explicitly unregister it before replacing the guest image.
static RSEQ_ADDR: [AtomicU64; MAX_SLOTS] = [const { AtomicU64::new(0) }; MAX_SLOTS];
static RSEQ_LEN: [AtomicU64; MAX_SLOTS] = [const { AtomicU64::new(0) }; MAX_SLOTS];
static RSEQ_SIG: [AtomicU64; MAX_SLOTS] = [const { AtomicU64::new(0) }; MAX_SLOTS];
// Guest-visible sigaltstack state, keyed by ring slot/thread. The real kernel
// alt-stack stays owned by sentry for SIGSYS; this only preserves the ABI state
// that runtimes query/set around their own signal machinery.
static mut GUEST_SIGALTSTACKS: [[u8; 24]; MAX_SLOTS] = [[0u8; 24]; MAX_SLOTS];
static mut VFORK_EFDS: [i32; MAX_SLOTS] = [-1; MAX_SLOTS];
// Cap on nested delegated-syscall depth. A guest signal handler that issues a
// delegated syscall while an outer delegate is parked re-enters delegate() at
// depth>0 and takes a FRESH (DELEG) slot; the deepest delegated call past this
// fails -EAGAIN (fails CLOSED — like an OS refusal — never corrupts a ring).
// Realistic POSIX signal nesting is shallow, so 8 is generous; a deliberately
// adversarial deeper nest is a DoS-against-self, not an isolation breach.
const MAX_DEPTH: usize = 8;
// Ring KIND (Ring.kind): a PROCESS slot is the per-thread mailbox a cell thread
// (main cell + every fork/clone child) owns from depth 0; a DELEG slot is a
// fresh mailbox a nested (depth>0) delegated syscall leases for the duration of
// the re-entry; CTL is a control-op slot exempt from cancel-on-reap teardown.
// The existing zeroed Ring init keeps every current slot PROCESS=0, so all call
// sites are behavior-identical until a slot is explicitly stamped DELEG/CTL.
const RING_PROCESS: u8 = 0;
const RING_DELEG: u8 = 1;
#[repr(C)]
struct Ring {
    request: u32,     // generation the cell has posted
    response: u32,    // generation the supervisor has completed
    pid: i32,         // the pid that owns this slot (a fork()'d child gets a new pid)
    tid: i32,         // real host tid for thread slots; equals pid for forked processes
    fork_parent: i32, // stamped (pre-fork) with the parent's pid: the supervisor
    // clones the parent's fd table for this slot's pid before its first request
    nr: i64,
    args: [u64; 6],
    ret: i64,
    kind: u8, // RING_PROCESS / RING_DELEG (see the constants above)
    // What a CANCEL_SENTINEL means for this slot. Normal process reap drops the
    // process-owned fd table; de_thread during exec only tears down a sibling
    // thread slot while the guest process and its fd table continue.
    cancel_mode: u8,
    _kind_pad: [u8; 2],
    // Race-free cancel-on-reap reclamation: the servicer stamps its own CANCEL
    // EVENTFD here on its first iteration. Every blocking host fd-wait the servicer
    // does is a `poll([target_fd, cancel_efd], …)` that ALSO watches this eventfd, so
    // when free_slots_of writes a token to it (because CTL_REAP named this slot's
    // now-dead pid while it was wedged) the poll returns IMMEDIATELY — level-triggered,
    // so there is NO missed-wakeup window the way a tgkill(SIGURG) had. The servicer
    // then abandons the call and tears its own slot down. No signals, no cross-thread
    // response store, no async-signal hazards. -1 until the servicer stamps it.
    cancel_efd: i32,
    // Live guest signal interrupt: when a sandbox-owned pid is signaled, the host
    // signal lands on the cell process, but its servicer may be blocked in a
    // delegated host wait on that process's behalf. Waking this eventfd makes the
    // wait return -EINTR to the guest instead of sleeping until unrelated readiness.
    intr_efd: i32,
    // Set after a writer posts to the corresponding eventfd. The servicer uses these
    // to avoid two doomed nonblocking eventfd reads on every delegated syscall.
    cancel_pending: u8,
    intr_pending: u8,
    _pad: [u8; 2], // keep the 8-byte tail aligned (Ring has i64 fields)
    // DIAGNOSTIC (SENTRY_SYSCALLTRACE only): the guest %rip that trapped into this
    // delegated syscall, stamped by delegate_on from the cell-side LAST_TRAP_RIP.
    // Lets the supervisor's DSYS trace addr2line the exact guest call-site — the
    // symbol route for pinning the ipcz/Chromium connect stall. Zero when not traced.
    dbg_rip: u64,
    // DIAGNOSTIC: first guest stack word in the main-exe range [0x400000, 0x3f000000)
    // — the ipcz/app caller of the libc syscall wrapper (the syscall %rip is always a
    // libc wrapper, shared by every socket op). With a non-component static build (ipcz
    // linked into the exe at 0x400000) this addr2line's directly to the parked function.
    dbg_caller: u64,
    // Live C4 capture support. When a cell leaves guest code to service a syscall in
    // sentry-owned code, ptrace sees the host futex/dispatch frame, not a resumable
    // guest frame. The cell writes the post-syscall guest register image here before
    // blocking so launcher-side capture can serialize that stable boundary.
    snap_valid: u32,
    _snap_pad: u32,
    snap_fs: u64,
    snap_gregs: [i64; 18],
}
/// Set THIS thread's `%gs` base (its ring slot). fsgsbase-only; TLS-free; safe in
/// the signal handler. Used by the fork/clone paths to give a child its slot.
#[inline(always)]
fn set_slot(slot: u64) {
    unsafe {
        std::arch::asm!("wrgsbase {}", in(reg) slot, options(nostack, nomem, preserves_flags))
    };
}
static mut RINGS: *mut Ring = std::ptr::null_mut();
// DIAGNOSTIC (SENTRY_SYSCALLTRACE): per-slot guest %rip of the syscall currently
// trapping into the SUD handler, captured cell-side in cell_layer1 and copied into
// the ring by delegate_on. Cell-private BSS (the cell that traps is the same cell
// that runs delegate_on), keyed by current_slot(). Not shared; supervisor reads the
// value via Ring.dbg_rip. Sufficient for the rewrite-OFF (pure-SUD) trace runs.
static mut LAST_TRAP_RIP: [u64; MAX_SLOTS] = [0u64; MAX_SLOTS];
// DIAGNOSTIC companion to LAST_TRAP_RIP: the first guest stack return-address in the
// main-exe range, captured in cell_layer1 by scanning a few words above the guest %rsp.
static mut LAST_TRAP_CALLER: [u64; MAX_SLOTS] = [0u64; MAX_SLOTS];
// The slot-allocation BITMAP lives in the SHARED ring mapping (after the ring
// array), NOT in a cell static: fork children inherit diverging COPIES of cell
// statics, and two processes allocating from private state hand out the same
// slot (two processes on one ring = a corrupted ring). Slots are RECYCLED when
// their owning process is reaped (see CTL_REAP / free_slots_of) — a Dockerfile
// `RUN` step forks thousands of times, far past MAX_SLOTS. Slot 0 = the main
// cell thread. Allocation is cell-side (atomics only: TLS-free, allowlist-free).
static mut SLOT_BM: *mut u64 = std::ptr::null_mut();
/// The `w`-th 64-bit word of the slot bitmap (slots `w*64 .. w*64+64`). The bitmap
/// spans SLOT_WORDS words in the shared mapping so the pool can exceed 64 slots.
#[inline]
fn slot_word(w: usize) -> &'static std::sync::atomic::AtomicU64 {
    unsafe { std::sync::atomic::AtomicU64::from_ptr(SLOT_BM.add(w)) }
}
/// Is slot `i` currently claimed? Word-indexed (the bitmap is multi-word now).
#[inline]
fn slot_is_set(i: u64) -> bool {
    slot_word((i / 64) as usize).load(Ordering::Relaxed) & (1u64 << (i % 64)) != 0
}
/// Claim the lowest free slot from the PROCESS pool (the low PROCESS_SLOTS bits);
/// returns MAX_SLOTS (caller ⇒ -EAGAIN) when full. The top DELEG_SLOTS bits are
/// masked OUT so a fork/clone can never consume a slot reserved for nested
/// delegation (and vice-versa — see `alloc_deleg_slot`); this keeps heavy
/// reentrancy from starving the fork pool.
fn alloc_slot() -> u32 {
    alloc_range(0, PROCESS_SLOTS as u32)
}
/// Claim a free slot from the DELEG pool (the top DELEG_SLOTS slots) for a nested
/// (depth>0) delegated syscall. Returns MAX_SLOTS when the DELEG pool is full
/// (caller ⇒ -EAGAIN — the re-entry fails closed, the outer slot is untouched).
fn alloc_deleg_slot() -> u32 {
    alloc_range(PROCESS_SLOTS as u32, MAX_SLOTS as u32)
}
/// Claim the lowest free slot in `[lo, hi)`, scanning the multi-word SLOT_BM with
/// the same per-word atomic CAS as the original single-word allocator. Returns
/// MAX_SLOTS (caller ⇒ -EAGAIN) when the range is full. PROCESS and DELEG pools
/// are DISJOINT slot ranges of the same bitmap, so a fork/clone can never consume
/// a slot reserved for nested delegation (and vice-versa) — this keeps heavy
/// reentrancy from starving the fork pool.
#[inline]
fn alloc_range(lo: u32, hi: u32) -> u32 {
    let first_word = (lo / 64) as usize;
    let last_word = ((hi - 1) / 64) as usize;
    for w in first_word..=last_word {
        let word = slot_word(w);
        let wbase = (w as u32) * 64;
        // The sub-range of THIS word that falls within [lo, hi), as a bit mask.
        let lobit = if w == first_word { lo - wbase } else { 0 };
        let hibit = if w == last_word { hi - wbase } else { 64 };
        let rangemask: u64 = if hibit - lobit == 64 {
            u64::MAX
        } else {
            ((1u64 << (hibit - lobit)) - 1) << lobit
        };
        loop {
            let cur = word.load(Ordering::Relaxed);
            let free = !cur & rangemask;
            if free == 0 {
                break; // this word is full within the range — try the next word
            }
            let bit = free.trailing_zeros();
            if word
                .compare_exchange(
                    cur,
                    cur | (1u64 << bit),
                    Ordering::Relaxed,
                    Ordering::Relaxed,
                )
                .is_ok()
            {
                let i = wbase + bit;
                // Reset the freshly-claimed slot's reentrancy DEPTH stack. A prior tenant
                // reaped mid-nested-delegate (watchdog SIGKILL / fault at depth>0, so the
                // balancing depth-restore write in delegate() never ran) leaves
                // SLOT_STACKS[i].depth ELEVATED. The new owner — especially a fork child
                // set_slot'd onto this slot — would then read depth>0 on its FIRST
                // delegate() and wrongly take the DELEG-lease arm, delegating on a fresh
                // DELEG slot while the servicer that the fork path ensured for THIS (base)
                // slot sits idle → cell spins on an unanswered ring, servicer spins idle =
                // the rewrite / fork-storm reentrancy livelock (C1). Claiming the slot is
                // the one chokepoint every new tenancy passes through. (task_13ec1495)
                unsafe {
                    std::ptr::write_volatile(
                        std::ptr::addr_of_mut!((*slot_stack(i as u64)).depth),
                        0,
                    );
                    std::ptr::write_volatile(
                        std::ptr::addr_of_mut!((*ring_at(i as u64)).kind),
                        RING_PROCESS,
                    );
                    std::ptr::write_volatile(
                        std::ptr::addr_of_mut!((*ring_at(i as u64)).cancel_mode),
                        CANCEL_REAP_PROCESS,
                    );
                }
                reset_guest_sigaltstack(i as u64);
                set_vfork_efd(i as u64, -1);
                return i;
            }
        }
    }
    MAX_SLOTS as u32
}
fn free_slot(i: u32) {
    // Clear the slot's recorded owner pid BEFORE releasing the bit, so the fd-leak
    // sweeper never sees a STALE dead pid on a reused-but-not-yet-stamped slot (the
    // gap between alloc_slot and the new fork child stamping `ring.pid` post-fork) and
    // wrongly reap_dead_pid's the in-transit slot. pid 0 is sentinel-skipped by the
    // sweep (≤1), so the slot is invisible to it until its real owner stamps a live pid.
    unsafe {
        clear_ring_owner(i as u64);
        std::ptr::write_volatile(
            std::ptr::addr_of_mut!((*ring_at(i as u64)).cancel_mode),
            CANCEL_REAP_PROCESS,
        );
    }
    set_vfork_efd(i as u64, -1);
    slot_word((i / 64) as usize).fetch_and(!(1u64 << (i % 64)), Ordering::Relaxed);
}
/// Release a DELEG slot (thin wrapper over `free_slot`: DELEG and PROCESS slots
/// share one SLOT_BM, so reclamation is uniform). Called when a nested delegated
/// syscall returns and its fresh slot is quiescent (we just read its response).
fn free_deleg_slot(i: u32) {
    free_slot(i)
}

// ─── per-trap-DEPTH ring-slot lease (masking-free reentrant trap) ────────────
//
// A single per-thread ring slot is reentrancy-UNSAFE: a nested SIGSYS (a guest
// SIGALRM/SIGCHLD handler issuing a delegated syscall while an outer delegate is
// parked in FUTEX_WAIT) would reuse the SAME slot and overwrite its in-flight
// (nr,args,request,response,ret) — the outer then reads the inner's ret (silent
// RAX corruption) or the generations desync into a hang. SIGSYS must stay
// UNBLOCKED (a blocked synchronous SIGSYS kills the cell) and the guest's own
// SIGALRM/SIGCHLD must stay deliverable mid-trap (what `timeout` needs), so
// masking can't close the window — the dropped sa_mask fix proved that (it broke
// `timeout 5 sleep 1`).
//
// Fix: track trap DEPTH per BASE slot in a SHARED-mapping array (NOT in %gs — the
// kernel doesn't save/restore an fsgsbase-written %gs base across the SIGSYS
// sigcontext, so a %gs depth would leak across siglongjmp/emulate_execve; and NOT
// at cell_layer1 entry — the rewrite fast path calls dispatch_simple DIRECTLY,
// never entering cell_layer1). Track it at the delegate() boundary, which BOTH
// the SIGSYS path and the rewrite fast path share. At depth 0 (essentially every
// syscall) delegate uses the base slot, byte-identical to today plus three plain
// u32 stores. At depth>0 it leases a FRESH DELEG slot, so a nested trap can never
// alias the outer's in-flight ring. depth is a plain u32 written only by the
// owning thread between SYNCHRONOUS nested-signal boundaries (a nested SIGSYS runs
// to completion before the outer resumes) — async-signal-safe, no atomics, the
// same contract current_slot()/set_slot already rely on.
#[repr(C)]
struct SlotStack {
    depth: u32,
    frames: [u32; MAX_DEPTH], // the slot index leased at each depth (frame 0 = base)
}
// Layout sanity (compile-time): SlotStack is a plain u32 array (4-aligned), and the
// shared mapping is grown by exactly size_of::<SlotStack>() * MAX_SLOTS. A surprise
// here (e.g. accidental padding flipping the mmap math) fails the build.
const _: () = assert!(std::mem::size_of::<SlotStack>() == 4 * (MAX_DEPTH + 1));
const _: () = assert!(std::mem::align_of::<SlotStack>() == 4);
// Indexed by BASE slot = current_slot() (NOT by thread), so fork children re-key
// by their own base slot and never diverge. Lives in the SAME MAP_SHARED tail as
// SLOT_BM/ZYG (set in setup_sandbox_env), so it is fork-safe.
static mut SLOT_STACKS: *mut SlotStack = std::ptr::null_mut();
#[inline]
fn slot_stack(base: u64) -> *mut SlotStack {
    unsafe { SLOT_STACKS.add(base as usize) }
}

#[inline]
fn ring_word(p: *mut u32) -> &'static AtomicU32 {
    unsafe { AtomicU32::from_ptr(p) }
}
#[inline]
fn ring_at(slot: u64) -> *mut Ring {
    unsafe { RINGS.add(slot as usize) }
}
#[inline]
unsafe fn set_ring_owner(slot: u64, pid: i32, tid: i32) {
    let r = ring_at(slot);
    std::ptr::write_volatile(std::ptr::addr_of_mut!((*r).pid), pid);
    std::ptr::write_volatile(std::ptr::addr_of_mut!((*r).tid), tid);
}
#[inline]
unsafe fn clear_ring_owner(slot: u64) {
    set_ring_owner(slot, 0, 0);
}
/// The calling cell thread's slot (its `%gs` base). fsgsbase-only (verified
/// present); 1 instruction, TLS-free, safe in the signal handler.
#[inline(always)]
fn current_slot() -> u64 {
    let s: u64;
    unsafe { std::arch::asm!("rdgsbase {}", out(reg) s, options(nostack, nomem, preserves_flags)) };
    s
}

unsafe fn store_slot_snapshot_regs(slot: u64, g: &[libc::greg_t; 23], rax: i64) {
    if slot as usize >= MAX_SLOTS {
        return;
    }
    let r = ring_at(slot);
    for i in 0..18 {
        std::ptr::write_volatile(std::ptr::addr_of_mut!((*r).snap_gregs[i]), g[i] as i64);
    }
    std::ptr::write_volatile(std::ptr::addr_of_mut!((*r).snap_gregs[REG_RAX]), rax);
    let mut fs = 0u64;
    host(
        SYS_ARCH_PRCTL,
        ARCH_GET_FS,
        std::ptr::addr_of_mut!(fs) as u64,
        0,
        0,
        0,
        0,
    );
    std::ptr::write_volatile(std::ptr::addr_of_mut!((*r).snap_fs), fs);
    std::ptr::write_volatile(std::ptr::addr_of_mut!((*r).snap_valid), 1);
}

unsafe fn update_slot_snapshot_rax(slot: u64, rax: i64) {
    if slot as usize >= MAX_SLOTS {
        return;
    }
    let r = ring_at(slot);
    if std::ptr::read_volatile(std::ptr::addr_of!((*r).snap_valid)) == 0 {
        return;
    }
    std::ptr::write_volatile(std::ptr::addr_of_mut!((*r).snap_gregs[REG_RAX]), rax);
}

#[cfg(all(target_os = "linux", target_arch = "x86_64"))]
fn ring_snapshot_regs_for_pid(pid: i32, vtid: state_snap::Vpid) -> Option<state_snap::ThreadRegs> {
    if pid <= 0 {
        return None;
    }
    for i in 0..MAX_SLOTS as u64 {
        let r = ring_at(i);
        unsafe {
            if std::ptr::read_volatile(std::ptr::addr_of!((*r).pid)) != pid {
                continue;
            }
            if std::ptr::read_volatile(std::ptr::addr_of!((*r).snap_valid)) == 0 {
                continue;
            }
            let mut gregs = [0i64; 18];
            for j in 0..18 {
                gregs[j] = std::ptr::read_volatile(std::ptr::addr_of!((*r).snap_gregs[j]));
            }
            let fs = std::ptr::read_volatile(std::ptr::addr_of!((*r).snap_fs));
            let req = ring_word(std::ptr::addr_of_mut!((*r).request)).load(Ordering::Acquire);
            let resp = ring_word(std::ptr::addr_of_mut!((*r).response)).load(Ordering::Acquire);
            let xsave = if req != resp {
                let nr = std::ptr::read_volatile(std::ptr::addr_of!((*r).nr));
                let mut args = [0u64; 6];
                for (idx, arg) in args.iter_mut().enumerate() {
                    *arg = std::ptr::read_volatile(std::ptr::addr_of!((*r).args[idx]));
                }
                encode_pending_syscall(nr, args)
            } else {
                Vec::new()
            };
            return Some(state_snap::ThreadRegs {
                vtid,
                gregs,
                fs,
                xsave,
            });
        }
    }
    None
}

const PENDING_SYSCALL_MAGIC: &[u8; 8] = b"SMPEND1\0";

fn encode_pending_syscall(nr: i64, args: [u64; 6]) -> Vec<u8> {
    let mut out = Vec::with_capacity(8 + 8 + 6 * 8);
    out.extend_from_slice(PENDING_SYSCALL_MAGIC);
    out.extend_from_slice(&nr.to_le_bytes());
    for arg in args {
        out.extend_from_slice(&arg.to_le_bytes());
    }
    out
}

fn decode_pending_syscall(buf: &[u8]) -> Option<(i64, [u64; 6])> {
    if buf.len() != 8 + 8 + 6 * 8 || &buf[..8] != PENDING_SYSCALL_MAGIC {
        return None;
    }
    let nr = i64::from_le_bytes(buf[8..16].try_into().ok()?);
    let mut args = [0u64; 6];
    let mut off = 16;
    for arg in &mut args {
        *arg = u64::from_le_bytes(buf[off..off + 8].try_into().ok()?);
        off += 8;
    }
    Some((nr, args))
}

fn record_rseq_result(addr: u64, len: u64, flags: u64, sig: u64, ret: i64) {
    if ret != 0 {
        return;
    }
    let slot = current_slot() as usize;
    if slot >= MAX_SLOTS {
        return;
    }
    const RSEQ_FLAG_UNREGISTER: u64 = 1;
    if (flags & RSEQ_FLAG_UNREGISTER) != 0 {
        RSEQ_ADDR[slot].store(0, Ordering::Release);
        RSEQ_LEN[slot].store(0, Ordering::Release);
        RSEQ_SIG[slot].store(0, Ordering::Release);
    } else {
        RSEQ_LEN[slot].store(len, Ordering::Release);
        RSEQ_SIG[slot].store(sig, Ordering::Release);
        RSEQ_ADDR[slot].store(addr, Ordering::Release);
    }
}

/// Carry a forking thread's rseq registration tracking onto its child's fresh
/// slot. `current->rseq` is duplicated into the child by the kernel's fork, but
/// RSEQ_ADDR/LEN/SIG are keyed by ring slot and the child took a new (zeroed)
/// slot — so without this copy the child would lose the record and a later
/// in-child execve could not unregister the inherited registration (see the
/// SYS_FORK arm). Both reads see the parent's values CoW-inherited into this
/// child's own RSEQ_* copies. No-op when neither slot is valid.
fn register_rseq_for_child(parent_slot: u64, child_slot: u64) {
    let (p, c) = (parent_slot as usize, child_slot as usize);
    if p >= MAX_SLOTS || c >= MAX_SLOTS {
        return;
    }
    RSEQ_ADDR[c].store(RSEQ_ADDR[p].load(Ordering::Acquire), Ordering::Release);
    RSEQ_LEN[c].store(RSEQ_LEN[p].load(Ordering::Acquire), Ordering::Release);
    RSEQ_SIG[c].store(RSEQ_SIG[p].load(Ordering::Acquire), Ordering::Release);
}

fn unregister_current_rseq_for_exec() {
    let slot = current_slot() as usize;
    if slot >= MAX_SLOTS {
        return;
    }
    let addr = RSEQ_ADDR[slot].swap(0, Ordering::AcqRel);
    if addr == 0 {
        return;
    }
    let len = RSEQ_LEN[slot].swap(0, Ordering::AcqRel);
    let sig = RSEQ_SIG[slot].swap(0, Ordering::AcqRel);
    const RSEQ_FLAG_UNREGISTER: u64 = 1;
    unsafe {
        host(SYS_RSEQ, addr, len, RSEQ_FLAG_UNREGISTER, sig, 0, 0);
    }
}

fn unregister_host_libc_rseq() {
    unsafe {
        let offset_ptr = libc::dlsym(
            std::ptr::null_mut(),
            b"__rseq_offset\0".as_ptr() as *const c_char,
        ) as *const isize;
        let size_ptr = libc::dlsym(
            std::ptr::null_mut(),
            b"__rseq_size\0".as_ptr() as *const c_char,
        ) as *const u32;
        if offset_ptr.is_null() || size_ptr.is_null() {
            return;
        }
        let offset = std::ptr::read_volatile(offset_ptr);
        let len = std::ptr::read_volatile(size_ptr);
        if len == 0 {
            return;
        }

        let mut fs = 0u64;
        if host(
            SYS_ARCH_PRCTL,
            ARCH_GET_FS,
            std::ptr::addr_of_mut!(fs) as u64,
            0,
            0,
            0,
            0,
        ) != 0
        {
            return;
        }
        let addr = if offset >= 0 {
            fs.wrapping_add(offset as u64)
        } else {
            fs.wrapping_sub(offset.wrapping_neg() as u64)
        };
        const RSEQ_FLAG_UNREGISTER: u64 = 1;
        host(
            SYS_RSEQ,
            addr,
            len as u64,
            RSEQ_FLAG_UNREGISTER,
            0x5305_3053,
            0,
            0,
        );
    }
}

fn disabled_sigaltstack() -> [u8; 24] {
    let mut old = [0u8; 24];
    old[8..12].copy_from_slice(&(libc::SS_DISABLE as u32).to_le_bytes());
    old
}

fn reset_guest_sigaltstack(slot: u64) {
    if (slot as usize) < MAX_SLOTS {
        unsafe {
            GUEST_SIGALTSTACKS[slot as usize] = disabled_sigaltstack();
        }
    }
}

fn guest_sigaltstack(slot: u64) -> [u8; 24] {
    if (slot as usize) >= MAX_SLOTS {
        return disabled_sigaltstack();
    }
    unsafe {
        let cur = GUEST_SIGALTSTACKS[slot as usize];
        if cur == [0u8; 24] {
            disabled_sigaltstack()
        } else {
            cur
        }
    }
}

fn set_vfork_efd(slot: u64, efd: i32) {
    if (slot as usize) < MAX_SLOTS {
        unsafe {
            VFORK_EFDS[slot as usize] = efd;
        }
    }
}

fn take_vfork_efd(slot: u64) -> i32 {
    if (slot as usize) >= MAX_SLOTS {
        return -1;
    }
    unsafe {
        let efd = VFORK_EFDS[slot as usize];
        VFORK_EFDS[slot as usize] = -1;
        efd
    }
}

fn notify_vfork_release() {
    let efd = take_vfork_efd(current_slot());
    if efd < 0 {
        return;
    }
    let one: u64 = 1;
    unsafe {
        host(
            SYS_WRITE,
            efd as u64,
            std::ptr::addr_of!(one) as u64,
            8,
            0,
            0,
            0,
        );
        host(SYS_CLOSE, efd as u64, 0, 0, 0, 0, 0);
    }
}

fn wait_vfork_release(efd: i32) {
    if efd < 0 {
        return;
    }
    let mut word = 0u64;
    loop {
        let n = unsafe {
            host(
                SYS_READ,
                efd as u64,
                std::ptr::addr_of_mut!(word) as u64,
                8,
                0,
                0,
                0,
            )
        };
        if n == 8 || n != -4 {
            break;
        }
    }
    unsafe { host(SYS_CLOSE, efd as u64, 0, 0, 0, 0, 0) };
}

/// Per-thread signal ALT-STACK (one per ring slot, in shared BSS — each thread
/// touches only ITS slot's buffer, so no cross-thread contention). The SUD
/// SIGSYS handler runs on this via `SA_ONSTACK`, so a trapped syscall can ALWAYS
/// be delivered onto a known-good stack — even when the guest thread's own RSP is
/// exhausted or trashed. Running the handler on the guest stack is the classic
/// SUD-sandbox footgun: a signal cannot be delivered onto a bad RSP, so the cell
/// dies (SIGSEGV 139) — exactly the multi-threaded corruption that wedged
/// Chromium + the python eventfd/epoll stress. Sized for `MAX_DEPTH` nested traps.
const ALT_SZ: usize = 128 * 1024;
#[no_mangle]
static mut ALT_STACKS: [[u8; ALT_SZ]; MAX_SLOTS] = [[0u8; ALT_SZ]; MAX_SLOTS];
/// Install THIS thread's slot alt-stack. One `sigaltstack` syscall; called once
/// per thread at creation — the main cell in [`install_handler`], cloned threads
/// from the [`do_clone`] trampoline via [`child_thread_init`]. `%gs` must already
/// hold the thread's slot (set by the clone trampoline / default 0 for the main
/// cell). Idempotent and harmless if the slot is out of range.
unsafe fn register_altstack() {
    let slot = current_slot() as usize;
    if slot < MAX_SLOTS {
        let ss = libc::stack_t {
            ss_sp: std::ptr::addr_of_mut!(ALT_STACKS[slot]) as *mut c_void,
            ss_flags: 0,
            ss_size: ALT_SZ,
        };
        libc::sigaltstack(&ss, std::ptr::null_mut());
    }
}
/// Called by the [`do_clone`] trampoline IN THE CHILD thread (above the SUD floor,
/// so its syscalls run native), right after `%gs`+SUD are set and before guest
/// code resumes: give the new thread its own signal alt-stack. `#[no_mangle]` so
/// the `global_asm` `call child_thread_init` resolves (same convention as
/// `sentry_dispatch_simple`). Clobbers only caller-saved regs — the trampoline
/// reloads the guest GPRs from `CLONE_REGS` afterward.
#[no_mangle]
extern "C" fn child_thread_init() {
    unsafe {
        let slot = current_slot();
        set_ring_owner(
            slot,
            host(SYS_GETPID, 0, 0, 0, 0, 0, 0) as i32,
            host(SYS_GETTID, 0, 0, 0, 0, 0, 0) as i32,
        );
    }
    unsafe { register_altstack() };
}

/// Cell side: marshal a delegated syscall on a SPECIFIC ring `slot` and block
/// until done. The EXACT transport that `delegate` used before the per-depth
/// split — same volatile arg writes, request.store(Release), FUTEX_WAKE, adaptive
/// RING_SPIN spin, FUTEX_WAIT, read_volatile(ret). Uses only shared-memory writes
/// + futex (which the seccomp wall permits).
fn delegate_on(
    slot: u64,
    nr: i64,
    a: u64,
    b: u64,
    c: u64,
    d: u64,
    e: u64,
    f: u64,
    saved_mask: Option<u64>,
) -> i64 {
    unsafe {
        // Defer guest signals for the duration of this delegated syscall. While the
        // cell thread is parked here (FUTEX_WAIT on the ring response) it is running on
        // sentry's private SIGSYS altstack; a guest signal the host delivers now would
        // run the guest's handler NESTED on that altstack, corrupting cell_layer1's
        // frames — the `rustc_compile_then_run_via_chain` SIGSEGV/SIGUSR1 exit-139
        // crash. Block all maskable guest signals so the kernel keeps them PENDING
        // instead. The sender's kill/tgkill/tkill arm already writes the target
        // servicer's `intr_efd` (interrupt_slots_for_pid) so a blocking delegated
        // syscall (read/poll/accept/…) returns EINTR, and cell_layer1's `rt_sigreturn`
        // then restores the guest mask → the pending signal is delivered on the GUEST
        // stack, correctly. Only in SIGSYS-trap context (saved_mask=Some): the rewrite
        // fast path runs on the guest stack (not the altstack) and passes None, and
        // cell-local signal-waits (pause/rt_sigsuspend/futex) never reach delegate_on.
        if saved_mask.is_some() {
            block_guest_signals_until_sigreturn();
        }
        let r = ring_at(slot);
        let req = ring_word(std::ptr::addr_of_mut!((*r).request));
        let resp = ring_word(std::ptr::addr_of_mut!((*r).response));
        // Only this thread writes its ring's `request`, so next gen = current + 1.
        let k = req.load(Ordering::Relaxed) + 1;
        std::ptr::write_volatile(std::ptr::addr_of_mut!((*r).nr), nr);
        let ap = std::ptr::addr_of_mut!((*r).args) as *mut u64;
        ap.write_volatile(a);
        ap.add(1).write_volatile(b);
        ap.add(2).write_volatile(c);
        ap.add(3).write_volatile(d);
        ap.add(4).write_volatile(e);
        ap.add(5).write_volatile(f);
        if SYSCALLTRACE || SLOTDUMP {
            let s = current_slot() as usize;
            let (rip, caller) = if s < MAX_SLOTS {
                (LAST_TRAP_RIP[s], LAST_TRAP_CALLER[s])
            } else {
                (0, 0)
            };
            std::ptr::write_volatile(std::ptr::addr_of_mut!((*r).dbg_rip), rip);
            std::ptr::write_volatile(std::ptr::addr_of_mut!((*r).dbg_caller), caller);
        }
        req.store(k, Ordering::Release);
        host(SYS_FUTEX, req.as_ptr() as u64, FUTEX_WAKE, 1, 0, 0, 0);
        let mut spins = 0u32;
        loop {
            let cur = resp.load(Ordering::Acquire);
            if cur == k {
                break;
            }
            // Spin first (servicer usually answers in well under a context switch);
            // only block once the budget is spent.
            if spins < RING_SPIN {
                spins += 1;
                std::hint::spin_loop();
                continue;
            }
            host(
                SYS_FUTEX,
                resp.as_ptr() as u64,
                FUTEX_WAIT,
                cur as u64,
                0,
                0,
                0,
            );
        }
        std::ptr::read_volatile(std::ptr::addr_of!((*r).ret))
    }
}

/// Cell side: marshal a delegated syscall and block until done, with the per-trap
/// DEPTH lease that makes a nested SIGSYS (a guest signal handler delegating while
/// an outer delegate is parked) reentrancy-SAFE. Tracks depth per BASE slot in the
/// SHARED-mapping `SLOT_STACKS` array:
///   * depth 0 (every non-nested syscall — essentially all of them): use the BASE
///     slot, byte-identical to the old transport plus one depth read + a frame
///     store + a depth store/clear. Sub-nanosecond; no atomics, no syscall.
///   * depth > 0 (re-entry): lease a FRESH DELEG slot, stamp its pid + kind, ensure
///     a servicer for it (idempotent), run on it, then free it. The fresh slot can
///     NEVER alias the outer's in-flight ring.
///   * depth >= MAX_DEPTH: -EAGAIN (the deepest delegated call fails like an OS
///     refusal; fails CLOSED, never corrupts).
/// CRITICAL ORDERING: the depth increment + frame push happen BEFORE delegate_on
/// parks, so a nested SIGSYS delivered while the outer is parked observes depth>0
/// and takes a fresh slot. depth is written only by the owning thread between
/// synchronous nested-signal boundaries — async-signal-safe, no atomics.
fn delegate(nr: i64, a: u64, b: u64, c: u64, d: u64, e: u64, f: u64) -> i64 {
    // Sentry-internal / fast-path delegations carry no trapped guest sigmask, so
    // they don't defer guest signals (the guest isn't parked on the altstack here).
    delegate_masked(nr, a, b, c, d, e, f, None)
}
/// Like [`delegate`] but carries the guest's trapped signal mask so the parked
/// transport wait can DEFER guest signals (block-until-`rt_sigreturn`), preventing a
/// nested guest-handler from running on sentry's SIGSYS altstack. `dispatch_simple`'s
/// delegated default arm passes `trap_saved_mask` (Some on the SIGSYS path).
fn delegate_masked(
    nr: i64,
    a: u64,
    b: u64,
    c: u64,
    d: u64,
    e: u64,
    f: u64,
    saved_mask: Option<u64>,
) -> i64 {
    let base = current_slot();
    let ss = slot_stack(base);
    let d_now = unsafe { std::ptr::read_volatile(std::ptr::addr_of!((*ss).depth)) };
    if d_now == 0 {
        // Common path: use the base slot. Push it as frame 0 and set depth=1
        // BEFORE delegate_on parks (so a nested trap re-enters at depth>0).
        unsafe {
            std::ptr::write_volatile(std::ptr::addr_of_mut!((*ss).frames[0]), base as u32);
            std::ptr::write_volatile(std::ptr::addr_of_mut!((*ss).depth), 1);
        }
        let ret = delegate_on(base, nr, a, b, c, d, e, f, saved_mask);
        unsafe { std::ptr::write_volatile(std::ptr::addr_of_mut!((*ss).depth), 0) };
        ret
    } else if (d_now as usize) >= MAX_DEPTH {
        -11 // -EAGAIN: deepest delegated syscall fails closed (never corrupts)
    } else {
        // Re-entry: a guest signal handler is delegating while an outer delegate is
        // parked. Lease a FRESH DELEG slot so the two can never alias. The DELEG
        // pool's servicers are PRE-SPAWNED at supervisor startup (see
        // spawn_deleg_servicers) — we can't route a CTL_ENSURE_SERVICER through the
        // base slot here, because the base slot's servicer is BUSY running the outer
        // (parked) syscall. The bounded (DELEG_SLOTS) always-idle servicer pool
        // sidesteps that circular dependency, so the lease path stays purely
        // cell-side (alloc + delegate_on), no control round-trip.
        let fresh = alloc_deleg_slot();
        if fresh as usize >= MAX_SLOTS {
            return -11; // DELEG pool exhausted — fail closed
        }
        unsafe {
            let fr = ring_at(fresh as u64);
            set_ring_owner(
                fresh as u64,
                host(SYS_GETPID, 0, 0, 0, 0, 0, 0) as i32,
                host(SYS_GETTID, 0, 0, 0, 0, 0, 0) as i32,
            );
            std::ptr::write_volatile(std::ptr::addr_of_mut!((*fr).kind), RING_DELEG);
            std::ptr::write_volatile(std::ptr::addr_of_mut!((*fr).fork_parent), 0);
        }
        unsafe {
            std::ptr::write_volatile(std::ptr::addr_of_mut!((*ss).frames[d_now as usize]), fresh);
            std::ptr::write_volatile(std::ptr::addr_of_mut!((*ss).depth), d_now + 1);
        }
        let ret = delegate_on(fresh as u64, nr, a, b, c, d, e, f, saved_mask);
        unsafe { std::ptr::write_volatile(std::ptr::addr_of_mut!((*ss).depth), d_now) };
        // The slot is quiescent (we just read its response) — safe to free.
        free_deleg_slot(fresh);
        ret
    }
}

// Per-slot snapshot of the GUEST's general-purpose registers at a CLONE_THREAD
// trap, so `do_clone` can resume the new thread REGISTER-FAITHFUL — exactly like a
// real `clone(CLONE_VM|CLONE_THREAD)`: the child sees the parent's registers at the
// syscall, except rax=0 (clone return) and rsp=the new stack. CLONE_VM means the
// child shares this address space, so a plain static is visibly shared with it; the
// clone syscall between the parent's write and the child's read is a full barrier.
// Layout (15 u64, byte offsets the do_clone asm hard-codes):
//   0:r8 8:r9 16:r10 24:r11 32:r12 40:r13 48:r14 56:r15 64:rbx 72:rbp 80:rcx 88:rdx
//   96:rsi 104:rdi 112:rip(post-clone resume point).
// WHY this matters: musl passes the thread start fn in %r9 and the child invokes it
// via `call *%r9`; the OLD trampoline resumed via `jmp` after only setting rax=0, so
// the child ran with the trampoline's leftover regs (r9 = the resume RIP, rdx =
// WINDOW_FLOOR, …) and `call *%r9` jumped back into __clone's own child path → an
// infinite loop that never ran the thread body. glibc happened to survive because it
// passes fn on the CHILD STACK (preserved across clone), not in a register — which is
// why single-libc (glibc/static) thread tests passed while musl runtimes (node/V8,
// alpine Python threads) hung at startup. Restoring the full GPR image fixes both.
static mut CLONE_REGS: [[u64; 15]; MAX_SLOTS] = [[0; 15]; MAX_SLOTS];

// ─── thread creation: clone (cell-local) + a trampoline that arms SUD, sets the
// child's ring slot in %gs, restores the guest GPRs, then resumes guest code. The
// kernel handles CLONE_THREAD/CLONE_SETTLS/CLONE_CHILD_CLEARTID (the last wakes
// pthread_join). Constants below MUST match WINDOW_FLOOR / (USER_TOP-FLOOR). ──────
std::arch::global_asm!(
    ".globl do_clone",
    "do_clone:",
    "    push %r12",
    "    push %r13",
    "    push %r14",
    "    push %r15",
    "    mov 40(%rsp), %r14", // slot  (7th arg)
    "    mov 48(%rsp), %r13", // &SELECTOR (8th arg)
    "    mov 56(%rsp), %r12", // &CLONE_REGS[slot] (9th arg)
    "    mov 64(%rsp), %r15", // &ALT_STACKS[slot] (10th arg)
    "    mov %rcx, %r10",     // ctid -> clone's 4th reg
    "    mov $56, %rax",      // clone: rdi=flags rsi=stack rdx=ptid r10=ctid r8=tls
    "    syscall",
    "    test %rax, %rax",
    "    jz 1f",
    "    pop %r15", // parent: restore + return child tid
    "    pop %r14",
    "    pop %r13",
    "    pop %r12",
    "    ret",
    "1:  wrgsbase %r14",  // child: %gs base = ring slot
    "    mov $157, %rax", // prctl(PR_SET_SYSCALL_USER_DISPATCH, ON, …)
    "    mov $59, %rdi",
    "    mov $1, %rsi",
    "    movabs $0x2000000000, %rdx",       // WINDOW_FLOOR
    "    movabs $0x00007fdffffff000, %r10", // USER_TOP - WINDOW_FLOOR
    "    mov %r13, %r8",                    // &SELECTOR
    "    syscall",
    // Install this slot's real host alt-stack without calling Rust/libc from the
    // raw-cloned child. The guest-visible sigaltstack state is virtualized in
    // service(); this host stack is reserved for sentry's SIGSYS handler.
    "    sub $32, %rsp",
    "    mov %r15, (%rsp)",
    "    movl $0, 8(%rsp)",
    "    movq $131072, 16(%rsp)",
    "    mov $131, %rax", // sigaltstack(&ss, NULL)
    "    mov %rsp, %rdi",
    "    xor %rsi, %rsi",
    "    syscall",
    "    add $32, %rsp",
    // Restore the GUEST's captured GPRs from CLONE_REGS[slot] (r12). rsp already =
    // the new stack (the clone syscall set it); rax = 0 below = the child's clone
    // return. r12 (our pointer) is restored LAST, from the buffer.
    "    mov 0(%r12), %r8",
    "    mov 8(%r12), %r9",
    "    mov 16(%r12), %r10",
    "    mov 24(%r12), %r11",
    "    mov 40(%r12), %r13",
    "    mov 48(%r12), %r14",
    "    mov 56(%r12), %r15",
    "    mov 64(%r12), %rbx",
    "    mov 72(%r12), %rbp",
    "    mov 80(%r12), %rcx",
    "    mov 88(%r12), %rdx",
    "    mov 96(%r12), %rsi",
    "    mov 104(%r12), %rdi",
    "    push 112(%r12)",     // guest post-clone RIP → child stack
    "    mov 32(%r12), %r12", // restore guest r12 (frees the pointer)
    "    xor %rax, %rax",     // child sees clone()==0
    "    ret",                // resume guest at the post-clone RIP
    options(att_syntax)
);
// Signal-handler restorer for GUEST handlers. When the kernel delivers a signal
// (e.g. SIGALRM) to the cell, it runs the guest's handler (below the SUD floor);
// on return the handler jumps to its sa_restorer to issue `rt_sigreturn`. The
// guest's own restorer is below the floor, so SUD would TRAP its rt_sigreturn
// instead of letting it unwind the signal frame. We install THIS restorer (part
// of the cell binary — a PIE loaded ABOVE the floor) instead: its `rt_sigreturn`
// runs natively (no dispatch) and properly restores the interrupted context.
std::arch::global_asm!(
    ".globl sig_restorer",
    "sig_restorer:",
    "    mov $15, %rax", // rt_sigreturn
    "    syscall",
    options(att_syntax)
);
// Thread-teardown collapse: munmap(base,len) then exit(0), touching NO stack
// between (mirrors musl's own `__unmapself`). Entered from cell_layer1 when the
// guest is unmapping its OWN running stack (musl `pthread_exit`→`__unmapself`):
// returning to the guest would resume on the just-freed stack, and the guest's
// following `exit` SIGSYS would land cell_layer1 on dead memory → the
// multi-threaded SIGSEGV that gated every musl workload (node/V8, alpine python,
// chromium thread churn). Both syscalls are issued from the cell binary (ABOVE the
// SUD floor) so they run NATIVE — no re-trap — and `munmap`/`exit` are both
// allowlisted. `base` is in %rdi, `len` in %rsi per the SysV C ABI, which is
// exactly munmap's (%rdi,%rsi); nothing is pushed, so the freed stack is never
// touched again. Never returns.
std::arch::global_asm!(
    ".globl unmap_and_exit",
    "unmap_and_exit:",
    "    mov $11, %eax", // SYS_munmap (rdi=base, rsi=len already in place)
    "    syscall",
    "    xor %edi, %edi", // exit(0)
    "    mov $60, %eax",  // SYS_exit (thread exit)
    "    syscall",
    "    ud2", // unreachable
    options(att_syntax)
);
// Rewrite fast-path dispatcher (Phase 1b). Reached when a patched syscall site
// executes `callq *%rax`: with rax = the syscall number (0..~454), control lands
// in the VA-0 NOP sled, slides to the page's `jmp` at +512, and arrives here (in
// the cell binary, ABOVE the floor — so its own syscalls run native). On entry
// the call pushed the site's return address (site+2); args are live in
// rdi/rsi/rdx/r10/r8/r9; rax = nr.
//
// Control-flow syscalls (SENTINEL/clone/execve) need the trapped ucontext the
// fast path lacks, so they jmp to `cf_stub` (VA 600 in the trampoline page) which
// issues a real `syscall` → SUD trap → cell_layer1. Everything else builds a
// [nr,a,b,c,d,e,f] array, calls the shared simple dispatch, restores the guest's
// arg registers, sets rax = return, and `ret`s back to site+2 — no signal.
std::arch::global_asm!(
    ".globl rewrite_dispatch",
    "rewrite_dispatch:",
    "    cmp $0x5359, %rax", // SENTINEL
    "    je 2f",
    "    cmp $56, %rax", // clone
    "    je 2f",
    "    cmp $435, %rax", // clone3
    "    je 2f",
    "    cmp $59, %rax", // execve
    "    je 2f",
    "    push %r9", // build [nr,a,b,c,d,e,f] at rsp
    "    push %r8",
    "    push %r10",
    "    push %rdx",
    "    push %rsi",
    "    push %rdi",
    "    push %rax",
    "    mov %rsp, %rdi", // arg0 = &regs
    "    mov %rsp, %rax", // stash array base
    "    and $-16, %rsp", // align for the call
    "    push %rax",      // save array base
    "    push %rax",      // pad (keep 16-align)
    "    call sentry_dispatch_simple",
    "    pop %rcx", // drop pad
    "    pop %rcx", // rcx = array base
    "    mov %rcx, %rsp",
    "    mov 8(%rsp), %rdi", // restore guest arg regs
    "    mov 16(%rsp), %rsi",
    "    mov 24(%rsp), %rdx",
    "    mov 32(%rsp), %r10",
    "    mov 40(%rsp), %r8",
    "    mov 48(%rsp), %r9",
    "    add $56, %rsp", // pop the array
    "    ret",           // → site+2 (rax = return value)
    "2:",                // control-flow → cf_stub (VA 600)
    "    mov $600, %r11d",
    "    jmp *%r11",
    options(att_syntax)
);
extern "C" {
    fn sig_restorer();
    fn rewrite_dispatch();
    fn do_clone(
        flags: u64,
        stack: u64,
        ptid: u64,
        ctid: u64,
        tls: u64,
        rip: u64,
        slot: u64,
        sel: u64,
        regs: u64,
        alt_stack: u64,
    ) -> i64;
    fn unmap_and_exit(base: u64, len: u64) -> !;
}

unsafe fn clone_resume_rip(g: &[libc::greg_t; 23]) -> u64 {
    let trapped_rip = g[REG_RIP] as u64;
    if trapped_rip <= 0x1000 {
        *(g[REG_RSP] as *const u64)
    } else {
        trapped_rip
    }
}

unsafe fn capture_clone_regs(g: &[libc::greg_t; 23], slot: usize, rip: u64) -> *mut u64 {
    let cr = std::ptr::addr_of_mut!(CLONE_REGS[slot]) as *mut u64;
    *cr.add(0) = g[REG_R8] as u64;
    *cr.add(1) = g[REG_R9] as u64;
    *cr.add(2) = g[REG_R10] as u64;
    *cr.add(3) = g[REG_R11] as u64;
    *cr.add(4) = g[REG_R12] as u64;
    *cr.add(5) = g[REG_R13] as u64;
    *cr.add(6) = g[REG_R14] as u64;
    *cr.add(7) = g[REG_R15] as u64;
    *cr.add(8) = g[REG_RBX] as u64;
    *cr.add(9) = g[REG_RBP] as u64;
    *cr.add(10) = g[REG_RCX] as u64;
    *cr.add(11) = g[REG_RDX] as u64;
    *cr.add(12) = g[REG_RSI] as u64;
    *cr.add(13) = g[REG_RDI] as u64;
    *cr.add(14) = rip;
    cr
}

unsafe fn handle_clone(
    g: &mut [libc::greg_t; 23],
    flags: u64,
    stack: u64,
    parent_tid: u64,
    child_tid: u64,
    tls: u64,
) -> i64 {
    let slot = alloc_slot();
    if slot as usize >= MAX_SLOTS {
        return -11; // -EAGAIN
    }
    if delegate(CTL_ENSURE_SERVICER, slot as u64, 0, 0, 0, 0, 0) < 0 {
        free_slot(slot);
        return -11; // -EAGAIN
    }
    // A CLONE_VM clone (a thread, or a CLONE_VM|CLONE_VFORK posix_spawn child) adds a
    // task that SHARES this address space. Record it so a later in-cell execve does not
    // rewind the shared mmap arena out from under the still-live sibling (see MM_USERS /
    // emulate_execve). The counter lives in the shared mm, so the increment is visible to
    // every sharer — including the very thread that will exec. Increment-only by design.
    if (flags & CLONE_VM) != 0 {
        MM_USERS.fetch_add(1, Ordering::AcqRel);
    }

    if (flags & CLONE_THREAD) != 0 {
        if stack == 0 {
            free_slot(slot);
            return -22; // -EINVAL
        }

        // CLONE_VM threads share the parent's address space, so the parent's tgid
        // is the correct process_vm_* target for the child slot.
        set_ring_owner(slot as u64, host(SYS_GETPID, 0, 0, 0, 0, 0, 0) as i32, 0);
        let rip = clone_resume_rip(g);
        let cr = capture_clone_regs(g, slot as usize, rip);
        let alt_stack = std::ptr::addr_of_mut!(ALT_STACKS[slot as usize]) as u64;
        let r = do_clone(
            flags & !CLONE_CLEAR_SIGHAND,
            stack,
            parent_tid,
            child_tid,
            tls,
            rip,
            slot as u64,
            std::ptr::addr_of!(SELECTOR) as u64,
            cr as u64,
            alt_stack,
        );
        if r < 0 {
            free_slot(slot);
        } else {
            set_ring_owner(
                slot as u64,
                host(SYS_GETPID, 0, 0, 0, 0, 0, 0) as i32,
                r as i32,
            );
        }
        return r;
    }

    // Process-spawning clone (fork/posix_spawn/vfork shape). CoW child processes
    // must be represented as separate sentry slots with their own fd table view.
    // rseq is INHERITED by such children (the kernel duplicates current->rseq for any
    // clone WITHOUT CLONE_THREAD), but our RSEQ_* tracking is per-slot and the child
    // took a fresh slot — so carry the parent slot's registration onto it, exactly as
    // the SYS_FORK arm does, so a later in-child execve unregisters the inherited
    // registration instead of leaving current->rseq dangling into a soon-to-be-remapped
    // arena (→ the rseq-fixup SIGSEGV). The hot path is glibc posix_spawn →
    // clone(CLONE_VM|CLONE_VFORK, stack) → execve(the linker) during a `rustc` compile.
    // Written parent-side BEFORE the clone so both CoW (fork-shape) and CLONE_VM
    // (shared-memory) children observe it without depending on the child's setup path.
    register_rseq_for_child(current_slot(), slot as u64);
    let vfork_like = (flags & CLONE_VFORK) != 0;
    let vfork_efd = if vfork_like {
        host(SYS_EVENTFD2, 0, 0, 0, 0, 0, 0) as i32
    } else {
        -1
    };
    if vfork_efd >= 0 {
        set_vfork_efd(slot as u64, vfork_efd);
    }
    let pflags = flags & !(CLONE_VM | CLONE_VFORK | CLONE_CLEAR_SIGHAND);
    delegate(
        CTL_FORK_TABLE,
        slot as u64,
        SYS_CLONE as u64,
        flags,
        0,
        0,
        0,
    );
    (*ring_at(slot as u64)).fork_parent = host(SYS_GETPID, 0, 0, 0, 0, 0, 0) as i32;

    let child = if stack != 0 {
        let rip = clone_resume_rip(g);
        let cr = capture_clone_regs(g, slot as usize, rip);
        let alt_stack = std::ptr::addr_of_mut!(ALT_STACKS[slot as usize]) as u64;
        do_clone(
            pflags,
            stack,
            parent_tid,
            child_tid,
            tls,
            rip,
            slot as u64,
            std::ptr::addr_of!(SELECTOR) as u64,
            cr as u64,
            alt_stack,
        )
    } else if pflags == SIGCHLD_FLAG {
        host(SYS_FORK, 0, 0, 0, 0, 0, 0)
    } else {
        host(SYS_CLONE, pflags, 0, parent_tid, child_tid, tls, 0)
    };

    if child == 0 && stack == 0 {
        set_slot(slot as u64);
        // stack==0 ⇒ this was serviced as a real CoW fork (host SYS_FORK), so the child
        // owns a fresh address space regardless of the requested CLONE_VM bit — it is the
        // sole user of its mm. Reset the (CoW-inherited) sharer count so a later in-child
        // execve rewinds the arena correctly.
        MM_USERS.store(1, Ordering::Release);
        set_ring_owner(
            slot as u64,
            host(SYS_GETPID, 0, 0, 0, 0, 0, 0) as i32,
            host(SYS_GETTID, 0, 0, 0, 0, 0, 0) as i32,
        );
        host(
            157,
            PR_SET_SYSCALL_USER_DISPATCH as u64,
            PR_SYS_DISPATCH_ON,
            WINDOW_FLOOR,
            USER_TOP - WINDOW_FLOOR,
            std::ptr::addr_of!(SELECTOR) as u64,
            0,
        );
        0
    } else if child < 0 {
        if vfork_efd >= 0 {
            set_vfork_efd(slot as u64, -1);
            host(SYS_CLOSE, vfork_efd as u64, 0, 0, 0, 0, 0);
        }
        (*ring_at(slot as u64)).fork_parent = 0;
        delegate(CTL_FORK_CANCEL, slot as u64, 0, 0, 0, 0, 0);
        child
    } else {
        set_ring_owner(slot as u64, child as i32, child as i32);
        delegate(CTL_BIND_SLOT, slot as u64, child as u64, 0, 0, 0, 0);
        wait_vfork_release(vfork_efd);
        child
    }
}

/// Build the VA-0 rewrite trampoline (Phase 1b): a NOP sled over 0..511 (so a
/// `callq *%rax` to address `nr` slides down), a `jmp` to [`rewrite_dispatch`] at
/// +512, and `cf_stub` (`syscall; ret`) at +600 for control-flow fallback. Mapped
/// by the SUPERVISOR before it forks cells (it still has CAP_SYS_RAWIO then —
/// mapping below `mmap_min_addr` needs it; cells inherit the page via CoW). On
/// failure (no privilege / mmap_min_addr), rewrite is disabled. Only called when
/// `SENTRY_REWRITE` is set.
fn setup_trampoline() {
    let p = unsafe {
        libc::mmap(
            std::ptr::null_mut(),
            4096,
            libc::PROT_READ | libc::PROT_WRITE | libc::PROT_EXEC,
            libc::MAP_PRIVATE | libc::MAP_ANONYMOUS | libc::MAP_FIXED,
            -1,
            0,
        )
    };
    if p != std::ptr::null_mut() {
        // couldn't get VA 0 (needs CAP_SYS_RAWIO or mmap_min_addr=0) → no rewrite
        REWRITE_ON.store(false, Ordering::Relaxed);
        return;
    }
    unsafe {
        // `p` is VA 0; black_box so the compiler can't const-fold it to a null
        // pointer (which it would then reject the writes through).
        let base = std::hint::black_box(p) as *mut u8;
        std::ptr::write_bytes(base, 0x90, 512); // NOP sled 0..511
                                                // +512: jmp qword ptr [rip+0]; then the 8-byte absolute &rewrite_dispatch
        let jmp = [0xff_u8, 0x25, 0, 0, 0, 0];
        std::ptr::copy_nonoverlapping(jmp.as_ptr(), base.add(512), 6);
        let disp = (rewrite_dispatch as usize as u64).to_le_bytes();
        std::ptr::copy_nonoverlapping(disp.as_ptr(), base.add(518), 8);
        // +600: cf_stub = `syscall; ret` (0F 05 C3) — below the floor, so SUD traps.
        let cf = [0x0f_u8, 0x05, 0xc3];
        std::ptr::copy_nonoverlapping(cf.as_ptr(), base.add(600), 3);
        // RX (drop write) — belt-and-suspenders W^X for the trampoline.
        libc::mprotect(base as *mut c_void, 4096, libc::PROT_READ | libc::PROT_EXEC);
    }
}

/// Lazy rewrite (Phase 1b): the SUD trap proved `rip-2` is a real `syscall` site,
/// so patch it `0F 05` → `FF D0` (`callq *%rax`). Future hits dispatch via the
/// VA-0 trampoline with no signal. Skips control-flow nrs (clone/execve/SENTINEL,
/// which need the trapped ucontext) and the trampoline page itself. No-op unless
/// rewrite is on; never runs in warm/zygote mode (the SENTINEL must keep trapping).
fn maybe_patch_site(rip: u64, nr: i64) {
    if !REWRITE_ON.load(Ordering::Relaxed) || WARM_MODE.load(Ordering::Relaxed) {
        return;
    }
    if nr == SENTINEL || nr == SYS_CLONE || nr == SYS_CLONE3 || nr == SYS_EXECVE {
        return;
    }
    if rip <= 0x1000 {
        return; // the trampoline page (cf_stub) — never patch
    }
    let site = rip - 2; // the 2-byte `syscall` instruction
    unsafe {
        let p = site as *const u8;
        if *p != 0x0f || *p.add(1) != 0x05 {
            return; // not a `syscall` site (e.g. an int 0x80 or a partial trap)
        }
        let page = site & !0xfff;
        let span: u64 = if site & 0xfff == 0xfff {
            0x2000
        } else {
            0x1000
        };
        host(
            SYS_MPROTECT,
            page,
            span,
            (libc::PROT_READ | libc::PROT_WRITE | libc::PROT_EXEC) as u64,
            0,
            0,
            0,
        );
        let pp = site as *mut u8;
        *pp = 0xff; // callq *%rax
        *pp.add(1) = 0xd0;
        host(
            SYS_MPROTECT,
            page,
            span,
            (libc::PROT_READ | libc::PROT_EXEC) as u64,
            0,
            0,
            0,
        );
    }
}

// ─── cell-local memory management (arena + brk), as in M1 ────────────────────
static mut HEAP_BASE: u64 = 0;
// ATOMIC: concurrent brk() growers (glibc may sbrk the main arena from more than one
// thread if its arena lock is contended/raced under sentry's futex emulation) must never
// observe a torn cursor nor get OVERLAPPING ranges. A non-atomic read-modify-write here
// let two growers both read the same stale cursor, both hand out [cur, ...), and one's
// zero-fill wipe the other's freshly-allocated chunk -> overlapping allocations + silent
// data loss (the canonical victim: node's child_process env buffer, where DATABASE_URL is
// clobbered by a sibling allocation mid-spawn). Mirrors arena_alloc's atomic bump.
static HEAP_CUR: std::sync::atomic::AtomicU64 = std::sync::atomic::AtomicU64::new(0);
static mut HEAP_END: u64 = 0;
const HEAP_SIZE: u64 = 64 * 1024 * 1024;
// The guest mmap arena cursor. ATOMIC, not `static mut`: the SUD/SIGSYS handler
// (`cell_layer1` → `guest_mmap` → `arena_alloc`) runs PER GUEST THREAD, so a
// multi-threaded guest (rustc's codegen/jobserver threads, node/V8, Chromium)
// issues concurrent mmaps that hit `arena_alloc` on multiple CPUs at once. A plain
// `ARENA_CUR -= need` is a non-atomic read-modify-write: two racing callers read the
// same cursor and get the SAME (overlapping) address, so the second mmap MAP_FIXED-
// clobbers the first's region → silent guest-memory corruption (the long-standing
// "reentrant_under_load" smash; deterministic under rustc, which mmaps libstd.rlib +
// its huge .so's from several threads). A CAS loop hands out strictly disjoint ranges.
static ARENA_CUR: std::sync::atomic::AtomicU64 = std::sync::atomic::AtomicU64::new(ARENA_TOP);
// Number of tasks sharing THIS cell's address space (mm). A real execve(2) from a
// multi-threaded process kills every sibling thread (de_thread) so the new image
// gets the address space to itself; sentry emulates execve IN-PROCESS and cannot
// kill OS-thread siblings, so when a CLONE_VM/CLONE_THREAD sharer is still alive,
// `emulate_execve` MUST NOT rewind ARENA_CUR back to ARENA_TOP — doing so re-hands
// the freed-looking high arena out to the new image's ld.so, which MAP_FIXED-maps a
// (multi-MiB) library straight over a live sibling's stack/TLS → the sibling later
// `ret`s through library bytes to a non-canonical address → #GP → fatal SIGSEGV
// (SI_KERNEL). This is the long-standing multi-threaded SUD crash: a rustup shim
// (tokio worker threads) exec'ing the real rustc, and rustc's own posix_spawn
// (CLONE_VM|CLONE_VFORK) of the linker while codegen threads run. Tracked as: start
// at 1; +1 whenever a CLONE_VM clone adds a sharer (the static lives in the shared
// mm, so the increment is visible to every sharer); reset to 1 in a CoW fork child
// (it gets its OWN fresh mm). Increment-only (no per-exit decrement) is deliberately
// conservative: an over-count only suppresses the arena rewind (wastes some arena),
// never clobbers. `emulate_execve` rewinds only when this is 1 (sole mm user).
static MM_USERS: std::sync::atomic::AtomicU32 = std::sync::atomic::AtomicU32::new(1);
const MAP_FIXED_BIT: u64 = libc::MAP_FIXED as u64;
const MAP_FIXED_NOREPLACE_BIT: u64 = 0x100000;
const PROT_EXEC_BIT: u64 = libc::PROT_EXEC as u64;

fn reset_brk_heap_for_exec() -> i64 {
    let (old_base, old_end) = unsafe { (HEAP_BASE, HEAP_END) };
    let h = unsafe {
        host(
            SYS_MMAP,
            0,
            HEAP_SIZE,
            PROT_RW,
            (libc::MAP_PRIVATE | libc::MAP_ANONYMOUS) as u64,
            u64::MAX,
            0,
        )
    };
    if h < 0 {
        return h;
    }
    let new_base = h as u64;
    unsafe {
        HEAP_BASE = new_base;
        HEAP_END = new_base + HEAP_SIZE;
    }
    HEAP_CUR.store(new_base, Ordering::Release);
    if old_base != 0 && old_end > old_base {
        unsafe {
            host(SYS_MUNMAP, old_base, old_end - old_base, 0, 0, 0, 0);
        }
    }
    0
}

fn overlaps_sud_host_region(addr: u64, len: u64) -> bool {
    let end = page_up(addr.saturating_add(len));
    addr < USER_TOP && end > WINDOW_FLOOR
}

#[repr(C)]
struct GuestVmaSlot {
    lo: std::sync::atomic::AtomicU64,
    hi: std::sync::atomic::AtomicU64,
}

const GUEST_VMA_MAX: usize = 4096;
static GUEST_VMA_NEXT: std::sync::atomic::AtomicU32 = std::sync::atomic::AtomicU32::new(0);
static GUEST_VMAS: [GuestVmaSlot; GUEST_VMA_MAX] = [const {
    GuestVmaSlot {
        lo: std::sync::atomic::AtomicU64::new(0),
        hi: std::sync::atomic::AtomicU64::new(0),
    }
}; GUEST_VMA_MAX];

fn guest_vma_note_mapping(addr: u64, len: u64) {
    if len == 0 {
        return;
    }
    let start = page_down(addr);
    let end = page_up(addr.saturating_add(len));
    if end <= WINDOW_FLOOR || start >= USER_TOP || end <= start {
        return;
    }
    let idx = GUEST_VMA_NEXT.fetch_add(1, Ordering::AcqRel) as usize;
    if idx >= GUEST_VMA_MAX {
        return;
    }
    // Publish hi first and lo last; capture treats lo==0 as an unused slot.
    GUEST_VMAS[idx]
        .hi
        .store(end.min(USER_TOP), Ordering::Release);
    GUEST_VMAS[idx]
        .lo
        .store(start.max(WINDOW_FLOOR), Ordering::Release);
}

fn guest_vma_forget_range(addr: u64, len: u64) {
    if len == 0 {
        return;
    }
    let start = page_down(addr);
    let end = page_up(addr.saturating_add(len));
    if end <= WINDOW_FLOOR || start >= USER_TOP || end <= start {
        return;
    }
    let n = (GUEST_VMA_NEXT.load(Ordering::Acquire) as usize).min(GUEST_VMA_MAX);
    for slot in &GUEST_VMAS[..n] {
        let lo = slot.lo.load(Ordering::Acquire);
        let hi = slot.hi.load(Ordering::Acquire);
        if lo != 0 && start < hi && end > lo {
            slot.lo.store(0, Ordering::Release);
            slot.hi.store(0, Ordering::Release);
        }
    }
}

// ─── zygote fork-from-warm restore ───────────────────────────────────────────
// A warm cell runs the guest to a CHECKPOINT (the guest issues the SENTINEL
// syscall when "ready"); the handler captures the guest's full register file and
// `siglongjmp`s back to a zygote loop in `cell_main`. Each acquire `fork()`s the
// warm (CoW-shared) image and resumes the guest at the captured registers —
// skipping the load + ld.so relocation + the guest's own init. (Spike: M3.0.)
const SENTINEL: i64 = 0x5359; // guest `syscall(0x5359)` ⇒ "checkpoint me"
static WARM_MODE: std::sync::atomic::AtomicBool = std::sync::atomic::AtomicBool::new(false);
// Rewrite fast-path (Phase 1b, opt-in via SENTRY_REWRITE): when on, a syscall that
// traps via SUD has its `0F 05` site patched to `FF D0` (`callq *%rax`) → the VA-0
// trampoline → the fast dispatcher, so future hits skip the ~2 µs SIGSYS trap.
// Set in setup_sandbox_env (CoW-inherited by cells); off by default.
static REWRITE_ON: std::sync::atomic::AtomicBool = std::sync::atomic::AtomicBool::new(false);
// Driven (`Pool::warm`/`acquire`) vs self-contained bench (`zygote_bench`).
static ZYG_DRIVEN: std::sync::atomic::AtomicBool = std::sync::atomic::AtomicBool::new(false);
static mut CAPTURED: [i64; 18] = [0; 18]; // guest gregs at the checkpoint
static mut GUEST_FS: u64 = 0; // guest %fs (TLS) at the checkpoint — restored on resume
static RESTORE_PENDING_SYSCALL: AtomicBool = AtomicBool::new(false);
static mut RESTORE_PENDING_NR: i64 = 0;
static mut RESTORE_PENDING_ARGS: [u64; 6] = [0; 6];
static mut JMPBUF: [u64; 32] = [0; 32]; // sigjmp_buf for the checkpoint longjmp
extern "C" {
    fn __sigsetjmp(env: *mut c_void, savesigs: c_int) -> c_int;
    fn siglongjmp(env: *mut c_void, val: c_int) -> !;
}
/// Cross-process zygote signaling (in the shared ring page): the warm cell sets
/// `ready` at the checkpoint; an acquirer bumps `req` (futex-wake); the warm cell
/// forks an instance, reaps it, stores `exit`, bumps `done` (futex-wake).
#[repr(C)]
struct ZygShared {
    ready: u32,
    req: u32,
    done: u32,
    exit: i32,
    mean_ns: u64, // bench: mean restore latency
    // L0b running-instance protocol (the supervisor publishes `run_mode` BEFORE the
    // `req` store-release, so the warm cell observes it after its `req` load-acquire):
    //   0 = run-to-completion: fork, WAIT4, CTL_REAP, report exit code (the original
    //       acquire / acquire_capture path — unchanged).
    //   1 = detached: fork a long-lived instance (a warm daemon), record its pid in
    //       `inst_pid`, report `done` immediately; do NOT wait/reap (it owns its slot
    //       + servicer until a RELEASE). For `Pool::acquire_running`.
    //   2 = release: SIGKILL + reap the instance named in `inst_pid` (in); no fork.
    run_mode: u32,
    inst_pid: i32, // mode 1: the detached instance pid (out); mode 2: victim (in)
}
// Layout note: `ZYG` sits at `tail + SLOT_WORDS*8` (right after the multi-word slot
// bitmap) with `SLOT_STACKS` at `tail + SLOT_WORDS*8 + 64` (see setup_sandbox_env) —
// a 64-byte slot. ZygShared is 32 bytes, well within that slot.
static mut ZYG: *mut ZygShared = std::ptr::null_mut();

// For execve emulation: the cell's own TLS (to restore before std/heavy lifting,
// since the handler otherwise runs with the guest's `%fs`), and the rootfs to
// load the new image from.
#[no_mangle]
static mut CELL_FS: u64 = 0;
static mut CELL_ROOT: Option<std::path::PathBuf> = None;

fn page_up(x: u64) -> u64 {
    (x + 0xFFF) & !0xFFF
}
fn page_down(x: u64) -> u64 {
    x & !0xFFF
}
fn arena_alignment(need: u64) -> u64 {
    const GIB: u64 = 1 << 30;
    if need >= 32 * GIB {
        32 * GIB
    } else if need >= 4 * GIB {
        4 * GIB
    } else if need >= GIB {
        GIB
    } else {
        0x1000
    }
}

/// Pick a low-arena slot for an addr==0, non-FIXED mapping (so guest code stays
/// below the SUD floor). Returns 0 on exhaustion.
fn arena_alloc(len: u64) -> u64 {
    use std::sync::atomic::Ordering;
    let need = page_up(len);
    let align = arena_alignment(need);
    // Atomic bump-down with a CAS retry: concurrent callers (per-thread SUD handlers)
    // never observe a torn cursor and always get disjoint ranges. AcqRel so the new
    // cursor (and the disjoint region it implies) is visible before the mmap that uses it.
    let mut cur = ARENA_CUR.load(Ordering::Relaxed);
    loop {
        let end = cur & !(align - 1);
        let nc = end.saturating_sub(need);
        if nc < ARENA_LIMIT {
            return 0; // exhausted
        }
        match ARENA_CUR.compare_exchange_weak(cur, nc, Ordering::AcqRel, Ordering::Relaxed) {
            Ok(_) => return nc,
            Err(actual) => cur = actual,
        }
    }
}

fn arena_reclaim_mapping(addr: u64, len: u64) {
    if len == 0 {
        return;
    }
    let start = page_down(addr);
    let end = page_up(addr.saturating_add(len)).min(ARENA_TOP);
    if start < ARENA_LIMIT || end <= start || end > ARENA_TOP {
        return;
    }
    let mut cur = ARENA_CUR.load(std::sync::atomic::Ordering::Relaxed);
    while cur == start {
        match ARENA_CUR.compare_exchange_weak(
            cur,
            end,
            std::sync::atomic::Ordering::AcqRel,
            std::sync::atomic::Ordering::Relaxed,
        ) {
            Ok(_) => break,
            Err(actual) => cur = actual,
        }
    }
}

fn arena_note_mapping(addr: u64, len: u64) {
    if len == 0 {
        return;
    }
    let start = page_down(addr);
    let end = page_up(addr.saturating_add(len));
    if end <= ARENA_LIMIT || start >= ARENA_TOP {
        return;
    }
    let next = start.max(ARENA_LIMIT);
    let mut cur = ARENA_CUR.load(std::sync::atomic::Ordering::Relaxed);
    while next < cur {
        match ARENA_CUR.compare_exchange_weak(
            cur,
            next,
            std::sync::atomic::Ordering::AcqRel,
            std::sync::atomic::Ordering::Relaxed,
        ) {
            Ok(_) => break,
            Err(actual) => cur = actual,
        }
    }
}

const MAP_ANON_BIT: u64 = libc::MAP_ANONYMOUS as u64;
const PROT_RW: u64 = (libc::PROT_READ | libc::PROT_WRITE) as u64;

// Loaded-image guard: the byte spans of the guest main exe and the ELF interpreter
// (ld.so) as mapped by `load_elf`. A guest that mmaps with a non-binding ADDRESS
// HINT (addr!=0, no MAP_FIXED) overlapping one of these must NOT be allowed to
// clobber it — node/V8 reserve the pointer-compression cage with exactly such a hint
// (≈ ld.so_base + 0x80000, which falls INSIDE ld-musl's tail), and the force-MAP_FIXED
// transport then destroyed musl's own .rodata/.bss pages (replaced with PROT_NONE) →
// musl read its `""` .rodata literal as not-present → SIGSEGV. Each pair is overwritten
// by `load_elf` per load (so execve re-load refreshes it). Written single-threaded
// during cell/exec setup before guest code runs; read in guest_mmap / munmap / mremap.
static EXE_SPAN_LO: std::sync::atomic::AtomicU64 = std::sync::atomic::AtomicU64::new(0);
static EXE_SPAN_HI: std::sync::atomic::AtomicU64 = std::sync::atomic::AtomicU64::new(0);
static INTERP_SPAN_LO: std::sync::atomic::AtomicU64 = std::sync::atomic::AtomicU64::new(0);
static INTERP_SPAN_HI: std::sync::atomic::AtomicU64 = std::sync::atomic::AtomicU64::new(0);

/// Record a loaded image's [lo, hi) span. `load_elf` calls this for the exe and the
/// interpreter; the interp pair is keyed by `base == INTERP_BASE`.
fn record_loader_span(base: u64, lo: u64, hi: u64) {
    let (slo, shi) = if base == INTERP_BASE {
        (&INTERP_SPAN_LO, &INTERP_SPAN_HI)
    } else {
        // The exe must fit below ld.so. If its top reaches INTERP_BASE, ld.so would be
        // mapped over the exe's own code (a direct `call` into a clobbered function →
        // SEGV in ld.so's header). Fail loud at load instead of corrupting silently.
        if hi > INTERP_BASE {
            die(b"sentry: main exe top exceeds INTERP_BASE (ld.so base) - raise INTERP_BASE/ARENA_LIMIT\n");
        }
        (&EXE_SPAN_LO, &EXE_SPAN_HI)
    };
    shi.store(hi, Ordering::Relaxed);
    slo.store(lo, Ordering::Relaxed); // LO last: overlaps_loader gates on LO != 0
}

/// True iff [start, end) intersects the guest exe or interpreter image span.
fn overlaps_loader(start: u64, end: u64) -> bool {
    for (slo, shi) in [
        (&EXE_SPAN_LO, &EXE_SPAN_HI),
        (&INTERP_SPAN_LO, &INTERP_SPAN_HI),
    ] {
        let lo = slo.load(Ordering::Relaxed);
        if lo != 0 && start < shi.load(Ordering::Relaxed) && end > lo {
            return true;
        }
    }
    false
}

/// MAP_SHARED bit (== 1). A guest `mmap(MAP_SHARED, fd)` is REAL cross-process
/// shared memory — the canonical use is a memfd shared between a parent and the
/// children it forks/launches (Chromium's persistent_memory_allocator + mojo, the
/// JVM, glibc shm). The cell can't honor it locally (it holds no host fd), and the
/// decomposed pread-copy below FAKES it as a private per-process copy, so writes
/// never propagate → Chromium's "Corruption detected in shared-memory segment".
const MAP_SHARED_BIT: u64 = libc::MAP_SHARED as u64;

/// The cross-cell shared-memory POOL: one big `MAP_SHARED|ANON` region created in
/// the supervisor BEFORE any cell forks (in `setup_sandbox_env`), so every cell
/// inherits it at the SAME virtual address (exactly like the ring TABLE). A guest
/// `mmap(MAP_SHARED, fd)` is backed by a slice of this pool keyed by the fd's
/// `(dev, ino)`, so every cell mapping the same memfd/file gets the SAME pages —
/// true cross-process sharing. `0` until set up (⇒ the caller falls back to the
/// private-copy path). This is VIRTUAL space; only touched pages consume RAM.
const SHM_POOL_SIZE: u64 = 64 << 30;
static SHM_POOL_BASE: AtomicU64 = AtomicU64::new(0);
static SHM_POOL_NEXT: AtomicU64 = AtomicU64::new(0);
/// Is `va` inside the shared pool? (munmap/mremap of a pool slice is a no-op /
/// rejected — the slice is shared and must not be torn out of THIS cell's view.)
fn in_shm_pool(va: u64) -> bool {
    let base = SHM_POOL_BASE.load(Ordering::Relaxed);
    base != 0 && va >= base && va < base + SHM_POOL_SIZE
}
#[derive(Clone, Copy)]
struct ShmSlice {
    pool_off: u64,
    capacity: u64,
    backing_fd: i32,
}

/// Counter surfaced under SENTRY_IPCTRACE: every increment is a `mmap(MAP_SHARED,
/// fd)` that the supervisor could NOT back with a coherent shared-pool slice and so
/// `guest_mmap` refused with -ENOMEM (rather than the old silent fall-through to an
/// incoherent private per-process copy, which corrupts ipcz's release/acquire
/// fragment handshake → silently empty parcels → ChildProcessHost::Ping never
/// completes → Chromium's 15s "no connection" watchdog kills the utility child).
/// MUST stay 0 for a healthy run; a nonzero value localizes the failure to the pool
/// (unstattable fd / dup failure / pool exhausted) instead of producing silent IPC
/// corruption downstream.
static SHM_ENOMEM_DECLINES: AtomicU64 = AtomicU64::new(0);

#[derive(Clone, Copy)]
struct ShmMap {
    addr: u64,
    len: u64,
    file_off: u64,
    key: (u64, u64),
    writable: bool,
}

/// `(dev, ino)` → pool slice metadata: shared identity across a memfd's dups
/// (fork dups the fd → same `(dev,ino)` → same slice). Supervisor-side (slice
/// allocation happens while servicing the delegated `mmap`).
fn shm_slices() -> &'static Mutex<HashMap<(u64, u64), ShmSlice>> {
    static S: OnceLock<Mutex<HashMap<(u64, u64), ShmSlice>>> = OnceLock::new();
    S.get_or_init(|| Mutex::new(HashMap::new()))
}
/// Free shared-pool extents whose backing objects are no longer mapped by any
/// live/pending cell. The pool is virtual address space, so reclaim has to return
/// both physical pages (MADV_DONTNEED) and the virtual slice; otherwise long
/// Chromium runs can exhaust the monotonic allocator despite unmapping normally.
fn shm_free_slices() -> &'static Mutex<Vec<(u64, u64)>> {
    static F: OnceLock<Mutex<Vec<(u64, u64)>>> = OnceLock::new();
    F.get_or_init(|| Mutex::new(Vec::new()))
}

fn shm_pool_alloc(aligned: u64) -> Option<u64> {
    if aligned == 0 || aligned > SHM_POOL_SIZE {
        return None;
    }

    {
        let mut free = shm_free_slices().lock().unwrap();
        if let Some(i) = free.iter().position(|(_, cap)| *cap >= aligned) {
            let (off, cap) = free.remove(i);
            if cap > aligned {
                free.push((off + aligned, cap - aligned));
            }
            return Some(off);
        }
    }

    let mut cur = SHM_POOL_NEXT.load(Ordering::Relaxed);
    loop {
        let next = cur.checked_add(aligned)?;
        if next > SHM_POOL_SIZE {
            return None;
        }
        match SHM_POOL_NEXT.compare_exchange(cur, next, Ordering::Relaxed, Ordering::Relaxed) {
            Ok(_) => return Some(cur),
            Err(actual) => cur = actual,
        }
    }
}

fn shm_pool_free(off: u64, capacity: u64) {
    if capacity == 0 || off >= SHM_POOL_SIZE {
        return;
    }
    let end = off.saturating_add(capacity).min(SHM_POOL_SIZE);
    if end <= off {
        return;
    }

    let mut free = shm_free_slices().lock().unwrap();
    free.push((off, end - off));
    free.sort_unstable_by_key(|(o, _)| *o);

    let mut merged: Vec<(u64, u64)> = Vec::with_capacity(free.len());
    for (o, cap) in free.drain(..) {
        if let Some((last_o, last_cap)) = merged.last_mut() {
            let last_end = last_o.saturating_add(*last_cap);
            if o <= last_end {
                let new_end = o.saturating_add(cap).max(last_end);
                *last_cap = new_end - *last_o;
                continue;
            }
        }
        merged.push((o, cap));
    }
    *free = merged;
}
/// Per-pid writable MAP_SHARED views into the shared pool. Linux's page cache
/// makes writes through a shared mapping visible to later file reads/execs. Sentry
/// backs mappings with an inherited anonymous pool, so it must explicitly flush
/// those bytes back to the backing fd at kernel-equivalent synchronization points.
fn shm_maps() -> &'static Mutex<HashMap<i32, Vec<ShmMap>>> {
    static M: OnceLock<Mutex<HashMap<i32, Vec<ShmMap>>>> = OnceLock::new();
    M.get_or_init(|| Mutex::new(HashMap::new()))
}
/// Fork-time shared mapping snapshots, keyed by the child's ring slot. MAP_SHARED
/// aliases are inherited by the kernel across fork just like fd tables are; the
/// supervisor bookkeeping has to inherit them too, or a parent unmap/reap can make
/// reclamation decisions as if the child did not still hold the mapping.
fn pending_shm_maps() -> &'static Mutex<HashMap<u64, Vec<ShmMap>>> {
    static M: OnceLock<Mutex<HashMap<u64, Vec<ShmMap>>>> = OnceLock::new();
    M.get_or_init(|| Mutex::new(HashMap::new()))
}

/// Shared mappings retired by an emulated execve.
///
/// A real execve tears down the old mm, so any old MAP_SHARED aliases disappear before
/// the new image runs. Sentry emulates execve in-process; it removes the logical map
/// records, but the host VMAs remain mapped until the host process exits. Keep those
/// backing slices live until reap so the pool never reuses pages still reachable through
/// a stale alias in the same process.
fn retired_exec_shm_keys() -> &'static Mutex<HashMap<i32, Vec<(u64, u64)>>> {
    static M: OnceLock<Mutex<HashMap<i32, Vec<(u64, u64)>>>> = OnceLock::new();
    M.get_or_init(|| Mutex::new(HashMap::new()))
}

fn retire_exec_shm_keys(pid: i32, keys: &[(u64, u64)]) {
    if keys.is_empty() {
        return;
    }
    let mut retired = retired_exec_shm_keys().lock().unwrap();
    let entry = retired.entry(pid).or_default();
    for key in keys {
        if !entry.contains(key) {
            entry.push(*key);
        }
    }
}

fn take_retired_exec_shm_keys(pid: i32) -> Vec<(u64, u64)> {
    retired_exec_shm_keys()
        .lock()
        .unwrap()
        .remove(&pid)
        .unwrap_or_default()
}

/// Supervisor side of a guest `mmap(MAP_SHARED, fd)`: map the (supervisor-held) host
/// fd `h` to a pool slice keyed by its `(dev,ino)`, copying the fd's current content
/// in on first allocation (zero for a fresh memfd; the file bytes for a real file).
/// Returns the POOL ADDRESS (`base + slice + off`) — the SAME VA in every cell, since
/// the pool is inherited — or `-1` to decline (no pool / unstattable / dup fail /
/// can't-grow-live-VA / pool full).
///
/// IMPORTANT: a decline is NO LONGER downgraded to a private per-process copy by the
/// caller — `guest_mmap` turns it into -ENOMEM. A private copy of a MAP_SHARED+fd
/// object is silently INCOHERENT: a producer's release-store of a value (e.g. ipcz's
/// `FragmentHeader::size`, published by `Parcel::CommitData`) lands in its own copy and
/// the consumer's acquire-load (`Parcel::AdoptDataFragment`) reads its own stale copy
/// (typically 0). ipcz ACCEPTS a 0 data_size as an EMPTY data view (it only rejects
/// `data_size > max`), so the parcel is delivered silently empty —
/// `ChildProcessHost::Ping`'s reply carries no payload, its callback never completes,
/// and Chromium's 15s "no connection" watchdog kills the NetworkService child. To keep
/// declines rare, slices get reservation padding so a later same-`(dev,ino)` mapping
/// fits in place. (Reclaiming exhausted slices is a separate, fork-refcount-gated
/// follow-up. The pool is sized for a full conformance run under one long-lived
/// supervisor, not just one Chromium launch.)
fn shm_pool_map(pid: i32, h: i32, want_len: u64, prot: u64, off: u64) -> i64 {
    let base = SHM_POOL_BASE.load(Ordering::Relaxed);
    if base == 0 {
        return -1;
    }
    let mut st = [0u8; 144];
    if unsafe { host(SYS_FSTAT, h as u64, st.as_mut_ptr() as u64, 0, 0, 0, 0) } != 0 {
        return -1;
    }
    let dev = u64::from_le_bytes(st[0..8].try_into().unwrap());
    let ino = u64::from_le_bytes(st[8..16].try_into().unwrap());
    let size = u64::from_le_bytes(st[48..56].try_into().unwrap());
    // The slice must hold the largest mapping any cell makes of this object.
    let need = want_len.max(size).saturating_add(off);
    let raw = (need + 0xfff) & !0xfff;
    // Reservation padding: round each slice up to at least 64 KiB and to a 64 KiB
    // multiple, then give small slices 2x headroom (capped at 1 MiB). This lets a
    // later, slightly-larger mapping of the SAME memfd land in place rather than
    // tripping the can't-grow-a-live-shared-VA decline below (which now ENOMEMs
    // instead of silently private-copying). ipcz ftruncates each buffer to its full
    // size before the first mmap, so in practice every map of a given `(dev,ino)` is
    // the same size and this just absorbs page-rounding jitter. The padding is VIRTUAL
    // (only touched pages cost RAM), and the multi-GiB pool is sized for the full
    // serial conformance run under one supervisor, so a Chromium launch (a bounded
    // handful of small ipcz buffers) is nowhere near exhaustion.
    let aligned = {
        let floor = raw.max(64 * 1024);
        let bucket = (floor + (64 * 1024 - 1)) & !(64 * 1024 - 1);
        if bucket <= 512 * 1024 {
            (bucket * 2).min(1 << 20).max(bucket)
        } else {
            bucket
        }
    };
    let key = (dev, ino);
    let mut slices = shm_slices().lock().unwrap();
    let existed = slices.get(&key).copied();
    let slice = if let Some(s) = existed {
        // Compare against the actual (un-padded) requirement: a re-map at the same or
        // smaller size reuses the slice; only a genuinely larger mapping of a live
        // shared VA can't be honored (it can't be grown/moved without breaking every
        // cell that already holds it at this VA). With the padding above this is
        // essentially unreachable for ipcz's fixed-size buffers.
        if raw > s.capacity {
            SHM_ENOMEM_DECLINES.fetch_add(1, Ordering::Relaxed);
            return -1; // → caller returns -ENOMEM (loud), never a private copy
        }
        s
    } else {
        let Some(o) = shm_pool_alloc(aligned) else {
            // Pool exhausted. Decline → caller returns -ENOMEM (loud). Do NOT fall
            // back to a private copy: that silently breaks ipcz's shared-fragment
            // release/acquire handshake (see fn header).
            SHM_ENOMEM_DECLINES.fetch_add(1, Ordering::Relaxed);
            return -1;
        };
        let dup = unsafe {
            host(
                SYS_FCNTL,
                h as u64,
                libc::F_DUPFD_CLOEXEC as u64,
                3,
                0,
                0,
                0,
            )
        };
        if dup < 0 {
            shm_pool_free(o, aligned);
            SHM_ENOMEM_DECLINES.fetch_add(1, Ordering::Relaxed);
            return -1;
        }
        // Seed only the requested mapping window. Linux mmap faults file pages
        // lazily; copying the entire backing file here materializes sparse/large
        // Chromium shared files into the cgroup as shmem and OOMs long visual
        // runs. The untouched part of the reserved slice remains demand-zero
        // until another mapping faults/copies it.
        let mut done = 0u64;
        while done < want_len {
            let chunk = (want_len - done).min(4 * 1024 * 1024);
            let n = unsafe {
                host(
                    SYS_PREAD64,
                    h as u64,
                    base + o + off + done,
                    chunk,
                    off + done,
                    0,
                    0,
                )
            };
            if n <= 0 {
                break;
            }
            done += n as u64;
        }
        let s = ShmSlice {
            pool_off: o,
            capacity: aligned,
            backing_fd: dup as i32,
        };
        slices.insert(key, s);
        s
    };
    drop(slices);
    let addr = base + slice.pool_off + off;
    shm_maps()
        .lock()
        .unwrap()
        .entry(pid)
        .or_default()
        .push(ShmMap {
            addr,
            len: want_len,
            file_off: off,
            key,
            writable: (prot & libc::PROT_WRITE as u64) != 0,
        });
    if unsafe { IPCTRACE } {
        ipc_logf(
            &[
                (b"SHMMAP sup=", unsafe {
                    host(SYS_GETPID, 0, 0, 0, 0, 0, 0)
                }),
                (b"hfd=", h as i64),
                (b"dev=", dev as i64),
                (b"ino=", ino as i64),
                (b"sz=", size as i64),
                (b"len=", want_len as i64),
                (b"prot=", prot as i64),
                (b"off=", off as i64),
                (b"writable=", ((prot & libc::PROT_WRITE as u64) != 0) as i64),
                (
                    b"existed=",
                    existed.map(|s| s.pool_off as i64).unwrap_or(-1),
                ),
                (b"slice=", slice.pool_off as i64),
                (b"base=", base as i64),
                (b"addr=", addr as i64),
                (
                    b"enomem=",
                    SHM_ENOMEM_DECLINES.load(Ordering::Relaxed) as i64,
                ),
            ],
            &[],
        );
    }
    addr as i64
}

fn shm_alias_pid_range(pid: i32, from: u64, to: u64, len: u64) -> i64 {
    let mut maps = shm_maps().lock().unwrap();
    let Some(entries) = maps.get_mut(&pid) else {
        return -2;
    };
    // `shm_pool_map` records the pool address before the cell aliases it. Update
    // the most recent matching record: a process may map one memfd more than once,
    // and the newest record corresponds to this just-returned mmap.
    for m in entries.iter_mut().rev() {
        if m.addr == from && m.len == len {
            m.addr = to;
            return 0;
        }
    }
    -2
}

fn shm_protect_pid_range(pid: i32, addr: u64, len: u64, prot: u64) -> i64 {
    let mut maps = shm_maps().lock().unwrap();
    let Some(entries) = maps.get_mut(&pid) else {
        return 0;
    };
    let writable = (prot & libc::PROT_WRITE as u64) != 0;
    for m in entries.iter_mut() {
        if !ranges_overlap(m.addr, m.len, addr, len) {
            continue;
        }
        if writable {
            // Safe over-approximation for partial mprotect: flushing unchanged bytes
            // is harmless, while skipping dirty bytes loses MAP_SHARED writes.
            m.writable = true;
        } else if addr <= m.addr && addr.saturating_add(len) >= m.addr.saturating_add(m.len) {
            m.writable = false;
        }
    }
    0
}

fn shm_move_pid_range(pid: i32, from: u64, old_len: u64, to: u64, new_len: u64) -> i64 {
    let mut maps = shm_maps().lock().unwrap();
    let Some(entries) = maps.get_mut(&pid) else {
        return 0;
    };
    for m in entries.iter_mut().rev() {
        if m.addr == from && m.len == old_len {
            m.addr = to;
            m.len = new_len;
            return 0;
        }
    }
    0
}

fn shm_snapshot(parent: i32, slot: u64) {
    let src = shm_maps()
        .lock()
        .unwrap()
        .get(&parent)
        .cloned()
        .unwrap_or_default();
    if src.is_empty() {
        pending_shm_maps().lock().unwrap().remove(&slot);
    } else {
        pending_shm_maps().lock().unwrap().insert(slot, src);
    }
}

fn shm_adopt(slot: u64, child: i32) {
    let Some(entries) = pending_shm_maps().lock().unwrap().remove(&slot) else {
        return;
    };
    let old = shm_maps().lock().unwrap().insert(child, entries);
    if let Some(old) = old {
        let keys: Vec<(u64, u64)> = old.iter().map(|m| m.key).collect();
        shm_reclaim_unreferenced_keys(&keys);
    }
}

fn shm_reclaim_unreferenced_keys(keys: &[(u64, u64)]) {
    if keys.is_empty() {
        return;
    }
    let mut uniq = Vec::new();
    for key in keys {
        if !uniq.contains(key) {
            uniq.push(*key);
        }
    }

    for key in uniq {
        let still_mapped = {
            let maps = shm_maps().lock().unwrap();
            maps.values()
                .any(|entries| entries.iter().any(|m| m.key == key))
        } || {
            let pending = pending_shm_maps().lock().unwrap();
            pending
                .values()
                .any(|entries| entries.iter().any(|m| m.key == key))
        } || {
            let retired = retired_exec_shm_keys().lock().unwrap();
            retired
                .values()
                .any(|entries| entries.iter().any(|retired_key| *retired_key == key))
        };
        if still_mapped {
            continue;
        }

        let slice = {
            let mut slices = shm_slices().lock().unwrap();
            slices.remove(&key)
        };
        let Some(slice) = slice else {
            continue;
        };
        let base = SHM_POOL_BASE.load(Ordering::Relaxed);
        if base != 0 {
            unsafe {
                host(
                    SYS_MADVISE,
                    base + slice.pool_off,
                    slice.capacity,
                    libc::MADV_DONTNEED as u64,
                    0,
                    0,
                    0,
                );
            }
        }
        if slice.backing_fd >= 0 {
            unsafe { host(SYS_CLOSE, slice.backing_fd as u64, 0, 0, 0, 0, 0) };
        }
        shm_pool_free(slice.pool_off, slice.capacity);
    }
}

fn ranges_overlap(a: u64, alen: u64, b: u64, blen: u64) -> bool {
    let ae = a.saturating_add(alen);
    let be = b.saturating_add(blen);
    a < be && b < ae
}

fn shm_pwrite_all(fd: i32, mut src: u64, mut len: u64, mut off: u64) -> i64 {
    while len > 0 {
        let chunk = len.min(4 * 1024 * 1024);
        let n = unsafe { host(SYS_PWRITE64, fd as u64, src, chunk, off, 0, 0) };
        if n < 0 {
            return n;
        }
        if n == 0 {
            return -5; // -EIO: pwrite made no progress
        }
        let n = n as u64;
        src = src.saturating_add(n);
        off = off.saturating_add(n);
        len -= n;
    }
    0
}

fn shm_flush_pid_range(pid: i32, addr: u64, len: u64, remove: bool) -> i64 {
    shm_flush_pid_range_inner(pid, addr, len, remove, false)
}

fn shm_flush_pid_range_for_exec(pid: i32, addr: u64, len: u64) -> i64 {
    shm_flush_pid_range_inner(pid, addr, len, true, true)
}

fn shm_reap_pid(pid: i32) {
    shm_flush_pid_range(pid, 0, u64::MAX, true);
    let retired = take_retired_exec_shm_keys(pid);
    shm_reclaim_unreferenced_keys(&retired);
}

fn shm_flush_pid_range_inner(
    pid: i32,
    addr: u64,
    len: u64,
    remove: bool,
    retire_until_reap: bool,
) -> i64 {
    let all = addr == 0 && len == u64::MAX;
    let mut removed_keys = Vec::new();
    let maps_to_flush: Vec<ShmMap> = {
        let mut maps = shm_maps().lock().unwrap();
        let Some(entries) = maps.get_mut(&pid) else {
            return 0;
        };
        let mut selected = Vec::new();
        entries.retain(|m| {
            let hit = all || ranges_overlap(m.addr, m.len, addr, len);
            if hit {
                selected.push(*m);
                if remove {
                    removed_keys.push(m.key);
                }
            }
            !(remove && hit)
        });
        if entries.is_empty() {
            maps.remove(&pid);
        }
        selected
    };
    if maps_to_flush.is_empty() {
        return 0;
    }

    let slices = shm_slices().lock().unwrap();
    let mut first_err = 0;
    for m in maps_to_flush {
        if !m.writable {
            if unsafe { IPCTRACE } && m.len <= 4096 {
                ipc_logf(
                    &[
                        (b"SHMFLUSH-SKIP pid=", pid as i64),
                        (b"addr=", m.addr as i64),
                        (b"len=", m.len as i64),
                        (b"file_off=", m.file_off as i64),
                        (b"writable=0", 0),
                    ],
                    &[],
                );
            }
            continue;
        }
        let Some(slice) = slices.get(&m.key).copied() else {
            continue;
        };
        if slice.backing_fd < 0 {
            continue;
        }
        let flush_start = if all { m.addr } else { m.addr.max(addr) };
        let flush_end = if all {
            m.addr.saturating_add(m.len)
        } else {
            m.addr.saturating_add(m.len).min(addr.saturating_add(len))
        };
        if flush_end <= flush_start {
            continue;
        }
        let rel = flush_start.saturating_sub(m.addr);
        let flush_len = flush_end - flush_start;
        let file_off = m.file_off.saturating_add(rel);
        let pool_src = SHM_POOL_BASE
            .load(Ordering::Relaxed)
            .saturating_add(slice.pool_off)
            .saturating_add(file_off);
        if file_off.saturating_add(flush_len) > slice.capacity {
            if first_err == 0 {
                first_err = -22; // -EINVAL: mapping extends past its shared slice
            }
            continue;
        }
        let n = shm_pwrite_all(slice.backing_fd, pool_src, flush_len, file_off);
        if unsafe { IPCTRACE } && flush_len <= 4096 {
            let bytes = unsafe {
                std::slice::from_raw_parts(pool_src as *const u8, flush_len.min(16) as usize)
            };
            ipc_logf(
                &[
                    (b"SHMFLUSH pid=", pid as i64),
                    (b"addr=", flush_start as i64),
                    (b"src=", pool_src as i64),
                    (b"len=", flush_len as i64),
                    (b"file_off=", file_off as i64),
                    (b"ret=", n),
                ],
                bytes,
            );
        }
        if n < 0 && first_err == 0 {
            first_err = n;
        }
    }
    drop(slices);
    if remove {
        if retire_until_reap {
            retire_exec_shm_keys(pid, &removed_keys);
        } else {
            shm_reclaim_unreferenced_keys(&removed_keys);
        }
    }
    first_err
}

#[derive(Clone, Copy)]
struct SysvShmSegment {
    id: i32,
    key: u64,
    size: u64,
    addr: u64,
    nattch: u64,
    removed: bool,
}

fn sysv_shm_segments() -> &'static Mutex<HashMap<i32, SysvShmSegment>> {
    static S: OnceLock<Mutex<HashMap<i32, SysvShmSegment>>> = OnceLock::new();
    S.get_or_init(|| Mutex::new(HashMap::new()))
}

fn sysv_shm_next_id() -> &'static AtomicU32 {
    static N: AtomicU32 = AtomicU32::new(1);
    &N
}

fn sysv_shmget(key: u64, size: u64, flags: u64) -> i64 {
    const IPC_PRIVATE: u64 = 0;
    const IPC_CREAT: u64 = 0o1000;
    const IPC_EXCL: u64 = 0o2000;

    let base = SHM_POOL_BASE.load(Ordering::Relaxed);
    if base == 0 {
        return -38; // -ENOSYS: shared pool unavailable
    }
    let mut segments = sysv_shm_segments().lock().unwrap();
    if key != IPC_PRIVATE {
        if let Some(existing) = segments.values().find(|s| s.key == key && !s.removed) {
            if (flags & IPC_CREAT) != 0 && (flags & IPC_EXCL) != 0 {
                return -17; // -EEXIST
            }
            if size > existing.size {
                return -22; // -EINVAL
            }
            return existing.id as i64;
        }
        if (flags & IPC_CREAT) == 0 {
            return -2; // -ENOENT
        }
    }
    if size == 0 {
        return -22; // -EINVAL
    }
    let aligned = page_up(size);
    let Some(off) = shm_pool_alloc(aligned) else {
        return -28; // -ENOSPC
    };
    let id = sysv_shm_next_id().fetch_add(1, Ordering::Relaxed) as i32;
    let addr = base + off;
    unsafe { std::ptr::write_bytes(addr as *mut u8, 0, aligned as usize) };
    segments.insert(
        id,
        SysvShmSegment {
            id,
            key,
            size,
            addr,
            nattch: 0,
            removed: false,
        },
    );
    id as i64
}

fn sysv_shmat(shmid: i32, shmaddr: u64, flags: u64) -> i64 {
    const SHM_RDONLY: u64 = 0o10000;
    if shmaddr != 0 || (flags & !SHM_RDONLY) != 0 {
        return -22; // -EINVAL: only default attaches are modeled today.
    }
    let mut segments = sysv_shm_segments().lock().unwrap();
    let Some(segment) = segments.get_mut(&shmid) else {
        return -22; // -EINVAL
    };
    if segment.removed {
        return -22;
    }
    segment.nattch = segment.nattch.saturating_add(1);
    segment.addr as i64
}

fn sysv_shmdt(addr: u64) -> i64 {
    let mut segments = sysv_shm_segments().lock().unwrap();
    let Some((&id, segment)) = segments.iter_mut().find(|(_, s)| s.addr == addr) else {
        return -22; // -EINVAL
    };
    segment.nattch = segment.nattch.saturating_sub(1);
    if segment.removed && segment.nattch == 0 {
        segments.remove(&id);
    }
    0
}

fn sysv_shmctl(pid: i32, shmid: i32, cmd: u64, buf: u64) -> i64 {
    const IPC_RMID: u64 = 0;
    const IPC_STAT: u64 = 2;
    let mut segments = sysv_shm_segments().lock().unwrap();
    match cmd {
        IPC_RMID => {
            let Some(segment) = segments.get_mut(&shmid) else {
                return -22; // -EINVAL
            };
            segment.removed = true;
            if segment.nattch == 0 {
                segments.remove(&shmid);
            }
            0
        }
        IPC_STAT => {
            let Some(segment) = segments.get(&shmid) else {
                return -22; // -EINVAL
            };
            if buf == 0 {
                return -14; // -EFAULT
            }
            // Linux x86_64 `struct shmid64_ds` is 112 bytes. Postgres only needs
            // the call to succeed, but expose the segment size at its normal offset.
            let mut out = [0u8; 112];
            out[32..40].copy_from_slice(&segment.size.to_le_bytes());
            vm_write(pid, buf, &out);
            0
        }
        _ => -22, // -EINVAL
    }
}

/// Layer 1 owns the cell's address space. Anonymous mappings run locally. A
/// FILE-backed mapping cannot run locally — the fd belongs to the supervisor, not
/// the cell — so it is DECOMPOSED: anon-map the target, then fill it via delegated
/// `pread` (the supervisor reads the file and process_vm_writev's the bytes in),
/// then set the requested protection. This is exactly how the cell loads a guest's
/// shared libraries (`ld.so`'s `mmap` of `libc.so`, etc.) without ever holding a
/// host fd. EXCEPTION: `mmap(MAP_SHARED, fd)` needs REAL cross-process pages, so it
/// is delegated to the supervisor's shared pool (see `MAP_SHARED_BIT`/`shm_pool_map`).
fn guest_mmap(addr: u64, len: u64, prot: u64, flags: u64, fd: u64, off: u64) -> i64 {
    if len == 0 {
        return -22; // -EINVAL
    }
    // MAP_SHARED on a real fd → REAL cross-process shared memory. The cell holds no
    // host fd, so it can't map the object itself; delegate to the supervisor, which
    // backs it with a slice of the shared pool (inherited by every cell at this same
    // VA) and returns that address.
    //
    // CRITICAL: a decline is turned into a HARD -ENOMEM, NOT a private per-process
    // copy. A private copy of a MAP_SHARED object is silently incoherent: a peer's
    // writes never become visible here (and ours never become visible to the peer).
    // The canonical victim is ipcz (Chromium's IPC transport): a parcel's data rides
    // a shared-memory FRAGMENT whose `FragmentHeader::size` the producer publishes
    // with a release-store (`Parcel::CommitData`) and the consumer reads with an
    // acquire-load (`Parcel::AdoptDataFragment`). On a private copy the consumer reads
    // a STALE size (0) — which `AdoptDataFragment` ACCEPTS as an empty data view, so
    // the parcel is delivered SILENTLY EMPTY. `ChildProcessHost::Ping`'s reply then
    // carries no payload, its callback never completes, and Chromium's 15s "no
    // connection" watchdog terminates the NetworkService child. Failing LOUD (ENOMEM)
    // instead surfaces the condition; with the pool's reservation padding (see
    // `shm_pool_map`) and 1 GiB capacity, a decline should never occur for a launch.
    if (flags & MAP_SHARED_BIT) != 0 && (flags & MAP_ANON_BIT) == 0 && fd != u64::MAX {
        let r = delegate(SYS_MMAP, addr, len, prot, flags, fd, off);
        if r > 0 {
            // The supervisor returns a pool-slice VA keyed by (dev,ino): the SAME VA for
            // every mapping of one object — that shared identity is what keeps the pool
            // coherent across cells. But chrome's base::SharedMemoryTracker keys LIVE
            // mappings by their start VA and CHECK-fails when one process maps the same
            // object twice at the same VA: mojo's ipcz DataPipe maps a shared buffer for
            // both ends in one process, then unmaps both, and the 2nd unmap finds its
            // tracker slot already erased (shared_memory_tracker.cc:62). Linux hands every
            // mmap a distinct VA; emulate that by aliasing the slice with
            // mremap(old_size=0, MREMAP_MAYMOVE) — a fresh VA over the SAME physical pages,
            // so coherence is unchanged. The guest sees (and later munmaps) the alias,
            // which lands outside the pool so munmap frees just the alias; the slice stays
            // mapped and tracked supervisor-side. Calling host() directly bypasses the
            // SYS_MREMAP guard (which refuses pool addresses). Fall back to the slice VA if
            // the host declines the alias (no worse than before).
            let alias = unsafe {
                host(
                    SYS_MREMAP,
                    r as u64,
                    0,
                    len,
                    libc::MREMAP_MAYMOVE as u64,
                    0,
                    0,
                )
            };
            if alias <= 0 {
                if unsafe { IPCTRACE } {
                    ipc_logf(
                        &[
                            (b"SHMALIAS-FAIL pool=", r),
                            (b"len=", len as i64),
                            (b"ret=", alias),
                        ],
                        &[],
                    );
                }
                let _ = delegate(CTL_SHM_FLUSH, r as u64, len, 1, 0, 0, 0);
                return -12; // -ENOMEM: cannot provide Linux's unique-VA MAP_SHARED.
            }
            let vis = alias as u64;
            // The shared pool itself is RW so the supervisor can seed/flush it,
            // but Linux applies the guest-requested protection to each mmap VMA.
            // `mremap(old_size=0)` clones the pool VMA's protections, so restore
            // the alias to the requested prot before guest code sees it.
            let _ = unsafe { host(SYS_MPROTECT, vis, len, prot, 0, 0, 0) };
            let aliased = delegate(CTL_SHM_ALIAS, r as u64, vis, len, 0, 0, 0);
            if aliased != 0 {
                let _ = unsafe { host(SYS_MUNMAP, vis, len, 0, 0, 0, 0) };
                let _ = delegate(CTL_SHM_FLUSH, r as u64, len, 1, 0, 0, 0);
                return -12;
            }
            guest_vma_note_mapping(vis, len);
            return vis as i64;
        }
        if unsafe { IPCTRACE } {
            ipc_logf(
                &[
                    (b"SHMMAP-DECLINE fd=", fd as i64),
                    (b"len=", len as i64),
                    (b"prot=", prot as i64),
                    (b"ret=", r),
                ],
                &[],
            );
        }
        // Never serve a shared fd as a private copy. (-ENOMEM is what the kernel
        // returns when it cannot back a mapping — the closest honest errno.)
        return -12; // -ENOMEM
    }
    // Where the mapping must land: MAP_FIXED and MAP_FIXED_NOREPLACE are binding.
    // A nonzero address without either is just a hint; forcing MAP_FIXED onto it
    // can clobber an existing mapping and corrupt large toolchains that use mmap
    // hints heavily.
    let guest_fixed = (flags & (MAP_FIXED_BIT | MAP_FIXED_NOREPLACE_BIT)) != 0;
    let guest_noreplace = (flags & MAP_FIXED_NOREPLACE_BIT) != 0;
    let target = if guest_fixed {
        if addr == 0 {
            return -1; // -EPERM: kernel rejects fixed mappings at NULL on this host.
        }
        addr
    } else {
        let t = arena_alloc(len);
        if t == 0 {
            if (flags & MAP_ANON_BIT) != 0 && (prot & PROT_EXEC_BIT) == 0 {
                // Large data-only reservations (notably V8's sandbox VA
                // reservation) cannot fit below the SUD floor. They are safe above
                // it as long as they never become executable: SUD only cares where
                // syscall instructions execute from. Let the kernel choose a VA and
                // guard later mprotect/pkey_mprotect upgrades.
                let host_flags = flags & !(MAP_FIXED_BIT | MAP_FIXED_NOREPLACE_BIT);
                let r = unsafe { host(SYS_MMAP, addr, len, prot, host_flags, u64::MAX, 0) };
                if r >= 0 {
                    guest_vma_note_mapping(r as u64, len);
                }
                return r;
            }
            if (prot & PROT_EXEC_BIT) == 0 {
                // Same pressure shape as the anonymous case, but for private
                // file-backed data such as Chromium's icudtl.dat after several
                // browser launches. The cell cannot mmap the supervisor-owned fd
                // directly, so keep the existing pread-copy model but let the
                // kernel choose a high, non-executable VA once the low arena is
                // full. mprotect/pkey_mprotect still reject later EXEC upgrades
                // above the SUD floor.
                let m = unsafe {
                    host(
                        SYS_MMAP,
                        0,
                        len,
                        PROT_RW,
                        (libc::MAP_PRIVATE | libc::MAP_ANONYMOUS) as u64,
                        u64::MAX,
                        0,
                    )
                };
                if m < 0 {
                    return m;
                }
                let base = m as u64;
                let mut done = 0u64;
                while done < len {
                    let chunk = (len - done).min(4 * 1024 * 1024);
                    let n = delegate(SYS_PREAD64, fd, base + done, chunk, off + done, 0, 0);
                    if n < 0 {
                        unsafe { host(SYS_MUNMAP, base, len, 0, 0, 0, 0) };
                        return n;
                    }
                    if n == 0 {
                        break;
                    }
                    done += n as u64;
                }
                if prot != PROT_RW {
                    unsafe { host(SYS_MPROTECT, base, len, prot, 0, 0, 0) };
                }
                guest_vma_note_mapping(base, len);
                return base as i64;
            }
            return -12;
        }
        t
    };

    if (prot & PROT_EXEC_BIT) != 0 && overlaps_sud_host_region(target, len) {
        return -1; // -EPERM: executable guest mappings must stay below SUD's host region.
    }

    // Loader-image guard. The transport below force-OR's MAP_FIXED (to keep guest
    // mappings in the controlled low region), which turns a NON-binding address HINT
    // into a destructive placement. node/V8 reserve the pointer-compression cage with
    // exactly such a hint (≈ ld.so_base + 0x80000, INSIDE ld-musl's tail); honoring it
    // verbatim destroyed musl's .rodata/.bss → SIGSEGV. So: a guest-requested MAP_FIXED
    // onto the loader is refused (-ENOMEM; we never knowingly clobber the exe/ld.so);
    // a bare HINT is treated as advisory and RELOCATED to the arena — exactly what the
    // kernel would do with a colliding non-FIXED hint (the guest accepts any address).
    if guest_fixed && overlaps_loader(target, page_up(target + len)) {
        if guest_noreplace {
            return -17; // -EEXIST
        }
        return -12; // -ENOMEM
    }

    if (flags & MAP_ANON_BIT) != 0 {
        // Anonymous — purely cell-local. Pin to the chosen target.
        let host_flags = if guest_noreplace {
            (flags & !MAP_FIXED_BIT) | MAP_FIXED_NOREPLACE_BIT
        } else {
            flags | MAP_FIXED_BIT
        };
        let r = unsafe { host(SYS_MMAP, target, len, prot, host_flags, u64::MAX, 0) };
        if r >= 0 {
            arena_note_mapping(target, len);
            guest_vma_note_mapping(r as u64, len);
        }
        return r;
    }

    // File-backed: reserve RW anon at the target, fill from the file, set prot.
    let reserve_flags = if guest_noreplace {
        (libc::MAP_PRIVATE as u64) | (libc::MAP_ANONYMOUS as u64) | MAP_FIXED_NOREPLACE_BIT
    } else {
        (libc::MAP_PRIVATE | libc::MAP_ANONYMOUS | libc::MAP_FIXED) as u64
    };
    let m = unsafe { host(SYS_MMAP, target, len, PROT_RW, reserve_flags, u64::MAX, 0) };
    if m < 0 {
        return m;
    }
    let mut done = 0u64;
    while done < len {
        let chunk = (len - done).min(4 * 1024 * 1024);
        let n = delegate(SYS_PREAD64, fd, target + done, chunk, off + done, 0, 0);
        if n < 0 {
            unsafe { host(SYS_MUNMAP, target, len, 0, 0, 0, 0) };
            return n;
        }
        if n == 0 {
            break; // EOF: tail stays zero, matching file-backed partial-page behavior.
        }
        done += n as u64;
    }
    if prot != PROT_RW {
        unsafe { host(SYS_MPROTECT, target, len, prot, 0, 0, 0) };
    }
    arena_note_mapping(target, len);
    guest_vma_note_mapping(target, len);
    target as i64
}
fn brk(req: u64) -> i64 {
    use std::sync::atomic::Ordering;
    let (base, end) = unsafe { (HEAP_BASE, HEAP_END) };
    if req == 0 || req < base || req > end {
        return HEAP_CUR.load(Ordering::Acquire) as i64;
    }
    // Atomic bump with CAS — concurrent growers never get torn/overlapping ranges.
    loop {
        let cur = HEAP_CUR.load(Ordering::Acquire);
        if req <= cur {
            // Shrink / no-op: just publish the (lower-or-equal) break.
            match HEAP_CUR.compare_exchange_weak(cur, req, Ordering::AcqRel, Ordering::Acquire) {
                Ok(_) => return req as i64,
                Err(_) => continue,
            }
        }
        // Grow: CLAIM [cur, req) by advancing the cursor FIRST (so no other grower can
        // re-hand or re-zero this range), THEN zero it. glibc won't touch [cur, req) until
        // this brk() returns, so the post-CAS zero-fill races nobody.
        //
        // Kernel parity: brk-extended memory MUST read as zero. This heap region is mmap'd
        // ONCE and REUSED across execve (HEAP_CUR resets to HEAP_BASE on exec, bytes NOT
        // cleared), so the newly-exposed range can hold stale data from a prior program.
        // Without zeroing, a static-pie binary (e.g. ldconfig) sbrk's its TLS out of the
        // reused heap and its .tbss — which glibc assumes zero-initialized — inherits
        // garbage → "malloc(): unaligned tcache chunk detected". calloc likewise relies on
        // fresh brk memory already being zero.
        match HEAP_CUR.compare_exchange_weak(cur, req, Ordering::AcqRel, Ordering::Acquire) {
            Ok(_) => {
                unsafe { std::ptr::write_bytes(cur as *mut u8, 0, (req - cur) as usize) };
                return req as i64;
            }
            Err(_) => continue,
        }
    }
}

// ─── cell-local virtualized credentials (uid/gid sets) ───────────────────────
//
// The guest is "root in its own sandbox" exactly like the root user of a
// container under mac/linux KVM/HVF (where the VM's init runs as uid 0 and a
// service entrypoint su-exec's down to an unprivileged image user). The HOST uid
// a cell actually runs as is an ISOLATION detail (T2 per-tenant uid), invisible
// to the guest — so credentials are VIRTUALIZED per cell process, defaulting to
// root (0). `set*id`/`get*id` are serviced cell-local (each cell is one process;
// CoW fork copies these atomics into the child, so credential inheritance is
// automatic — a worker forked by a root master starts root, then drops itself).
// Without this, a guest privilege-drop (`nginx` worker `setgid`/`setuid`,
// `postgres`/`su-exec`) hit ENOSYS and the process died; and a daemon that
// REFUSES to run as root (postgres) needs `getuid` to reflect the drop.
struct CellCreds {
    ruid: std::sync::atomic::AtomicU32,
    euid: std::sync::atomic::AtomicU32,
    suid: std::sync::atomic::AtomicU32,
    fsuid: std::sync::atomic::AtomicU32,
    rgid: std::sync::atomic::AtomicU32,
    egid: std::sync::atomic::AtomicU32,
    sgid: std::sync::atomic::AtomicU32,
    fsgid: std::sync::atomic::AtomicU32,
}
#[derive(Clone, Copy)]
struct CellCredSnapshot {
    ruid: u32,
    euid: u32,
    suid: u32,
    fsuid: u32,
    rgid: u32,
    egid: u32,
    sgid: u32,
    fsgid: u32,
}
impl Default for CellCredSnapshot {
    fn default() -> Self {
        Self {
            ruid: 0,
            euid: 0,
            suid: 0,
            fsuid: 0,
            rgid: 0,
            egid: 0,
            sgid: 0,
            fsgid: 0,
        }
    }
}
static CELL_CREDS: CellCreds = CellCreds {
    ruid: std::sync::atomic::AtomicU32::new(0),
    euid: std::sync::atomic::AtomicU32::new(0),
    suid: std::sync::atomic::AtomicU32::new(0),
    fsuid: std::sync::atomic::AtomicU32::new(0),
    rgid: std::sync::atomic::AtomicU32::new(0),
    egid: std::sync::atomic::AtomicU32::new(0),
    sgid: std::sync::atomic::AtomicU32::new(0),
    fsgid: std::sync::atomic::AtomicU32::new(0),
};
#[inline]
fn cred_get(a: &std::sync::atomic::AtomicU32) -> u32 {
    a.load(std::sync::atomic::Ordering::Relaxed)
}
#[inline]
fn cred_set(a: &std::sync::atomic::AtomicU32, v: u32) {
    a.store(v, std::sync::atomic::Ordering::Relaxed)
}
fn cred_reset(uid: u32, gid: u32) {
    cred_set(&CELL_CREDS.ruid, uid);
    cred_set(&CELL_CREDS.euid, uid);
    cred_set(&CELL_CREDS.suid, uid);
    cred_set(&CELL_CREDS.fsuid, uid);
    cred_set(&CELL_CREDS.rgid, gid);
    cred_set(&CELL_CREDS.egid, gid);
    cred_set(&CELL_CREDS.sgid, gid);
    cred_set(&CELL_CREDS.fsgid, gid);
}
fn cred_snapshot_current() -> CellCredSnapshot {
    CellCredSnapshot {
        ruid: cred_get(&CELL_CREDS.ruid),
        euid: cred_get(&CELL_CREDS.euid),
        suid: cred_get(&CELL_CREDS.suid),
        fsuid: cred_get(&CELL_CREDS.fsuid),
        rgid: cred_get(&CELL_CREDS.rgid),
        egid: cred_get(&CELL_CREDS.egid),
        sgid: cred_get(&CELL_CREDS.sgid),
        fsgid: cred_get(&CELL_CREDS.fsgid),
    }
}
fn pack_u32_pair(lo: u32, hi: u32) -> u64 {
    lo as u64 | ((hi as u64) << 32)
}
fn unpack_u32_pair(v: u64) -> (u32, u32) {
    (v as u32, (v >> 32) as u32)
}
fn supervisor_creds() -> &'static Mutex<HashMap<i32, CellCredSnapshot>> {
    static T: OnceLock<Mutex<HashMap<i32, CellCredSnapshot>>> = OnceLock::new();
    T.get_or_init(|| Mutex::new(HashMap::new()))
}
fn supervisor_cred_for(pid: i32) -> CellCredSnapshot {
    supervisor_creds()
        .lock()
        .unwrap()
        .get(&pid)
        .copied()
        .unwrap_or_default()
}
fn sync_creds_to_supervisor() {
    let c = cred_snapshot_current();
    let _ = delegate(
        CTL_SET_CREDS,
        pack_u32_pair(c.ruid, c.euid),
        pack_u32_pair(c.suid, c.fsuid),
        pack_u32_pair(c.rgid, c.egid),
        pack_u32_pair(c.sgid, c.fsgid),
        0,
        0,
    );
}

// ─── cell-local virtual umask ────────────────────────────────────────────────
//
// `umask(2)` is process state: inherited across fork and preserved across exec.
// The host syscall cannot be forwarded because umask is process-global, while a
// sentry supervisor is multi-threaded and serves many cell processes at once.
const DEFAULT_UMASK: u32 = 0o022;
static CELL_UMASK: std::sync::atomic::AtomicU32 = std::sync::atomic::AtomicU32::new(DEFAULT_UMASK);

fn cell_umask_get() -> u32 {
    CELL_UMASK.load(std::sync::atomic::Ordering::Relaxed) & 0o777
}

fn cell_umask_set(mask: u32) -> u32 {
    CELL_UMASK.swap(mask & 0o777, std::sync::atomic::Ordering::Relaxed) & 0o777
}

fn cell_umask_reset() {
    CELL_UMASK.store(DEFAULT_UMASK, std::sync::atomic::Ordering::Relaxed);
}

fn supervisor_umasks() -> &'static Mutex<HashMap<i32, u32>> {
    static T: OnceLock<Mutex<HashMap<i32, u32>>> = OnceLock::new();
    T.get_or_init(|| Mutex::new(HashMap::new()))
}

fn supervisor_umask_for(pid: i32) -> u32 {
    supervisor_umasks()
        .lock()
        .unwrap()
        .get(&pid)
        .copied()
        .unwrap_or(DEFAULT_UMASK)
        & 0o777
}

fn sync_umask_to_supervisor() {
    let _ = delegate(CTL_SET_UMASK, cell_umask_get() as u64, 0, 0, 0, 0, 0);
}

fn apply_umask(pid: i32, mode: u64) -> u64 {
    mode & !(supervisor_umask_for(pid) as u64)
}

/// Privileged iff the effective uid is root — our model of CAP_SETUID/CAP_SETGID.
#[inline]
fn cred_privileged() -> bool {
    cred_get(&CELL_CREDS.euid) == 0
}
const ID_UNCHANGED: u32 = u32::MAX; // -1 in set*resuid/set*reuid means "leave alone"
/// `setuid(u)`: privileged → set all of r/e/s/fs uid; unprivileged → only `euid`
/// (+`fsuid`), and only to a currently-held uid. Returns 0 / -EPERM.
fn cred_setuid(u: u32) -> i64 {
    if cred_privileged() {
        cred_set(&CELL_CREDS.ruid, u);
        cred_set(&CELL_CREDS.euid, u);
        cred_set(&CELL_CREDS.suid, u);
        cred_set(&CELL_CREDS.fsuid, u);
        0
    } else if u == cred_get(&CELL_CREDS.ruid)
        || u == cred_get(&CELL_CREDS.suid)
        || u == cred_get(&CELL_CREDS.euid)
    {
        cred_set(&CELL_CREDS.euid, u);
        cred_set(&CELL_CREDS.fsuid, u);
        0
    } else {
        -1
    }
}
fn cred_setgid(g: u32) -> i64 {
    if cred_privileged() {
        cred_set(&CELL_CREDS.rgid, g);
        cred_set(&CELL_CREDS.egid, g);
        cred_set(&CELL_CREDS.sgid, g);
        cred_set(&CELL_CREDS.fsgid, g);
        0
    } else if g == cred_get(&CELL_CREDS.rgid)
        || g == cred_get(&CELL_CREDS.sgid)
        || g == cred_get(&CELL_CREDS.egid)
    {
        cred_set(&CELL_CREDS.egid, g);
        cred_set(&CELL_CREDS.fsgid, g);
        0
    } else {
        -1
    }
}
/// `setresuid(r,e,s)` — each `ID_UNCHANGED` leaves the field. Privileged sets any;
/// unprivileged restricts each new value to a currently-held uid. fsuid tracks euid.
fn cred_setresuid(r: u32, eu: u32, s: u32) -> i64 {
    let held = |v: u32| {
        v == cred_get(&CELL_CREDS.ruid)
            || v == cred_get(&CELL_CREDS.euid)
            || v == cred_get(&CELL_CREDS.suid)
    };
    if !cred_privileged() {
        for &v in &[r, eu, s] {
            if v != ID_UNCHANGED && !held(v) {
                return -1;
            }
        }
    }
    if r != ID_UNCHANGED {
        cred_set(&CELL_CREDS.ruid, r);
    }
    if eu != ID_UNCHANGED {
        cred_set(&CELL_CREDS.euid, eu);
        cred_set(&CELL_CREDS.fsuid, eu);
    }
    if s != ID_UNCHANGED {
        cred_set(&CELL_CREDS.suid, s);
    }
    0
}
fn cred_setresgid(r: u32, eg: u32, s: u32) -> i64 {
    let held = |v: u32| {
        v == cred_get(&CELL_CREDS.rgid)
            || v == cred_get(&CELL_CREDS.egid)
            || v == cred_get(&CELL_CREDS.sgid)
    };
    if !cred_privileged() {
        for &v in &[r, eg, s] {
            if v != ID_UNCHANGED && !held(v) {
                return -1;
            }
        }
    }
    if r != ID_UNCHANGED {
        cred_set(&CELL_CREDS.rgid, r);
    }
    if eg != ID_UNCHANGED {
        cred_set(&CELL_CREDS.egid, eg);
        cred_set(&CELL_CREDS.fsgid, eg);
    }
    if s != ID_UNCHANGED {
        cred_set(&CELL_CREDS.sgid, s);
    }
    0
}

fn cred_getresuid(r: u64, e: u64, s: u64) -> i64 {
    if r == 0 || e == 0 || s == 0 {
        return -14; // -EFAULT
    }
    unsafe {
        std::ptr::write_unaligned(r as *mut u32, cred_get(&CELL_CREDS.ruid));
        std::ptr::write_unaligned(e as *mut u32, cred_get(&CELL_CREDS.euid));
        std::ptr::write_unaligned(s as *mut u32, cred_get(&CELL_CREDS.suid));
    }
    0
}

fn cred_getresgid(r: u64, e: u64, s: u64) -> i64 {
    if r == 0 || e == 0 || s == 0 {
        return -14; // -EFAULT
    }
    unsafe {
        std::ptr::write_unaligned(r as *mut u32, cred_get(&CELL_CREDS.rgid));
        std::ptr::write_unaligned(e as *mut u32, cred_get(&CELL_CREDS.egid));
        std::ptr::write_unaligned(s as *mut u32, cred_get(&CELL_CREDS.sgid));
    }
    0
}

fn sentry_reserved_signal_mask() -> u64 {
    (1u64 << (31 - 1)) | (1u64 << ((SIG_DETHREAD as u64) - 1))
}

fn sanitize_guest_sigmask(mask: u64) -> u64 {
    mask & !sentry_reserved_signal_mask()
}

unsafe fn block_guest_signals_until_sigreturn() {
    const SIG_BLOCK: u64 = 0;
    let mut set = sanitize_guest_sigmask(!0u64); // keep sentry-private signals deliverable
    set &= !(1u64 << (9 - 1)); // SIGKILL cannot be blocked, but keep the mask honest
    set &= !(1u64 << (19 - 1)); // SIGSTOP cannot be blocked either
    host(
        SYS_RT_SIGPROCMASK,
        SIG_BLOCK,
        std::ptr::addr_of!(set) as u64,
        0,
        8,
        0,
        0,
    );
}

unsafe fn pending_unmasked_guest_signal(saved_mask: u64) -> bool {
    let mut pending = 0u64;
    if host(
        SYS_RT_SIGPENDING,
        std::ptr::addr_of_mut!(pending) as u64,
        8,
        0,
        0,
        0,
        0,
    ) != 0
    {
        return false;
    }
    let ignored = (1u64 << (9 - 1)) | (1u64 << (19 - 1)) | sentry_reserved_signal_mask();
    (pending & !saved_mask & !ignored) != 0
}

unsafe fn reset_exec_signal_dispositions() {
    let dfl: [u64; 4] = [0, 0, 0, 0]; // sa_handler=SIG_DFL, flags=0, restorer=0, mask=0
    for sig in 1u64..=64 {
        if sig == 31 || sig == 32 || sig == 33 || sig == SIG_DETHREAD as u64 {
            continue;
        }
        host(SYS_RT_SIGACTION, sig, dfl.as_ptr() as u64, 0, 8, 0, 0);
    }
}

unsafe fn short_futex_timeout_for(op: u64) -> [i64; 2] {
    const ONE_MS_NS: i64 = 1_000_000;
    let cmd = op & !(FUTEX_PRIVATE_FLAG | FUTEX_CLOCK_REALTIME);
    if cmd == FUTEX_WAIT_BITSET {
        let clock = if (op & FUTEX_CLOCK_REALTIME) != 0 {
            0
        } else {
            1
        };
        let mut ts = [0i64; 2];
        host(SYS_CLOCK_GETTIME, clock, ts.as_mut_ptr() as u64, 0, 0, 0, 0);
        ts[1] += ONE_MS_NS;
        if ts[1] >= 1_000_000_000 {
            ts[0] += 1;
            ts[1] -= 1_000_000_000;
        }
        ts
    } else {
        [0, ONE_MS_NS]
    }
}

/// Translate a guest **absolute CLOCK_MONOTONIC** `FUTEX_WAIT_BITSET` deadline into the
/// host's monotonic timeline before the kernel sees it. Sentry virtualizes the guest's
/// CLOCK_MONOTONIC to start at 0 (`guest = host - GUEST_MONOTONIC_BASE_NS`, see
/// [`guest_monotonic_timespec`]), but `futex` is cell-local `host()` and the kernel
/// compares the absolute timeout against the RAW host CLOCK_MONOTONIC. So a deadline a
/// guest computes from its (offset) clock lands ~BASE ns in the PAST → the kernel returns
/// ETIMEDOUT immediately → every `pthread_cond_timedwait` / `std::condition_variable::
/// wait_until` / `base::WaitableEvent::TimedWait` (all FUTEX_WAIT_BITSET, absolute) returns
/// spuriously and the caller re-checks-then-re-waits forever = a BUSY-SPIN. This livelocked
/// Chromium's NetworkService (many threads hammering FUTEX_WAIT_BITSET) → 15s "no connection".
/// Returns the timeout pointer to actually pass to `host(futex)`: the rewritten host-monotonic
/// timespec in `out` for the absolute-monotonic case, or the original `d` unchanged otherwise
/// (relative FUTEX_WAIT, no-timeout, or FUTEX_CLOCK_REALTIME — CLOCK_REALTIME is not virtualized).
unsafe fn host_monotonic_futex_deadline(op: u64, d: u64, out: &mut [i64; 2]) -> u64 {
    if d == 0 {
        return 0;
    }
    let cmd = op & !(FUTEX_PRIVATE_FLAG | FUTEX_CLOCK_REALTIME);
    if cmd != FUTEX_WAIT_BITSET || (op & FUTEX_CLOCK_REALTIME) != 0 {
        return d; // relative timeout, or absolute-vs-CLOCK_REALTIME → no virtualization
    }
    let Some(&base) = GUEST_MONOTONIC_BASE_NS.get() else {
        return d; // clock not virtualized yet → guest clock == host clock
    };
    let g = d as *const i64;
    let total = (g.read() as i128) * 1_000_000_000 + g.add(1).read() as i128 + base as i128;
    out[0] = (total / 1_000_000_000) as i64;
    out[1] = (total % 1_000_000_000) as i64;
    out.as_ptr() as u64
}

unsafe fn futex_from_sigsys_trap(
    a: u64,
    b: u64,
    c: u64,
    d: u64,
    e: u64,
    f: u64,
    saved_mask: Option<u64>,
) -> i64 {
    // Re-base a guest-monotonic absolute deadline onto the host clock (see the fn doc).
    // `adj` must outlive every `host(SYS_FUTEX, …, d, …)` below — it backs the pointer.
    let mut adj = [0i64; 2];
    let d = host_monotonic_futex_deadline(b, d, &mut adj);
    let Some(saved_mask) = saved_mask else {
        return host(SYS_FUTEX, a, b, c, d, e, f);
    };
    let cmd = b & !(FUTEX_PRIVATE_FLAG | FUTEX_CLOCK_REALTIME);
    let interruptible_wait = (cmd == FUTEX_WAIT || cmd == FUTEX_WAIT_BITSET) && d == 0;
    if !interruptible_wait {
        return host(SYS_FUTEX, a, b, c, d, e, f);
    }

    // A futex wait issued from the SIGSYS handler is otherwise interruptible while
    // the thread is running on sentry's private trap altstack. Keep guest signals
    // pending until rt_sigreturn restores the guest context, but still report EINTR
    // once an unmasked guest signal is pending.
    block_guest_signals_until_sigreturn();
    loop {
        if pending_unmasked_guest_signal(saved_mask) {
            return EINTR;
        }
        let mut ts = short_futex_timeout_for(b);
        let timeout = ts.as_mut_ptr() as u64;
        let bitset = if cmd == FUTEX_WAIT_BITSET {
            if f == 0 {
                FUTEX_BITSET_MATCH_ANY
            } else {
                f
            }
        } else {
            f
        };
        let r = host(SYS_FUTEX, a, b, c, timeout, e, bitset);
        if r == -110 {
            continue;
        }
        if r == EINTR && pending_unmasked_guest_signal(saved_mask) {
            return EINTR;
        }
        return r;
    }
}

// ─── shared "simple" syscall dispatch: every syscall that just computes a return
// value (cell-local or delegated). Used by BOTH entry paths — the SUD/SIGSYS
// handler (`cell_layer1`, slow) and the rewrite fast-path dispatcher
// (`sentry_dispatch_simple`). The control-flow / register-manipulating syscalls
// (SENTINEL, clone(thread), execve) are NOT here: they need the trapped ucontext,
// so they stay in `cell_layer1` and the fast path routes them to `cf_stub`.
fn dispatch_simple(
    nr: i64,
    a: u64,
    b: u64,
    c: u64,
    d: u64,
    e: u64,
    f: u64,
    trap_saved_mask: Option<u64>,
) -> i64 {
    match nr {
        SYS_BRK => brk(a),
        SYS_MMAP => guest_mmap(a, b, c, d, e, f),
        // munmap/mremap of a range covering the loader image (exe / ld.so) would
        // remove the pages musl/node execute and read from → a not-present fault. The
        // guest never legitimately tears down its own loader, so refuse: munmap is a
        // benign no-op (the image stays mapped, which is correct), mremap -EINVAL.
        // (Defense-in-depth alongside the guest_mmap hint guard; the actual node crash
        // is an mmap, not these — but this closes the same footgun on every arm.)
        SYS_MUNMAP => {
            let _ = delegate(CTL_SHM_FLUSH, a, b, 1, 0, 0, 0);
            // A MAP_SHARED mapping lives in the shared POOL (inherited by every cell);
            // actually munmapping it would punch a hole in THIS cell's pool view and
            // break a later re-map of the same slice (which returns the same pool VA).
            // Treat unmapping a pool address as a no-op — the slice stays shared.
            if overlaps_loader(a, page_up(a + b)) || in_shm_pool(a) {
                0
            } else {
                let r = unsafe { host(SYS_MUNMAP, a, b, 0, 0, 0, 0) };
                if r == 0 {
                    guest_vma_forget_range(a, b);
                    arena_reclaim_mapping(a, b);
                }
                r
            }
        }
        SYS_MSYNC => delegate(CTL_SHM_FLUSH, a, b, 0, 0, 0, 0),
        SYS_MREMAP => {
            if overlaps_loader(a, page_up(a + b)) || in_shm_pool(a) {
                -22 // -EINVAL (can't remap a shared-pool slice)
            } else {
                let r = unsafe { host(SYS_MREMAP, a, b, c, d, e, f) };
                if r >= 0 {
                    guest_vma_forget_range(a, b);
                    guest_vma_note_mapping(r as u64, c);
                    let _ = delegate(CTL_SHM_MOVE, a, b, r as u64, c, 0, 0);
                }
                r
            }
        }
        // MPROTECT stays UNGUARDED: both musl and node legitimately mprotect their
        // GNU_RELRO region (within the loader span) read-only after relocation —
        // guarding it would break RELRO/GOT hardening. MADVISE on MAP_PRIVATE|ANON
        // (MADV_DONTNEED) re-faults as a present zero page, never error-4, so it is safe.
        // mincore(addr,len,vec): residency probe used by PartitionAlloc/V8/tcmalloc.
        // Cell-local: it reads only the cell's OWN page residency and writes the result
        // byte-vector straight into guest memory (host() runs in the cell's address
        // space). Delegating it to the supervisor returned ENOSYS (service() has no
        // arm), so chromium's allocators saw a halving probe-loop fail 88×/run; running
        // it in-cell makes it real. Needs nr 27 in the seccomp wall (added there).
        SYS_MPROTECT => {
            if (c & PROT_EXEC_BIT) != 0 && overlaps_sud_host_region(a, b) {
                -1 // -EPERM: executable guest pages must stay below SUD's host region.
            } else {
                let r = unsafe { host(SYS_MPROTECT, a, b, c, d, e, f) };
                if r == 0 {
                    let _ = delegate(CTL_SHM_PROTECT, a, b, c, 0, 0, 0);
                }
                r
            }
        }
        SYS_MADVISE | SYS_MINCORE | SYS_ARCH_PRCTL => unsafe { host(nr, a, b, c, d, e, f) },
        // PKU / memory-protection keys. Chromium 147's PartitionAlloc thread-isolation
        // (and V8's sandbox hardening) allocate a pkey at startup and CHECK-fail —
        // `int3`, ip:9e6b87e — if `pkey_alloc` returns ENOSYS; they do NOT fall back.
        // Run cell-local (host() in the cell's OWN mm): a pkey is per-mm, pkey_mprotect
        // tags the cell's own pages, and WRPKRU is an unprivileged instruction that
        // executes natively under SUD. Delegating to the supervisor was the bug — it
        // allocates the key in the WRONG process. These touch only the caller's own mm
        // and reach no host resource, so the wall allows them by nr (see install_wall).
        SYS_PKEY_MPROTECT => {
            if (c & PROT_EXEC_BIT) != 0 && overlaps_sud_host_region(a, b) {
                -1 // -EPERM: executable guest pages must stay below SUD's host region.
            } else {
                unsafe { host(SYS_PKEY_MPROTECT, a, b, c, d, e, f) }
            }
        }
        SYS_PKEY_ALLOC | SYS_PKEY_FREE => unsafe { host(nr, a, b, c, d, e, f) },
        SYS_FUTEX => unsafe { futex_from_sigsys_trap(a, b, c, d, e, f, trap_saved_mask) },
        SYS_GETPID | SYS_GETTID | SYS_GETPPID => unsafe { host(nr, 0, 0, 0, 0, 0, 0) },
        // Identity reads come from the cell's VIRTUALIZED credentials (default root,
        // mutated by the set*id arms below) — not the host uid, which is an isolation
        // detail invisible to the guest. Parity with the KVM/HVF guest (uid 0).
        SYS_GETUID => cred_get(&CELL_CREDS.ruid) as i64,
        SYS_GETEUID => cred_get(&CELL_CREDS.euid) as i64,
        SYS_GETGID => cred_get(&CELL_CREDS.rgid) as i64,
        SYS_GETEGID => cred_get(&CELL_CREDS.egid) as i64,
        SYS_GETRESUID => cred_getresuid(a, b, c),
        SYS_GETRESGID => cred_getresgid(a, b, c),
        SYS_UMASK => {
            let old = cell_umask_set(a as u32) as i64;
            sync_umask_to_supervisor();
            old
        }
        // Supplementary groups are virtualized as an empty set. `size == 0`
        // queries the count; any non-zero buffer also receives zero entries.
        // This matches the accepted setgroups no-op below and prevents libc/id
        // from treating the otherwise-unimplemented syscall as fatal.
        SYS_GETGROUPS => 0,
        // Privilege drops (nginx worker, su-exec, postgres): mutate the virtual
        // creds and succeed — the guest owns its own uid space. setfsuid/setfsgid
        // return the PREVIOUS id (never an error); setgroups is accepted as a no-op.
        SYS_SETUID => {
            let r = cred_setuid(a as u32);
            if r == 0 {
                sync_creds_to_supervisor();
            }
            r
        }
        SYS_SETGID => {
            let r = cred_setgid(a as u32);
            if r == 0 {
                sync_creds_to_supervisor();
            }
            r
        }
        SYS_SETRESUID => {
            let r = cred_setresuid(a as u32, b as u32, c as u32);
            if r == 0 {
                sync_creds_to_supervisor();
            }
            r
        }
        SYS_SETRESGID => {
            let r = cred_setresgid(a as u32, b as u32, c as u32);
            if r == 0 {
                sync_creds_to_supervisor();
            }
            r
        }
        SYS_SETREUID => {
            let r = cred_setresuid(a as u32, b as u32, ID_UNCHANGED);
            if r == 0 {
                sync_creds_to_supervisor();
            }
            r
        }
        SYS_SETREGID => {
            let r = cred_setresgid(a as u32, b as u32, ID_UNCHANGED);
            if r == 0 {
                sync_creds_to_supervisor();
            }
            r
        }
        SYS_SETFSUID => {
            let old = cred_get(&CELL_CREDS.fsuid) as i64;
            let u = a as u32;
            if cred_privileged()
                || u == cred_get(&CELL_CREDS.ruid)
                || u == cred_get(&CELL_CREDS.euid)
                || u == cred_get(&CELL_CREDS.suid)
                || u == cred_get(&CELL_CREDS.fsuid)
            {
                cred_set(&CELL_CREDS.fsuid, u);
                sync_creds_to_supervisor();
            }
            old
        }
        SYS_SETFSGID => {
            let old = cred_get(&CELL_CREDS.fsgid) as i64;
            let g = a as u32;
            if cred_privileged()
                || g == cred_get(&CELL_CREDS.rgid)
                || g == cred_get(&CELL_CREDS.egid)
                || g == cred_get(&CELL_CREDS.sgid)
                || g == cred_get(&CELL_CREDS.fsgid)
            {
                cred_set(&CELL_CREDS.fsgid, g);
                sync_creds_to_supervisor();
            }
            old
        }
        SYS_SETGROUPS => 0,
        // set_tid_address(tidptr): record THIS thread's clear_child_tid AND return the
        // caller's REAL tid. The old `424242` stub returned a bogus constant that libc
        // (musl __init_tp / glibc) CACHES as pthread_self()->tid — diverging from the
        // real gettid() the cell forwards — so raise()/pthread_kill() do tgkill(pid,
        // 424242, sig) to a nonexistent tid. The tidptr is a guest VA in the cell's OWN
        // address space, so forward directly (like the MMAP/FUTEX arms). Wall-allowlisted.
        SYS_SET_TID_ADDRESS => unsafe { host(SYS_SET_TID_ADDRESS, a, 0, 0, 0, 0, 0) },
        // set_robust_list(head, len): register THIS thread's robust-futex list so
        // the REAL kernel walks it on thread death and OR-s FUTEX_OWNER_DIED into any
        // mutex the dead thread held (waking deadlocked siblings) — VM parity. The
        // old `=> 0` no-op acked but registered nothing, so a cell thread that died
        // (watchdog SIGKILL, segfault, abrupt RUN-step exit) holding a robust
        // pthread_mutex_t deadlocked its siblings forever. `head` (`a`) is a guest VA
        // in the cell's OWN address space → forward directly (like SET_TID_ADDRESS).
        // Wall-allowlisted (nr 273).
        SYS_SET_ROBUST_LIST => unsafe { host(SYS_SET_ROBUST_LIST, a, b, 0, 0, 0, 0) },
        // rseq(rseq, len, flags, sig): register THIS thread's restartable-sequence
        // area. We must NOT forward this to the host kernel in the in-process sentry
        // model. rseq registration makes the kernel run an rseq fixup on EVERY
        // return-to-userspace on this thread (whenever TIF_NOTIFY_RESUME is set, e.g.
        // after a CPU migration) — and sentry injects many of its OWN syscalls on the
        // guest thread (every delegated syscall is at least a host futex), so the fixup
        // fires constantly. The fixup reads rseq.rseq_cs and writes rseq.cpu_id at the
        // GUEST's struct, which lives in the guest's glibc TLS — and that TLS is
        // mmap-allocated into sentry's controlled guest arena, a region that an execve's
        // ld.so remaps and that sibling CLONE_VM threads churn. The moment the struct's
        // page is mid-remap/clobbered, the fixup dereferences a garbage rseq_cs pointer
        // and the kernel raises a fatal SIGSEGV (SI_KERNEL, si_addr=0) we cannot trap —
        // the `sh -c "rustc …"` / `rustc` compile (LLVM codegen threads) exit-139. A real
        // VM never hits this: there the guest kernel runs the fixup only on the guest's
        // own syscalls, over a coherent address space. So decline registration (-ENOSYS):
        // glibc>=2.35 then cleanly disables rseq (sets __rseq_size=0 and falls back to
        // the getcpu vdso), and jemalloc/tcmalloc/Go's scheduler all detect-and-fall-back.
        // Correctness is preserved; only the rseq fast-path optimization is forgone. (The
        // cell already unregisters its OWN inherited glibc rseq in cell_main via
        // unregister_host_libc_rseq, so with no guest registration current->rseq stays
        // NULL on guest threads and the kernel does no fixup at all.) Wall-allowlisted
        // (nr 334) so the trap still reaches us rather than executing raw.
        SYS_RSEQ => -38, // -ENOSYS
        // pause()/rt_sigtimedwait(): SIGNAL-WAITS that must run on THIS (guest) thread —
        // they block until a signal is delivered to THIS cell. Delegating (the old `_`
        // fall-through) ran them on the SUPERVISOR servicer, waiting for a signal to the
        // SUPERVISOR that the guest's USR1/SIGCHLD never reach. Cell-local (the cell IS
        // the guest process; `a` = the sigset is a guest VA). SIGSYS stays deliverable
        // (SA_NODEFER) so a nested guest handler's syscalls still trap during the wait.
        SYS_PAUSE => unsafe { host(SYS_PAUSE, 0, 0, 0, 0, 0, 0) },
        SYS_RT_SIGTIMEDWAIT => unsafe { host(SYS_RT_SIGTIMEDWAIT, a, b, c, d, 0, 0) },
        // rt_sigsuspend(set@a, sigsetsize@b): the THIRD signal-wait — and the one a
        // shell's `wait`/job-control loop spins on. It must run CELL-LOCAL for the
        // same reason as PAUSE/RT_SIGTIMEDWAIT: rt_sigsuspend atomically swaps THIS
        // thread's mask to `set` and blocks until a signal is delivered to THIS cell,
        // runs the guest handler, then returns -EINTR. Delegated (the old `_`
        // fall-through), it ran on the SUPERVISOR servicer instead — which receives
        // the supervisor's own SIGCHLD storm (the fork-heavy guest workload makes the
        // supervisor reap constantly), so the servicer's rt_sigsuspend returned -EINTR
        // IMMEDIATELY and REPEATEDLY without ever delivering a signal to the guest. The
        // guest's `wait` saw a phantom EINTR with its SIGCHLD-flag still clear, so it
        // re-suspended without ever reaping its child — an unbounded busy-spin (the C1
        // rewrite-mode hang: 58M delegated sigsuspends on slot 0, child never reaped).
        // `set` is a guest VA in the cell's own address space. Wall-allowlisted (130).
        //
        // SAFETY: the requested mask must NEVER block SIGSYS. rt_sigsuspend applies
        // `set` for the duration of the wait; a nested guest handler's syscall traps
        // via SUD into a SIGSYS during that window, and a BLOCKED synchronous SIGSYS is
        // force-delivered by the kernel as a fatal signal (cell death). The
        // rt_sigprocmask arm force-unblocks SIGSYS for the same reason, but a transient
        // suspend mask can't be post-adjusted — so clear SIGSYS from a PRIVATE copy of
        // the guest's set (signal 31 → bit 30). SIGSYS is the sentry's own mechanism,
        // invisible to the guest, so this is transparent. A malformed (NULL / wrong
        // size) set is passed through for the kernel to reject.
        SYS_RT_SIGSUSPEND => unsafe {
            if a == 0 || b != 8 {
                host(SYS_RT_SIGSUSPEND, a, b, 0, 0, 0, 0)
            } else {
                let set = sanitize_guest_sigmask(std::ptr::read_unaligned(a as *const u64));
                host(
                    SYS_RT_SIGSUSPEND,
                    std::ptr::addr_of!(set) as u64,
                    8,
                    0,
                    0,
                    0,
                    0,
                )
            }
        },
        // prctl: allowlist by subop (arg0 = option). dispatch_simple runs IN the
        // cell's own address space (that's why brk/mmap live here), so a buffer
        // pointer in `b` is a guest VA valid for a DIRECT host prctl — same as the
        // neighboring MMAP/MPROTECT arms call `host` on raw guest pointers.
        SYS_PRCTL => match a {
            // PR_SET_NAME / PR_GET_NAME: thread comm name (16 bytes via `b`).
            // PR_SET_PDEATHSIG: parent-death signal (cell-local). Forward to host.
            PR_SET_NAME | PR_GET_NAME | PR_SET_PDEATHSIG => unsafe {
                host(SYS_PRCTL, a, b, c, d, e, f)
            },
            // Capability bounding set is empty in the cell (uid-dropped, no caps);
            // report "not in set" rather than forwarding.
            PR_CAPBSET_READ => 0,
            // Dumpable is process-local kernel state. Chromium/crash handlers toggle
            // it to control core dumping; lying here lets a fatal Chrome CHECK dump
            // sentry's inherited shared-pool VMA and fault gigabytes of shmem.
            PR_GET_DUMPABLE | PR_SET_DUMPABLE => unsafe { host(SYS_PRCTL, a, b, c, d, e, f) },
            // VMA naming is metadata only. Chromium/V8 names its pointer-compression
            // cage with PR_SET_VMA_ANON_NAME and treats failure as fatal even though
            // the name has no execution semantics. Preserve the name on hosts that
            // support it; otherwise report success so the guest sees a modern Linux
            // kernel's developer-facing behavior without tying sentry to host support.
            PR_SET_VMA if b == PR_SET_VMA_ANON_NAME => {
                let r = unsafe { host(SYS_PRCTL, a, b, c, d, e, f) };
                if r == -22 || r == -38 {
                    0
                } else {
                    r
                }
            }
            // Dangerous subops that would breach the wall (install a competing
            // seccomp filter, flip no_new_privs, or re-home syscall-user-dispatch).
            // NEVER allow — refuse with EPERM.
            PR_SET_SECCOMP | PR_SET_NO_NEW_PRIVS => -1,
            x if x == PR_SET_SYSCALL_USER_DISPATCH as u64 => -1,
            // Everything else stays ENOSYS (unchanged from the old blanket arm).
            _ => -38,
        },
        // ─── process-group / session (job control: shells, tini/dumb-init/s6,
        // npm/yarn detached children, the postgres postmaster). These mutate or
        // read the CALLING process's session/pgrp, so they run CELL-LOCAL here —
        // in service() they would change the SUPERVISOR, not the cell. The cell's
        // seccomp wall allowlists these nrs so the forwarded `host()` call (above
        // the SUD floor) reaches the kernel; the GUEST's own call still traps via
        // SUD into this arm first (guest code is below the floor), so the
        // ownership validation below cannot be bypassed.
        //
        // setsid()/getpgrp(): no args / operate on self — forward directly.
        SYS_SETSID | SYS_GETPGRP => unsafe { host(nr, 0, 0, 0, 0, 0, 0) },
        // setpgid(pid, pgid): re-homes a process group. The kernel already
        // restricts the target to the caller or a child IN THE CALLER'S SESSION
        // (EPERM otherwise), but we additionally refuse any non-self pid that
        // isn't a member of THIS sandbox — a guest must never reach a host /
        // supervisor / other-tenant process group. pid==0 means "self": always
        // allowed. The pgid (`b`) is validated by the kernel (must be in the
        // caller's session), so forwarding it is safe.
        SYS_SETPGID => {
            if a as i32 == 0 || pid_in_sandbox(a as i32) {
                unsafe { host(SYS_SETPGID, a, b, 0, 0, 0, 0) }
            } else {
                -1 // -EPERM: not a process group inside this sandbox
            }
        }
        // getpgid(pid)/getsid(pid): read-only. pid==0 reads self — forward. A
        // non-self pid is only probed if it belongs to this sandbox; otherwise we
        // refuse to leak a foreign process's pgid/sid and instead return the
        // CELL's own (forward with pid 0), which is the benign, info-free answer a
        // shell expects when it can't see the target.
        SYS_GETPGID | SYS_GETSID => {
            if a as i32 == 0 || pid_in_sandbox(a as i32) {
                unsafe { host(nr, a, 0, 0, 0, 0, 0) }
            } else {
                unsafe { host(nr, 0, 0, 0, 0, 0, 0) }
            }
        }
        SYS_CLONE3 => -38,
        // fork/vfork: a new PROCESS. Works via the fast path too — the CoW child
        // returns 0 from the real fork, re-arms SUD, and resumes at the call site
        // via its copied stack (no ucontext needed).
        SYS_FORK | SYS_VFORK => {
            // The forking thread's slot — its rseq registration tracking lives here
            // and must be carried to the child's fresh slot below (captured pre-fork,
            // while %gs still holds the parent slot).
            let parent_slot = current_slot();
            let slot = alloc_slot();
            if slot as usize >= MAX_SLOTS {
                -11
            } else if delegate(CTL_ENSURE_SERVICER, slot as u64, 0, 0, 0, 0, 0) < 0 {
                free_slot(slot);
                -11
            } else {
                delegate(CTL_FORK_TABLE, slot as u64, nr as u64, 0, 0, 0, 0);
                unsafe {
                    (*ring_at(slot as u64)).fork_parent = host(SYS_GETPID, 0, 0, 0, 0, 0, 0) as i32;
                }
                let child = unsafe { host(SYS_FORK, 0, 0, 0, 0, 0, 0) };
                if child < 0 {
                    unsafe { (*ring_at(slot as u64)).fork_parent = 0 };
                    delegate(CTL_FORK_CANCEL, slot as u64, 0, 0, 0, 0, 0);
                    child
                } else if child == 0 {
                    set_slot(slot as u64);
                    // rseq is INHERITED across fork (the kernel duplicates the parent's
                    // current->rseq into the child), but our per-slot RSEQ_ADDR tracking
                    // is keyed by slot and the child just took a FRESH slot whose tracking
                    // is zeroed. Carry the parent slot's registration (CoW-inherited into
                    // this child's RSEQ_ADDR copy) onto the new slot so a subsequent
                    // in-child execve's unregister_current_rseq_for_exec() actually clears
                    // the inherited registration. Without this the tracking is lost, the
                    // unregister no-ops, and current->rseq stays pointing at the parent's
                    // (guest-TLS) rseq struct; the freshly-exec'd image's ld.so then remaps
                    // the mmap arena over that address, so rseq.rseq_cs becomes arbitrary
                    // library bytes — and the kernel's rseq fixup on the next CPU migration
                    // dereferences the garbage rseq_cs pointer → EFAULT → force_sig(SIGSEGV)
                    // (SI_KERNEL, si_addr=0). This is the long-standing `sh -c "rustc …"` /
                    // apt-get fork-then-exec exit-139: a direct launcher exec runs
                    // cell_main → unregister_host_libc_rseq and never hits it, but a guest
                    // fork→execve child does.
                    register_rseq_for_child(parent_slot, slot as u64);
                    // This is a CoW fork: the child gets its OWN fresh address space, so
                    // it is the sole user of its mm. Reset the inherited (CoW-copied)
                    // sharer count so a later in-child execve correctly rewinds the arena.
                    MM_USERS.store(1, Ordering::Release);
                    unsafe {
                        set_ring_owner(
                            slot as u64,
                            host(SYS_GETPID, 0, 0, 0, 0, 0, 0) as i32,
                            host(SYS_GETTID, 0, 0, 0, 0, 0, 0) as i32,
                        );
                        host(
                            157,
                            PR_SET_SYSCALL_USER_DISPATCH as u64,
                            PR_SYS_DISPATCH_ON,
                            WINDOW_FLOOR,
                            USER_TOP - WINDOW_FLOOR,
                            std::ptr::addr_of!(SELECTOR) as u64,
                            0,
                        );
                        // The fork child took a new slot, so give it that slot's own SIGSYS
                        // alt-stack (clone'd threads do the same via child_thread_init):
                        // otherwise it keeps the inherited sigaltstack registration pointing
                        // at the PARENT slot's ALT_STACKS buffer (its own CoW copy — valid,
                        // but the wrong slot's), so cell_layer1 would run on a foreign slot's
                        // altstack.
                        register_altstack();
                    }
                    0
                } else {
                    delegate(CTL_BIND_SLOT, slot as u64, child as u64, 0, 0, 0, 0);
                    child
                }
            }
        }
        SYS_WAIT4 => {
            let r = unsafe { host(SYS_WAIT4, a, b, c, d, e, f) };
            if r > 0 {
                delegate(CTL_REAP, r as u64, 0, 0, 0, 0, 0);
            }
            r
        }
        // waitid(idtype@a, id@b, siginfo*@c OUT, options@d, rusage*@e OUT?): like
        // wait4 this is CELL-LOCAL — the reaped child was forked in this cell, so
        // only this process is its parent (a serviced host(waitid) would -ECHILD).
        // The direct host call writes the cell's own siginfo/rusage buffers (c/e are
        // valid guest VAs here, same address space). Delegate CTL_REAP — so the
        // supervisor frees the child's ring slot + fd table, identical bookkeeping to
        // wait4's delegate(CTL_REAP, pid) — ONLY when the child was actually reaped:
        // a non-WNOWAIT call whose siginfo reports a TERMINAL si_code (CLD_EXITED /
        // CLD_KILLED / CLD_DUMPED = 1/2/3). A CLD_STOPPED/CONTINUED/TRAPPED report
        // leaves the child alive (must NOT free its table), and WNOHANG-with-no-child
        // zeroes si_pid. P_PID/P_PIDFD/P_ALL all flow through unchanged.
        SYS_WAITID => {
            let r = unsafe { host(SYS_WAITID, a, b, c, d, e, f) };
            if r == 0 && c != 0 && (d & WNOWAIT) == 0 {
                // siginfo header: si_code @ offset 8, si_pid @ SIGINFO_SI_PID_OFF.
                let si_code = unsafe { ((c as usize + 8) as *const i32).read() };
                let reaped = unsafe { ((c as usize + SIGINFO_SI_PID_OFF) as *const i32).read() };
                let terminal = (1..=3).contains(&si_code); // CLD_EXITED/KILLED/DUMPED
                if reaped > 0 && terminal {
                    delegate(CTL_REAP, reaped as u64, 0, 0, 0, 0, 0);
                }
            }
            r
        }
        SYS_EXIT => {
            // Thread exit (nr 60 ends only the calling thread; the process ends via
            // exit_group). Release this thread's ring slot so a long-lived,
            // thread-churning guest (chromium spawns/joins thousands of threads, node
            // worker pools, postgres backends) can't exhaust the bitmap → clone
            // -EAGAIN. The thread is quiescent here (voluntary exit, no delegated call
            // in flight), and the slot's persistent servicer stays parked for the next
            // tenant (ensure_servicer is idempotent). Slot 0 (main cell) is left
            // reserved. musl frees its stack via __unmapself first, so it takes the
            // self-stack-unmap collapse in cell_layer1 and never reaches here; glibc
            // exits cleanly on a still-valid stack and lands here.
            let slot = current_slot();
            notify_vfork_release();
            if slot != 0 {
                free_slot(slot as u32);
            }
            unsafe { host(SYS_EXIT, a, 0, 0, 0, 0, 0) }
        }
        SYS_EXIT_GROUP => {
            let me = unsafe { host(SYS_GETPID, 0, 0, 0, 0, 0, 0) };
            notify_vfork_release();
            delegate(CTL_REAP, me as u64, 0, 0, 0, 0, 0);
            unsafe { host(SYS_EXIT_GROUP, a, 0, 0, 0, 0, 0) }
        }
        // rt_sigaction: install the guest's REAL handler — EXCEPT SIGSYS (31, our
        // wall trap) and SIG_DETHREAD (64, our execve de_thread teardown signal),
        // whose sentry handlers must never be overridden. Override sa_restorer to our
        // above-floor trampoline.
        SYS_RT_SIGACTION => {
            if a as i32 == 31 || a as i32 == SIG_DETHREAD {
                0
            } else if b != 0 {
                unsafe {
                    let src = b as *const u64;
                    let flags = src.add(1).read() & 0xffff_ffff;
                    let k = [
                        src.read(),
                        // sa_flags: force SA_RESTORER (0x04000000 — we supply our own
                        // restorer below) and STRIP SA_ONSTACK (0x08000000). The guest's
                        // sigaltstack(2) is delegated to a no-op (the sentry OWNS the
                        // per-thread kernel altstack for its SUD/SIGSYS handler), so the
                        // guest has no altstack of its own. If SA_ONSTACK survived, the
                        // kernel would deliver the guest's handler (cc1/rustc/glibc's
                        // SIGSEGV stack-overflow/ICE handler) ONTO the sentry's SUD
                        // altstack — nesting with / clobbering cell_layer1's frames →
                        // guest-memory corruption that surfaces as a wild NULL/garbage
                        // deref (SIGSEGV si_addr≈0x3d) deep inside a complex workload
                        // (compilers, V8/node, Chromium). Stripping SA_ONSTACK runs the
                        // guest's handler on its OWN (regular) stack — correct, since it
                        // has no separate altstack anyway — leaving the SUD altstack safe.
                        (flags | 0x0400_0000) & !0x0800_0000,
                        sig_restorer as u64,
                        sanitize_guest_sigmask(src.add(3).read()),
                    ];
                    host(SYS_RT_SIGACTION, a, k.as_ptr() as u64, c, d, 0, 0)
                }
            } else {
                unsafe { host(SYS_RT_SIGACTION, a, 0, c, d, 0, 0) }
            }
        }
        SYS_RT_SIGPROCMASK => unsafe {
            let r = host(SYS_RT_SIGPROCMASK, a, b, c, d, 0, 0);
            // Force sentry's reserved signals UNBLOCKED no matter what the guest set:
            // SIGSYS (31, the SUD/seccomp wall trap) and SIG_DETHREAD (64, the execve
            // de_thread teardown) must always be deliverable to a guest thread.
            let mut s: u64 = sentry_reserved_signal_mask();
            host(
                SYS_RT_SIGPROCMASK,
                1,
                std::ptr::addr_of_mut!(s) as u64,
                0,
                8,
                0,
                0,
            );
            r
        },
        SYS_SETITIMER | SYS_GETITIMER | SYS_ALARM => unsafe { host(nr, a, b, c, d, e, f) },
        // Read-only/current-cell data. These write only to guest buffers in this
        // address space, so delegating them just adds ring/futex exposure and can
        // observe the wrong process/thread context.
        SYS_CLOCK_GETTIME
            if a as i32 == libc::CLOCK_MONOTONIC || a as i32 == libc::CLOCK_BOOTTIME =>
        {
            if b == 0 {
                -14
            } else {
                let ts = guest_monotonic_timespec();
                unsafe { std::ptr::copy_nonoverlapping(ts.as_ptr(), b as *mut u8, ts.len()) };
                0
            }
        }
        SYS_CLOCK_GETTIME => unsafe { host(SYS_CLOCK_GETTIME, a, b, 0, 0, 0, 0) },
        SYS_GETRANDOM => unsafe { host(SYS_GETRANDOM, a, b, c, 0, 0, 0) },
        // membarrier MUST run IN THE CELL — it fences the CALLING process's threads.
        // Delegating it to the supervisor (the old service() arm) barriered the WRONG
        // process, making it a silent no-op for the guest. On x86 (TSO) membarrier's
        // PRIVATE_EXPEDITED is the asymmetric fence that prevents store-load reordering
        // between a process's own threads — exactly what lock-free shared-memory queues
        // (ipcz/mojo parcel rings, V8, tcmalloc/glibc arenas, base::AtomicFlag fast
        // paths) rely on: the slow path issues membarrier so the fast path can omit its
        // fence. A no-op membarrier ⇒ a publishing store can stay reordered behind a
        // load ⇒ a peer thread waits on a flag it never observes (a silent cross-thread
        // visibility stall). Run cell-local: the kernel confines PRIVATE_EXPEDITED to
        // this process's own threads and reaches no host resource. (GLOBAL_EXPEDITED
        // would IPI threads of OTHER processes registered for it — a minor cross-tenant
        // nuisance, never a breach; acceptable, and the guest uses PRIVATE in practice.)
        SYS_MEMBARRIER => unsafe { host(SYS_MEMBARRIER, a, b, c, d, e, f) },
        SYS_PPOLL => unsafe { delegate_with_sigmask(nr, [a, b, c, d, e, f], d, e) },
        SYS_EPOLL_PWAIT => unsafe { delegate_with_sigmask(nr, [a, b, c, d, e, f], e, f) },
        SYS_PSELECT6 => unsafe {
            let (sigmask_ptr, sigset_size) = if f == 0 {
                (0, 0)
            } else {
                (
                    std::ptr::read_unaligned(f as *const u64),
                    std::ptr::read_unaligned((f + 8) as *const u64),
                )
            };
            delegate_with_sigmask(nr, [a, b, c, d, e, f], sigmask_ptr, sigset_size)
        },
        // Delegated to the supervisor. Carry the trapped guest sigmask so the parked
        // transport wait defers guest signals (no nested handler on the SIGSYS
        // altstack); fast-path callers pass None via `delegate`.
        _ => delegate_masked(nr, a, b, c, d, e, f, trap_saved_mask),
    }
}

unsafe fn delegate_with_sigmask(
    nr: i64,
    args: [u64; 6],
    sigmask_ptr: u64,
    sigset_size: u64,
) -> i64 {
    const SIG_SETMASK: u64 = 2;
    if sigmask_ptr == 0 || sigset_size != 8 {
        return delegate(nr, args[0], args[1], args[2], args[3], args[4], args[5]);
    }
    let set = sanitize_guest_sigmask(std::ptr::read_unaligned(sigmask_ptr as *const u64));
    let mut old = 0u64;
    let r = host(
        SYS_RT_SIGPROCMASK,
        SIG_SETMASK,
        std::ptr::addr_of!(set) as u64,
        std::ptr::addr_of_mut!(old) as u64,
        8,
        0,
        0,
    );
    if r < 0 {
        return r;
    }
    let ret = delegate(nr, args[0], args[1], args[2], args[3], args[4], args[5]);
    host(
        SYS_RT_SIGPROCMASK,
        SIG_SETMASK,
        std::ptr::addr_of!(old) as u64,
        0,
        8,
        0,
        0,
    );
    let mut sigsys = sentry_reserved_signal_mask();
    host(
        SYS_RT_SIGPROCMASK,
        1,
        std::ptr::addr_of_mut!(sigsys) as u64,
        0,
        8,
        0,
        0,
    );
    ret
}

unsafe fn rt_sigprocmask_in_ucontext(
    uc: *mut libc::ucontext_t,
    how: u64,
    set_ptr: u64,
    old_ptr: u64,
    sigset_size: u64,
) -> i64 {
    const SIG_BLOCK: u64 = 0;
    const SIG_UNBLOCK: u64 = 1;
    const SIG_SETMASK: u64 = 2;
    if sigset_size != 8 {
        return -22; // -EINVAL
    }
    let saved = std::ptr::addr_of_mut!((*uc).uc_sigmask) as *mut u64;
    let current = std::ptr::read_unaligned(saved);
    if old_ptr != 0 {
        std::ptr::write_unaligned(old_ptr as *mut u64, current);
    }
    if set_ptr == 0 {
        return 0;
    }
    let set = std::ptr::read_unaligned(set_ptr as *const u64);
    let mut next = match how {
        SIG_BLOCK => current | set,
        SIG_UNBLOCK => current & !set,
        SIG_SETMASK => set,
        _ => return -22, // -EINVAL
    };
    next = sanitize_guest_sigmask(next); // never block sentry-private signals
    std::ptr::write_unaligned(saved, next);
    0
}

/// `extern "C"` shim called by the rewrite fast-path asm dispatcher: reads the
/// 7-word register array it built (`[nr, a, b, c, d, e, f]`) and runs the shared
/// simple dispatch. Read-only on the array (the dispatcher restores the guest
/// arg regs from it afterwards).
#[no_mangle]
extern "C" fn sentry_dispatch_simple(regs: *const u64) -> i64 {
    let r = |i: usize| unsafe { *regs.add(i) };
    dispatch_simple(r(0) as i64, r(1), r(2), r(3), r(4), r(5), r(6), None)
}

// ─── CELL: Layer-1 handler. Cell-local syscalls run here; everything else is
// delegated to the supervisor. The handler issues only allowlisted syscalls. ──
extern "C" fn cell_layer1(_sig: c_int, _info: *mut libc::siginfo_t, ctx: *mut c_void) {
    let uc = ctx as *mut libc::ucontext_t;
    let g = unsafe { &mut (*uc).uc_mcontext.gregs };
    let nr = g[REG_RAX];
    // SENTRY_REGDIFF: snapshot guest GP regs (indices 0..17 = R8..R15,RDI,RSI,RBP,RBX,
    // RDX,RAX,RCX,RSP,RIP) on entry, to diff against the exit state before returning.
    let regdiff_snap: Option<[libc::greg_t; 17]> =
        if std::hint::black_box(REGDIFF.load(std::sync::atomic::Ordering::Relaxed)) {
            let mut s = [0 as libc::greg_t; 17];
            let mut i = 0;
            while i < 17 {
                // read_volatile: the optimizer can otherwise PROVE g[i]==snap[i] across the
                // handler (the only visible write to g is g[RAX]=ret) and fold `changed` to 0,
                // DCE-ing the whole diff. Volatile loads force a real re-read so a re-entrant
                // signal's invisible write to this same ucontext is actually observed.
                s[i] = unsafe { std::ptr::read_volatile(&g[i]) };
                i += 1;
            }
            Some(s)
        } else {
            None
        };
    // DIAGNOSTIC: stash the guest call-site %rip for this slot so delegate_on can
    // stamp it into the ring (DSYS trace → addr2line). One plain store, gated.
    if unsafe { SYSCALLTRACE || SLOTDUMP } {
        let s = current_slot() as usize;
        if s < MAX_SLOTS {
            unsafe { LAST_TRAP_RIP[s] = g[REG_RIP] as u64 };
            // Scan up to 32 words above the guest %rsp for the first return-address in
            // the main-exe range — the ipcz caller (the syscall %rip is libc). The guest
            // stack is mapped well above %rsp, so this read is safe; bounded to 256B.
            let rsp = g[REG_RSP] as u64;
            let mut caller = 0u64;
            if rsp != 0 && rsp < USER_TOP {
                for i in 0..32usize {
                    let w =
                        unsafe { std::ptr::read_volatile((rsp + (i as u64) * 8) as *const u64) };
                    if w >= EXE_DYN_BASE && w < 0x3f00_0000 {
                        caller = w;
                        break;
                    }
                }
            }
            unsafe { LAST_TRAP_CALLER[s] = caller };
        }
    }
    let (a, b, c, d, e, f) = (
        g[REG_RDI] as u64,
        g[REG_RSI] as u64,
        g[REG_RDX] as u64,
        g[REG_R10] as u64,
        g[REG_R8] as u64,
        g[REG_R9] as u64,
    );
    if nr == SYS_RT_SIGPROCMASK {
        let ret = unsafe { rt_sigprocmask_in_ucontext(uc, a, b, c, d) };
        g[REG_RAX] = ret;
        return;
    }
    // Zygote CHECKPOINT: the warm guest issued the SENTINEL when "ready". Capture
    // its full register file and longjmp back to the zygote loop in cell_main.
    // Restore the cell's own TLS first (the handler otherwise runs with the
    // guest's %fs, and siglongjmp touches glibc TLS) — same as execve emulation.
    if nr == SENTINEL && WARM_MODE.load(Ordering::Relaxed) {
        unsafe {
            // Capture the guest's TLS (ld.so set %fs for a dynamic guest), THEN
            // restore the cell's own TLS so siglongjmp's glibc TLS use is sound.
            host(
                SYS_ARCH_PRCTL,
                ARCH_GET_FS,
                std::ptr::addr_of_mut!(GUEST_FS) as u64,
                0,
                0,
                0,
                0,
            );
            host(SYS_ARCH_PRCTL, ARCH_SET_FS, CELL_FS, 0, 0, 0, 0);
            for i in 0..18 {
                CAPTURED[i] = g[i];
            }
            siglongjmp(std::ptr::addr_of_mut!(JMPBUF) as *mut c_void, 1);
        }
    }
    let ret: i64 = match nr {
        // clone3 is a uapi wrapper around clone with a struct argument. It is still
        // a control-flow syscall for sentry: the child must resume from the trapped
        // ucontext, and clone3.stack is the LOW address plus stack_size rather than
        // old-clone's direct child stack pointer.
        SYS_CLONE3 => unsafe {
            if a == 0 || b < 64 {
                -22 // -EINVAL
            } else {
                let p = a as *const u64;
                let flags = std::ptr::read_unaligned(p.add(0));
                let pidfd = std::ptr::read_unaligned(p.add(1));
                let child_tid = std::ptr::read_unaligned(p.add(2));
                let parent_tid = std::ptr::read_unaligned(p.add(3));
                let exit_signal = std::ptr::read_unaligned(p.add(4));
                let stack_base = std::ptr::read_unaligned(p.add(5));
                let stack_size = std::ptr::read_unaligned(p.add(6));
                let tls = std::ptr::read_unaligned(p.add(7));
                let old_clone_stack = if stack_base != 0 && stack_size != 0 {
                    stack_base.wrapping_add(stack_size)
                } else {
                    stack_base
                };
                let old_clone_parent_tid = if (flags & CLONE_PIDFD) != 0 {
                    pidfd
                } else {
                    parent_tid
                };
                handle_clone(
                    g,
                    flags | (exit_signal & 0xff),
                    old_clone_stack,
                    old_clone_parent_tid,
                    child_tid,
                    tls,
                )
            }
        },
        // thread creation is cell-LOCAL (a new thread in the cell's own address
        // space). Allocate it a ring slot, then do_clone forwards the real clone
        // and the trampoline arms SUD + sets %gs on the child. A thread `exit`(60)
        // ends only that thread (≠ exit_group, which is delegated to tear the cell
        // down). clone3 → ENOSYS so glibc falls back to clone.
        SYS_CLONE => unsafe { handle_clone(g, a, b, c, d, e) },
        // execve is EMULATED in-cell (a raw execve would reset SUD and leave the new
        // program seccomp-walled-but-not-interposed). Replaces the guest image in
        // place, keeping SUD/handler/seccomp, and redirects this context to the new
        // entry. Returns into the new program (never falls through to set RAX).
        SYS_EXECVE => {
            emulate_execve(g, a, b, c);
            return; // emulate_execve set RIP/RSP for the new program (or RAX on error)
        }
        // THREAD TEARDOWN: the guest is unmapping the very stack it is running on
        // (guest RSP ∈ [a, a+b)). This is musl's `pthread_exit`→`__unmapself`
        // (munmap own stack, then exit(0), crafted to touch no stack between). If we
        // serviced the munmap and RETURNED, the guest would resume on the freed
        // stack and its trailing `exit` SIGSYS would re-enter cell_layer1 on dead
        // memory → SIGSEGV (the multi-threaded crash that gated node/V8, alpine
        // python, and chromium's constant thread churn). Collapse it: release this
        // thread's ring slot (it is quiescent — the thread is voluntarily exiting,
        // no delegated call in flight), then `unmap_and_exit` does the munmap+exit in
        // asm without ever touching the dying stack. The slot's persistent servicer
        // stays parked and is reused by the next tenant (ensure_servicer is
        // idempotent). glibc never self-unmaps its running stack (cached/freed by
        // another thread), so it exits via the plain SYS_EXIT arm below and never
        // trips this guard. Slot 0 (main cell) is never freed here.
        SYS_MUNMAP
            if b != 0 && {
                let gsp = g[REG_RSP] as u64;
                gsp >= a && gsp < a.wrapping_add(b)
            } =>
        {
            let slot = current_slot();
            notify_vfork_release();
            if slot != 0 {
                free_slot(slot as u32);
            }
            unsafe { unmap_and_exit(a, b) }
        }
        // everything else: the shared "simple" dispatch (cell-local + delegated).
        _ => {
            let saved_mask = unsafe {
                std::ptr::read_unaligned(std::ptr::addr_of!((*uc).uc_sigmask) as *const u64)
            };
            unsafe { store_slot_snapshot_regs(current_slot(), g, EINTR) };
            dispatch_simple(nr, a, b, c, d, e, f, Some(saved_mask))
        }
    };
    unsafe { update_slot_snapshot_rax(current_slot(), ret) };
    // Rewrite fast-path (opt-in): this trap proved `rip-2` is a real syscall site,
    // so patch it to `callq *%rax` → the trampoline → the fast dispatcher, skipping
    // the SIGSYS trap on every future hit. No-op unless SENTRY_REWRITE is on; never
    // touches control-flow sites (clone/execve/SENTINEL) or the trampoline page.
    maybe_patch_site(g[REG_RIP] as u64, nr);
    g[REG_RAX] = ret;
    // RIP already past the syscall — do not advance.
    // SENTRY_REGDIFF: every reg except RAX must be preserved across the trap. Any other
    // delta = the handler corrupted a guest register (e.g. a callee-saved reg node holds
    // an env-buffer base in) → log nr/rip + each changed reg before→after. Stack buffer +
    // delegate (no malloc); capped so a frequent anomaly can't flood the IPC.
    if let Some(snap) = regdiff_snap {
        let mut changed = 0u32;
        for i in 0..17usize {
            let cur = unsafe { std::ptr::read_volatile(&g[i]) };
            if i != REG_RAX && cur != snap[i] {
                changed |= 1u32 << i;
            }
        }
        if std::hint::black_box(changed) != 0 {
            use std::sync::atomic::{AtomicU32, Ordering};
            static ANOM: AtomicU32 = AtomicU32::new(0);
            if ANOM.fetch_add(1, Ordering::Relaxed) < 48 {
                let mut buf = [0u8; 512];
                let mut p = 0usize;
                macro_rules! puts {
                    ($s:expr) => {{
                        let s: &[u8] = $s;
                        for &b in s {
                            if p < buf.len() {
                                buf[p] = b;
                                p += 1;
                            }
                        }
                    }};
                }
                macro_rules! puthex {
                    ($v:expr) => {{
                        let v = $v as u64;
                        puts!(b"0x");
                        for sh in (0..16u32).rev() {
                            let nib = ((v >> (sh * 4)) & 0xf) as usize;
                            if p < buf.len() {
                                buf[p] = b"0123456789abcdef"[nib];
                                p += 1;
                            }
                        }
                    }};
                }
                puts!(b"REGDIFF nr=");
                puthex!(nr);
                puts!(b" rip=");
                puthex!(g[REG_RIP]);
                puts!(b" changed:");
                for i in 0..17usize {
                    if (changed & (1u32 << i)) != 0 {
                        puts!(b" [");
                        let mut nb = [0u8; 4];
                        let mut k = 4;
                        let mut x = i;
                        loop {
                            k -= 1;
                            nb[k] = b'0' + (x % 10) as u8;
                            x /= 10;
                            if x == 0 {
                                break;
                            }
                        }
                        puts!(&nb[k..]);
                        puts!(b"]");
                        puthex!(snap[i]);
                        puts!(b"->");
                        puthex!(g[i]);
                    }
                }
                puts!(b"\n");
                delegate(CTL_ENVDBG, buf.as_ptr() as u64, p as u64, 0, 0, 0, 0);
            }
        }
    }
}

// Read a NUL-terminated string from the cell's own (guest) memory.
unsafe fn read_cstr(p: u64) -> Vec<u8> {
    let mut v = Vec::new();
    if p == 0 {
        return v;
    }
    let mut q = p as *const u8;
    loop {
        let b = *q;
        if b == 0 || v.len() > 65536 {
            break;
        }
        v.push(b);
        q = q.add(1);
    }
    v
}
// Read a NULL-terminated array of C-string pointers (argv/envp) from guest memory.
unsafe fn read_cstr_array(p: u64) -> Vec<Vec<u8>> {
    let mut out = Vec::new();
    if p == 0 {
        return out;
    }
    let mut q = p as *const u64;
    loop {
        let ptr = *q;
        if ptr == 0 || out.len() > 4096 {
            break;
        }
        out.push(read_cstr(ptr));
        q = q.add(1);
    }
    out
}
/// Read a whole file FROM THE ROOTFS via the supervisor — the cell is sealed, so
/// it can't `open`/`read` directly (those aren't allowlisted). The guest path is
/// confined by the supervisor (`openat2(RESOLVE_IN_ROOT)`). Used by execve to load
/// the new image. (Allocation is fine here — `CELL_FS` was restored first.)
fn delegated_read(guest_path: &[u8]) -> Option<Vec<u8>> {
    let mut cpath = guest_path.to_vec();
    cpath.push(0);
    let fd = delegate(
        SYS_OPENAT,
        (-100i64) as u64,
        cpath.as_ptr() as u64,
        0, /*O_RDONLY*/
        0,
        0,
        0,
    );
    if fd < 0 {
        return None;
    }
    let mut st = [0u8; 144];
    let r = delegate(SYS_FSTAT, fd as u64, st.as_mut_ptr() as u64, 0, 0, 0, 0);
    let size = if r == 0 {
        u64::from_le_bytes(st[48..56].try_into().unwrap()) as usize // st_size @ off 48
    } else {
        0
    };
    let mut buf = vec![0u8; size];
    let mut off = 0usize;
    while off < buf.len() {
        let want = (buf.len() - off).min(4 * 1024 * 1024);
        let n = delegate(
            SYS_PREAD64,
            fd as u64,
            buf[off..].as_mut_ptr() as u64,
            want as u64,
            off as u64,
            0,
            0,
        );
        if n <= 0 {
            break;
        }
        off += n as usize;
    }
    buf.truncate(off);
    delegate(SYS_CLOSE, fd as u64, 0, 0, 0, 0, 0);
    if off == 0 {
        None
    } else {
        Some(buf)
    }
}

/// In-cell `execve` emulation. A raw `execve` would reset SUD and leave the new
/// program seccomp-walled-but-not-interposed, so the cell replaces its own guest
/// image in place — keeping SUD, the SIGSYS handler, and the seccomp filter — and
/// redirects `g` (the trapped context) to the new program's entry. On error it
/// sets `g[RAX] = -errno` and returns (the guest sees a failed execve).
fn passwd_entry(spec: &str) -> Option<(u32, u32, String)> {
    let wanted_uid = spec.parse::<u32>().ok();
    let bytes = delegated_read(b"/etc/passwd")?;
    for line in String::from_utf8_lossy(&bytes).lines() {
        let fields: Vec<&str> = line.split(':').collect();
        if fields.len() < 7 {
            continue;
        }
        let uid = fields[2].parse::<u32>().ok()?;
        let gid = fields[3].parse::<u32>().ok()?;
        if fields[0] == spec || wanted_uid == Some(uid) {
            return Some((uid, gid, fields[5].to_owned()));
        }
    }
    None
}

fn group_id(spec: &str) -> Option<u32> {
    if let Ok(gid) = spec.parse::<u32>() {
        return Some(gid);
    }
    let bytes = delegated_read(b"/etc/group")?;
    String::from_utf8_lossy(&bytes).lines().find_map(|line| {
        let mut fields = line.split(':');
        let name = fields.next()?;
        fields.next()?;
        let gid = fields.next()?.parse().ok()?;
        (name == spec).then_some(gid)
    })
}

fn execvp_path(command: &str, envp: &[Vec<u8>]) -> String {
    if command.contains('/') {
        return command.to_owned();
    }
    let path = envp
        .iter()
        .find_map(|v| v.strip_prefix(b"PATH="))
        .map(String::from_utf8_lossy)
        .unwrap_or_else(|| "/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin".into());
    for dir in path.split(':') {
        let candidate = format!("{dir}/{command}");
        if delegated_read(candidate.as_bytes()).is_some() {
            return candidate;
        }
    }
    command.to_owned()
}

fn emulate_execve(g: &mut [libc::greg_t], path_ptr: u64, argv_ptr: u64, envp_ptr: u64) {
    // Restore the cell's own TLS so std (alloc, fs) is sound — the handler runs
    // with the guest's `%fs` otherwise. The new program re-sets `%fs` in its crt.
    let mut old_guest_fs = 0u64;
    unsafe {
        host(
            SYS_ARCH_PRCTL,
            ARCH_GET_FS,
            std::ptr::addr_of_mut!(old_guest_fs) as u64,
            0,
            0,
            0,
            0,
        );
        host(SYS_ARCH_PRCTL, ARCH_SET_FS, CELL_FS, 0, 0, 0, 0);
    }
    macro_rules! execve_fail {
        ($errno:expr) => {{
            unsafe { host(SYS_ARCH_PRCTL, ARCH_SET_FS, old_guest_fs, 0, 0, 0, 0) };
            g[REG_RAX] = $errno;
            return;
        }};
    }

    // Capture path/argv/envp BEFORE we tear the old image down.
    let (path, argv, mut envp) = unsafe {
        (
            read_cstr(path_ptr),
            read_cstr_array(argv_ptr),
            read_cstr_array(envp_ptr),
        )
    };
    // Do not repair env entries here. `execve(2)` must consume exactly the
    // caller-provided envp bytes; rewriting prefix values from a previous shadow
    // would turn legitimate shorter values into older longer ones.
    // DBPOS/DBRAW: per-exec DATABASE_URL index+length (parent layout) + a raw hex dump of
    // the entry's guest bytes PAST the NUL when truncated, to see exactly how the cut happens.
    if ENVDBG.load(Ordering::Relaxed) {
        let mut dbidx: i64 = -1;
        let mut dblen: i64 = 0;
        for (i, e) in envp.iter().enumerate() {
            if e.starts_with(b"DATABASE_URL=") {
                dbidx = i as i64;
                dblen = e.len().saturating_sub(b"DATABASE_URL=".len()) as i64;
                break;
            }
        }
        if dbidx >= 0 {
            let a0 = argv.first().map(|a| a.as_slice()).unwrap_or(b"?");
            let mut m: Vec<u8> = Vec::with_capacity(220);
            m.extend_from_slice(b"DBPOS a0=");
            m.extend_from_slice(&a0[..a0.len().min(40)]);
            m.extend_from_slice(b" idx=");
            let mut nb = [0u8; 24];
            let i = fmt_i64(dbidx, &mut nb);
            m.extend_from_slice(&nb[i..]);
            m.extend_from_slice(b" len=");
            let mut nb = [0u8; 24];
            let i = fmt_i64(dblen, &mut nb);
            m.extend_from_slice(&nb[i..]);
            m.extend_from_slice(b" total=");
            let mut nb = [0u8; 24];
            let i = fmt_i64(envp.len() as i64, &mut nb);
            m.extend_from_slice(&nb[i..]);
            m.push(b'\n');
            delegate(CTL_ENVDBG, m.as_ptr() as u64, m.len() as u64, 0, 0, 0, 0);
            if dblen < 70 {
                // CRIME-SCENE CAPTURE: this process just built a truncated env block. Ask
                // the supervisor to gcore us (we'll block in the delegate → consistent
                // snapshot). One-shot per cell (+ supervisor caps the total) so we grab the
                // chain ORIGIN (npx→prisma: own process.env still 108, built block 59), not
                // every downstream inheritor.
                {
                    use std::sync::atomic::{AtomicBool, Ordering};
                    static DUMPED: AtomicBool = AtomicBool::new(false);
                    if false && !DUMPED.swap(true, Ordering::Relaxed) {
                        delegate(CTL_COREDUMP, 0, 0, 0, 0, 0, 0);
                    }
                }
                unsafe {
                    let entry_ptr = *((envp_ptr as *const u64).add(dbidx as usize));
                    if entry_ptr != 0 {
                        let raw = std::slice::from_raw_parts(entry_ptr as *const u8, 90);
                        let mut hx: Vec<u8> = Vec::with_capacity(300);
                        const H: &[u8; 16] = b"0123456789abcdef";
                        hx.extend_from_slice(b"DBRAW ptr=");
                        for s in (0..16u32).rev() {
                            hx.push(H[((entry_ptr >> (s * 4)) & 0xf) as usize]);
                        }
                        hx.extend_from_slice(b" hbase=");
                        for s in (0..16u32).rev() {
                            hx.push(H[((HEAP_BASE >> (s * 4)) & 0xf) as usize]);
                        }
                        hx.extend_from_slice(b" hend=");
                        for s in (0..16u32).rev() {
                            hx.push(H[((HEAP_END >> (s * 4)) & 0xf) as usize]);
                        }
                        hx.extend_from_slice(b" bytes=");
                        for &b in raw {
                            hx.push(H[(b >> 4) as usize]);
                            hx.push(H[(b & 0xf) as usize]);
                        }
                        hx.push(b'\n');
                        delegate(CTL_ENVDBG, hx.as_ptr() as u64, hx.len() as u64, 0, 0, 0, 0);
                    }
                }
            }
        }
    }
    // SENTRY_ENVDBG: at every execve, log argv0 + the DATABASE_URL the new image is
    // handed (length + full value) straight to stderr via a wall-allowlisted host WRITE
    // (openat/ipc_logf is NOT allowlisted in the sealed cell). Pins which exec layer in
    // a deep chain (sh→npx→node→schema-engine) truncates a long DATABASE_URL — the
    // shopify-test Prisma "table public.Session does not exist" (db-name cut to 26 chars).
    if ENVDBG.load(Ordering::Relaxed) {
        let dbs: Vec<&Vec<u8>> = envp
            .iter()
            .filter(|v| v.starts_with(b"DATABASE_URL="))
            .collect();
        if !dbs.is_empty() {
            let a0 = argv.first().map(|a| a.as_slice()).unwrap_or(b"?");
            let mut msg: Vec<u8> = Vec::with_capacity(400);
            msg.extend_from_slice(b"ENVDBG2 a0=");
            msg.extend_from_slice(&a0[..a0.len().min(40)]);
            msg.extend_from_slice(b" count=");
            let mut nb = [0u8; 24];
            let i = fmt_i64(dbs.len() as i64, &mut nb);
            msg.extend_from_slice(&nb[i..]);
            // log EACH DATABASE_URL entry's value length — a dup (59 then 108) means
            // node appended the override instead of replacing → getenv returns the short one.
            for du in &dbs {
                let val = &du[b"DATABASE_URL=".len()..];
                msg.extend_from_slice(b" len=");
                let mut nb = [0u8; 24];
                let i = fmt_i64(val.len() as i64, &mut nb);
                msg.extend_from_slice(&nb[i..]);
            }
            msg.push(b'\n');
            // Forward to the supervisor (it can openat/ipc_logf to box-readable
            // /tmp/sentry_ipc.log; the sealed cell cannot openat). Synchronous — msg alive.
            delegate(
                CTL_ENVDBG,
                msg.as_ptr() as u64,
                msg.len() as u64,
                0,
                0,
                0,
                0,
            );
            // When DATABASE_URL is TRUNCATED, dump the whole env layout (idx, name, value
            // length) so we can spot the trigger var (a malformed/odd-length entry adjacent
            // to DATABASE_URL, or DATABASE_URL's byte-position in the block).
            let truncated = dbs
                .iter()
                .any(|d| d.len().saturating_sub(b"DATABASE_URL=".len()) < 70);
            if truncated {
                let mut m: Vec<u8> = Vec::with_capacity(7800);
                m.extend_from_slice(b"ENVFULL total=");
                let mut nb = [0u8; 24];
                let i = fmt_i64(envp.len() as i64, &mut nb);
                m.extend_from_slice(&nb[i..]);
                for (idx, e) in envp.iter().enumerate() {
                    let eq = e.iter().position(|&c| c == b'=').unwrap_or(e.len());
                    m.push(b' ');
                    let mut nb = [0u8; 24];
                    let i = fmt_i64(idx as i64, &mut nb);
                    m.extend_from_slice(&nb[i..]);
                    m.push(b':');
                    m.extend_from_slice(&e[..eq.min(22)]);
                    m.push(b'=');
                    let vlen = e.len().saturating_sub(eq + 1);
                    let mut nb = [0u8; 24];
                    let i = fmt_i64(vlen as i64, &mut nb);
                    m.extend_from_slice(&nb[i..]);
                    if m.len() > 7600 {
                        break;
                    }
                }
                m.push(b'\n');
                delegate(CTL_ENVDBG, m.as_ptr() as u64, m.len() as u64, 0, 0, 0, 0);
                // ENVRAW: dump the FULL env (KEY=VALUE, 0x01-separated) ONCE — so a faithful
                // minimal repro can replay the EXACT keys + lengths + order on native vs
                // sentry (the truncation is env-size/layout-correlated, so exact key lengths
                // matter; the per-line ENVFULL above cuts keys at 22 chars).
                {
                    use std::sync::atomic::{AtomicBool, Ordering};
                    static RAW_DONE: AtomicBool = AtomicBool::new(false);
                    if !RAW_DONE.swap(true, Ordering::Relaxed) {
                        let mut r: Vec<u8> = Vec::with_capacity(8192);
                        r.extend_from_slice(b"ENVRAW ");
                        for e in envp.iter() {
                            if r.len() + e.len() + 1 > 8000 {
                                break;
                            }
                            r.extend_from_slice(e);
                            r.push(0x01);
                        }
                        r.push(b'\n');
                        delegate(CTL_ENVDBG, r.as_ptr() as u64, r.len() as u64, 0, 0, 0, 0);
                    }
                }
            }
        }
    }

    // Resolve `#!` scripts to their interpreter (binfmt_script), rebuilding argv, so an
    // exec'd entrypoint/wrapper script (a Docker `/docker-entrypoint.sh`, a shell/python
    // wrapper) RUNS instead of returning -ENOEXEC. Reads delegate to the supervisor (the
    // cell is sealed). The shared resolver is UTF-8 (exec paths/argv are ASCII in practice).
    let mut path_s = String::from_utf8_lossy(&path).into_owned();
    let mut argv_s: Vec<String> = argv
        .iter()
        .map(|a| String::from_utf8_lossy(a).into_owned())
        .collect();
    // su-exec/gosu perform only identity setup followed by execvp. Running their
    // musl setxid rendezvous inside sentry deadlocks against sentry-owned helper
    // threads that share the cell process. Apply the equivalent virtual-credential
    // transition here, then feed the target back into the normal exec loader.
    if matches!(path_s.rsplit('/').next(), Some("su-exec" | "gosu")) && argv_s.len() >= 3 {
        let mut parts = argv_s[1].splitn(2, ':');
        let user = parts.next().unwrap_or_default();
        let explicit_group = parts.next();
        let entry = passwd_entry(user);
        let uid = user
            .parse::<u32>()
            .ok()
            .or_else(|| entry.as_ref().map(|e| e.0));
        let gid = explicit_group
            .and_then(group_id)
            .or_else(|| entry.as_ref().map(|e| e.1));
        let (Some(uid), Some(gid)) = (uid, gid) else {
            execve_fail!(-2); // -ENOENT: unknown user/group, matching failed lookup
        };
        if cred_setgid(gid) < 0 || cred_setuid(uid) < 0 {
            execve_fail!(-1); // -EPERM
        }
        if let Some((_, _, home)) = entry {
            envp.retain(|v| !v.starts_with(b"HOME="));
            envp.push(format!("HOME={home}").into_bytes());
        }
        argv_s = argv_s.split_off(2);
        path_s = execvp_path(&argv_s[0], &envp);
    }
    // `/proc/self/exe` must resolve to the cell's CURRENT executable, which changes
    // on every in-cell execve. `guest_exe()` is seeded by the supervisor at cell
    // spawn (e.g. `/bin/sh`) and is otherwise stale after an exec — so a program that
    // re-execs `/proc/self/exe` to launch helpers (Chromium spawns its renderer/gpu/
    // utility children EXACTLY this way) would resolve to the original exe and run the
    // wrong binary (busybox sh choking on `--type=...` → the children never start →
    // the browser hangs on IPC). Substitute the tracked exe for the load, then update
    // it below to whatever we actually run. The cell keeps its own `guest_exe()` copy
    // (CoW-forked); `delegated_read` then reads the real path, never `/proc/self/exe`.
    let effective = if path_s == "/proc/self/exe" {
        String::from_utf8_lossy(&guest_exe().lock().unwrap()).into_owned()
    } else {
        path_s.clone()
    };
    let log_proc_self_exe_exec_fail = |stage: &[u8], errno: i64| {
        if path_s != "/proc/self/exe" {
            return;
        }
        let mut line = Vec::new();
        line.extend_from_slice(b"errno=");
        line.extend_from_slice(errno.to_string().as_bytes());
        line.extend_from_slice(b" stage=");
        line.extend_from_slice(stage);
        line.extend_from_slice(b" effective=");
        line.extend_from_slice(effective.as_bytes());
        line.extend_from_slice(b" argv0=");
        if let Some(a0) = argv_s.first() {
            line.extend_from_slice(a0.as_bytes());
        }
        delegate(
            CTL_PROCEXE_EXEC_FAIL,
            line.as_ptr() as u64,
            line.len() as u64,
            0,
            0,
            0,
            0,
        );
    };
    let (path, bytes, argv) =
        match resolve_shebang(&effective, &argv_s, |p| delegated_read(p.as_bytes())) {
            Some((p, b, a)) => (
                p.into_bytes(),
                b,
                a.into_iter()
                    .map(String::into_bytes)
                    .collect::<Vec<Vec<u8>>>(),
            ),
            None => {
                log_proc_self_exe_exec_fail(b"resolve", -2);
                execve_fail!(-2); // -ENOENT
            }
        };
    if bytes.len() < 64 || &bytes[0..4] != b"\x7fELF" || bytes[4] != 2 {
        log_proc_self_exe_exec_fail(b"elf", -8);
        execve_fail!(-8); // -ENOEXEC (not a script, not an ELF)
    }
    // Commit: track the program we are about to run as the cell's `/proc/self/exe`
    // (the resolved interpreter for a `#!` script, like the real kernel). Updates the
    // cell's own copy, used by the substitution above on the NEXT in-cell execve.
    *guest_exe().lock().unwrap() = path.clone();
    // Forward the new exe to the SUPERVISOR so /proc/self/exe (served supervisor-
    // side) reflects this cell's actual binary, not the spawn-time `/bin/sh`.
    // `path` stays alive across this synchronous delegate.
    delegate(
        CTL_SET_EXE,
        path.as_ptr() as u64,
        path.len() as u64,
        0,
        0,
        0,
        0,
    );
    // Forward the NUL-separated guest argv so the supervisor serves the real
    // command at /proc/<pid>/cmdline (read by in-guest `pgrep -f`/`ps`), instead
    // of the sentry binary's own argv. This is the procfs cmdline wire format:
    // arg0\0arg1\0…\0 (trailing NUL on the last arg). Stays alive across the
    // synchronous delegate.
    {
        let mut joined: Vec<u8> = Vec::with_capacity(256);
        for a in &argv {
            joined.extend_from_slice(a);
            joined.push(0);
            if joined.len() >= 4096 {
                break;
            }
        }
        delegate(
            CTL_SET_CMDLINE,
            joined.as_ptr() as u64,
            joined.len() as u64,
            0,
            0,
            0,
            0,
        );
    }
    delegate(CTL_CLOSE_CLOEXEC, 0, 0, 0, 0, 0, 0);
    notify_vfork_release();
    delegate(CTL_SHM_FLUSH, 0, u64::MAX, 2, 0, 0, 0);

    // de_thread: a real execve from a multi-threaded process kills every sibling thread
    // so the new image owns the address space alone. This is kernel bookkeeping, not a
    // guest syscall: the sealed cell must not open /proc or send tgkill itself. Delegate
    // it to the supervisor, which can enumerate /proc/<tgid>/task, signal siblings with
    // SIG_DETHREAD, and reclaim only their ring slots while preserving the process fd
    // table. Must run BEFORE the SIG_DFL reset below, which would un-install the
    // SIG_DETHREAD handler the teardown relies on.
    let de = delegate(
        CTL_DETHREAD_FOR_EXEC,
        current_slot(),
        unsafe { host(SYS_GETTID, 0, 0, 0, 0, 0, 0) as u64 },
        unsafe { host(SYS_GETPID, 0, 0, 0, 0, 0, 0) as u64 },
        0,
        0,
        0,
    );
    if de < 0 {
        execve_fail!(de);
    }
    MM_USERS.store(1, Ordering::Release);

    // execve(2) RESETS every caught signal to SIG_DFL. We emulate execve in-place
    // and never call the real syscall, so we must replicate that — otherwise the
    // guest's old signal handlers, whose code lives in the image we abandon/overlay
    // below, stay installed. A signal delivered AFTER the exec (e.g. SIGCHLD/SIGALRM
    // during a `timeout`-wrapped fork storm) then has the kernel jump to a handler
    // address in the torn-down old image — an unmapped RIP → SIGSEGV, killing the
    // cell (exit 139). Reset signals to SIG_DFL EXCEPT: SIGSYS (31), OUR SUD/seccomp
    // wall trap (cell_layer1) which MUST survive the exec; the musl-internal realtime
    // signals 32/33 (SIGTIMER/SIGCANCEL, SIGSETXID), which musl's own runtime owns and
    // re-arms — clobbering them races its thread/setxid machinery in the freshly-exec'd
    // program (proven by integration_sentry's sentry_fork_wait_timeout_no_slot_leak,
    // which 139'd before this reset); and SIG_DETHREAD (64), sentry's de_thread teardown
    // signal, which must stay armed for the new image's own future execs.
    unsafe {
        reset_exec_signal_dispositions();
    }

    // Reset the guest's mmap arena + program break for the new image — but ONLY when
    // this task is the sole user of its address space (MM_USERS == 1). We do NOT munmap
    // the old image: a spawn child's stack is itself low (in the arena), and the handler
    // is currently running on it — load_elf uses MAP_FIXED to overlay the new exe/interp
    // in place, and ld.so re-maps libs into the (reset) arena. The old low mappings are
    // abandoned (the short-lived child exits soon).
    //
    // When the address space is SHARED with live siblings (a multi-threaded process
    // exec'ing — e.g. a rustup/tokio shim exec'ing the real rustc — or a CLONE_VM
    // posix_spawn child exec'ing while the parent's codegen threads run), rewinding to
    // ARENA_TOP would re-hand the high arena to the new image's ld.so and MAP_FIXED a
    // library over a live sibling's stack → that sibling crashes. A real execve kills
    // those siblings (de_thread); we cannot, so we leave the cursor where it is and let
    // the new image's libs allocate strictly BELOW the live siblings' mappings. The old
    // image's high arena is not reclaimed (it stays live for the siblings) — acceptable;
    // the process is being replaced and the cursor still has the whole low arena to grow
    // into. See MM_USERS.
    if MM_USERS.load(std::sync::atomic::Ordering::Acquire) <= 1 {
        ARENA_CUR.store(ARENA_TOP, std::sync::atomic::Ordering::Release);
    }
    let heap_reset = reset_brk_heap_for_exec();
    if heap_reset < 0 {
        execve_fail!(heap_reset);
    }

    // Load the new image (+ its interpreter), exactly like the initial load.
    let exe_base = if rd_u16(&bytes, 16) == 3 {
        EXE_DYN_BASE
    } else {
        0
    };
    let exe = load_elf(&bytes, exe_base);
    let (jump_to, interp_base) = if let Some(ref ip) = exe.interp {
        let ib = match delegated_read(ip) {
            Some(b) => b,
            None => {
                execve_fail!(-8);
            }
        };
        let interp = load_elf(&ib, INTERP_BASE);
        (interp.entry, Some(INTERP_BASE))
    } else {
        (exe.entry, None)
    };
    unregister_current_rseq_for_exec();

    let argvv: Vec<Vec<u8>> = if argv.is_empty() {
        vec![path.clone()]
    } else {
        argv
    };
    let envpv: Vec<Vec<u8>> = if envp.is_empty() {
        vec![b"PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin".to_vec()]
    } else {
        envp
    };
    reset_guest_sigaltstack(current_slot());
    let sp = build_stack(&exe, interp_base, &argvv, &envpv);

    // Redirect the trapped context into the new program. SUD/handler/seccomp are
    // unchanged (we never called real execve); sigreturn enters the new entry.
    g[REG_RIP] = jump_to as libc::greg_t;
    g[REG_RSP] = sp as libc::greg_t;
    g[REG_RDX] = 0;
    g[REG_RBP] = 0;
}

// ─── SUPERVISOR (Layer 2): service delegated syscalls in ITS address space ───

fn iov(base: u64, len: usize) -> [u64; 2] {
    [base, len as u64]
}
// ─── INV-1: forbidden-zone check on every cell-supplied transfer VA ──────────
//
// The ring TABLE (RINGS + SLOT_BM + ZYG + SLOT_STACKS) is ONE MAP_SHARED|ANON
// mapping (setup_sandbox_env, null mmap hint) placed in the HIGH mmap region —
// INSIDE every cell's nameable VA and inherited across fork. A hostile cell could
// name a syscall buffer VA that lands ON that mapping; the supervisor's own
// process_vm_writev would then write attacker bytes into ANOTHER cell's in-flight
// ring response / the slot allocator (a cross-cell escape). We record the mapping's
// exact [base, base+table_bytes) extent at setup (supervisor-PRIVATE statics, written
// once pre-fork, inherited by value) and reject any cell-supplied (va,len) that
// touches it. CRITICAL: this is the EXACT recorded extent, NOT a `>= WINDOW_FLOOR`
// floor — the guest STACK is also mmap(NULL) and lands in the SAME high region, so
// stack-resident syscall buffers are legitimately above the floor; a floor check
// would EFAULT all of them. (lo,hi)==(0,0) before setup ⇒ overlap test disabled.
static SHARED_ZONE_LO: std::sync::atomic::AtomicU64 = std::sync::atomic::AtomicU64::new(0);
static SHARED_ZONE_HI: std::sync::atomic::AtomicU64 = std::sync::atomic::AtomicU64::new(0);
fn shared_zone() -> (u64, u64) {
    (
        SHARED_ZONE_LO.load(Ordering::Relaxed),
        SHARED_ZONE_HI.load(Ordering::Relaxed),
    )
}
/// True iff a cell-supplied remote buffer [va, va+len) is safe for the supervisor to
/// process_vm_readv/writev: non-NULL, no wrap, within the guest user range, and NOT
/// overlapping the shared ring zone. The SINGLE bounds gate for every transfer
/// (vm_read/vm_write are the only callers of process_vm_*).
fn vm_arg_ok(va: u64, len: u64) -> bool {
    if va == 0 || va >= USER_TOP {
        return false;
    }
    let end = match va.checked_add(len) {
        Some(e) if e <= USER_TOP => e,
        _ => return false, // overflow or runs past the user range
    };
    let (lo, hi) = shared_zone();
    // Half-open overlap of [va, end) with [lo, hi). (lo,hi)==(0,0) ⇒ no zone yet.
    if hi > lo && va < hi && end > lo {
        return false;
    }
    true
}
/// Reject a process_vm_* TARGET pid the cell must never reach: the supervisor's OWN
/// process (a forged `ring.pid` == our pid would let a cell drive our privileged
/// process_vm at the SUPERVISOR's address space — reading its secrets / corrupting
/// its state, the worst breach when the cell is NOT uid-dropped) or pid <= 1.
/// In a servicer thread, cache the supervisor's own pid so the process_vm hot path does
/// not issue getpid() for every bounced read/write. Outside a servicer thread, keep the
/// fresh getpid(): a cached value is inherited across cell forks and would be wrong in
/// any accidental forked-cell context.
fn vm_self_pid_for_target_check() -> i32 {
    let sr = SERVICER_RING.with(|cell| cell.get());
    if sr.is_null() {
        return unsafe { host(SYS_GETPID, 0, 0, 0, 0, 0, 0) } as i32;
    }
    let cached = SUPERVISOR_SELF_PID.load(Ordering::Relaxed);
    if cached > 1 {
        return cached;
    }
    let pid = unsafe { host(SYS_GETPID, 0, 0, 0, 0, 0, 0) } as i32;
    if pid > 1 {
        SUPERVISOR_SELF_PID.store(pid, Ordering::Relaxed);
    }
    pid
}

/// Reject a process_vm_* TARGET pid the cell must never reach: the supervisor's OWN
/// process (a forged `ring.pid` == our pid would let a cell drive our privileged
/// process_vm at the SUPERVISOR's address space — reading its secrets / corrupting
/// its state, the worst breach when the cell is NOT uid-dropped) or pid <= 1.
/// Multi-tenant isolation across tenants additionally rests on per-tenant uid-drop
/// (the kernel denies cross-uid process_vm) + netns; this gate is the in-process
/// self-protection.
fn vm_target_ok(pid: i32) -> bool {
    pid > 1 && pid != vm_self_pid_for_target_check()
}
/// Read `buf.len()` bytes of the cell's memory at `remote`. process_vm_readv may
/// transfer less than requested (page-cache / iov limits), so loop to completion.
fn vm_read(pid: i32, remote: u64, buf: &mut [u8]) -> i64 {
    if !vm_target_ok(pid) {
        return -14; // -EFAULT: forged owner — our own / invalid target pid
    }
    if !vm_arg_ok(remote, buf.len() as u64) {
        return -14; // -EFAULT: NULL / wrapping / out-of-range / shared-zone VA
    }
    let total = buf.len();
    let base = buf.as_mut_ptr() as u64;
    let mut done = 0usize;
    while done < total {
        let l = iov(base + done as u64, total - done);
        let r = iov(remote + done as u64, total - done);
        let n = unsafe {
            host(
                SYS_PROCESS_VM_READV,
                pid as u64,
                l.as_ptr() as u64,
                1,
                r.as_ptr() as u64,
                1,
                0,
            )
        };
        if n <= 0 {
            return if done == 0 { n } else { done as i64 };
        }
        done += n as usize;
    }
    done as i64
}
fn vm_write(pid: i32, remote: u64, buf: &[u8]) -> i64 {
    if !vm_target_ok(pid) {
        return -14; // -EFAULT: forged owner — our own / invalid target pid
    }
    // PID-REUSE GUARD (reentrant_under_load corruption fix). A servicer captures the slot's
    // owner `pid` once at request time, then runs a possibly-blocking service(). If that cell
    // is REAPED mid-call (free_slot → clear_ring_owner sets ring.pid=0) and the slot is reused
    // by a fork child (which may RECYCLE the dead pid), writing the now-stale result with
    // process_vm_writev(stale_pid, …) lands in the NEW tenant's memory — a single-byte stray
    // write that corrupts e.g. a fork child's env buffer (the DATABASE_URL 108→59 truncation).
    // The cancel_efd closes most of this, but the "syscall completed AND reap fired together"
    // race slips a write through. So: on a servicer thread, if our slot's CURRENT owner pid no
    // longer equals the pid we're about to write to, the request is stale → drop the write.
    {
        let sr = SERVICER_RING.with(|c| c.get());
        if !sr.is_null() {
            let cur = unsafe { std::ptr::read_volatile(std::ptr::addr_of!((*sr).pid)) };
            if cur != pid {
                return -14; // -EFAULT: slot reaped/reused since the request — stale target
            }
        }
    }
    if !vm_arg_ok(remote, buf.len() as u64) {
        return -14; // -EFAULT: NULL / wrapping / out-of-range / shared-zone VA
    }
    // DIAGNOSTIC (DATABASE_URL truncation): log any SMALL cross-process write that lands in the
    // high stack/brk range where node builds the spawn env. If a 1-byte NUL write to ~0x7fffb3…
    // correlates with an ENVSCAN_HIT, the stray is a SUPERVISOR vm_write and `nr` names the
    // syscall whose servicing strayed. Cheap range gate keeps the hot path clean.
    if ENVDBG.load(Ordering::Relaxed)
        && buf.len() <= 64
        && remote < 0x7fff_b800_0000
        && remote + buf.len() as u64 > 0x7fff_b000_0000
    {
        let nr = {
            let sr = SERVICER_RING.with(|c| c.get());
            if sr.is_null() {
                -1i64
            } else {
                unsafe { std::ptr::read_volatile(std::ptr::addr_of!((*sr).nr)) }
            }
        };
        let zeros = buf.iter().filter(|&&b| b == 0).count();
        ipc_logf_raw(
            &[
                b"VMWRITE_ENVRGN pid=".as_slice(),
                pid.to_string().as_bytes(),
                b" remote=0x".as_slice(),
                format!("{remote:x}").as_bytes(),
                b" len=".as_slice(),
                buf.len().to_string().as_bytes(),
                b" zeros=".as_slice(),
                zeros.to_string().as_bytes(),
                b" nr=".as_slice(),
                nr.to_string().as_bytes(),
            ]
            .concat(),
        );
    }
    // DISCRIMINATOR (DATABASE_URL truncation): does the SUPERVISOR ever vm_write a SMALL value
    // into V8's HEAP (the arena, < WINDOW_FLOOR 0x20_0000_0000)? The corrupted object (dbConn's
    // ConsString length field, →0x3b) lives there. If a small write lands here — especially one
    // whose bytes contain 0x3b — it is the stray, and `nr` names the syscall whose servicing
    // strays. If NOTHING lands here while len59 persists, the corruption is purely in-node (a V8
    // bug sentry's timing provokes) and the supervisor is exonerated.
    if ENVDBG.load(Ordering::Relaxed)
        && buf.len() <= 32
        && remote >= 0x41_00_0000
        && remote < 0x20_0000_0000
    {
        let nr = {
            let sr = SERVICER_RING.with(|c| c.get());
            if sr.is_null() {
                -1i64
            } else {
                unsafe { std::ptr::read_volatile(std::ptr::addr_of!((*sr).nr)) }
            }
        };
        let has3b = buf.contains(&0x3b);
        let mut m: Vec<u8> = Vec::with_capacity(96);
        m.extend_from_slice(b"VMWRITE_ARENA pid=");
        m.extend_from_slice(pid.to_string().as_bytes());
        m.extend_from_slice(b" remote=0x");
        m.extend_from_slice(format!("{remote:x}").as_bytes());
        m.extend_from_slice(b" len=");
        m.extend_from_slice(buf.len().to_string().as_bytes());
        m.extend_from_slice(b" nr=");
        m.extend_from_slice(nr.to_string().as_bytes());
        m.extend_from_slice(if has3b {
            b" has3b=1 b="
        } else {
            b" has3b=0 b="
        });
        const HX: &[u8; 16] = b"0123456789abcdef";
        for &b in buf.iter().take(16) {
            m.push(HX[(b >> 4) as usize]);
            m.push(HX[(b & 0xf) as usize]);
        }
        ipc_logf_raw(&m);
    }
    let total = buf.len();
    let base = buf.as_ptr() as u64;
    let mut done = 0usize;
    while done < total {
        let l = iov(base + done as u64, total - done);
        let r = iov(remote + done as u64, total - done);
        let n = unsafe {
            host(
                SYS_PROCESS_VM_WRITEV,
                pid as u64,
                l.as_ptr() as u64,
                1,
                r.as_ptr() as u64,
                1,
                0,
            )
        };
        if n <= 0 {
            return if done == 0 { n } else { done as i64 };
        }
        done += n as usize;
    }
    done as i64
}

/// Pull a NUL-terminated path from the cell (returns a host C string with NUL).
fn pull_path(pid: i32, remote: u64) -> Vec<u8> {
    let mut out = Vec::new();
    let mut off = 0u64;
    loop {
        let mut chunk = [0u8; 256];
        let n = vm_read(pid, remote + off, &mut chunk);
        if n <= 0 {
            break;
        }
        for &byte in &chunk[..n as usize] {
            if byte == 0 {
                out.push(0);
                return out;
            }
            out.push(byte);
        }
        off += n as u64;
        if out.len() > 4096 {
            break;
        }
    }
    out.push(0);
    out
}
const CAP: u64 = 64 * 1024 * 1024;

// ─── per-process guest fd tables (gVisor-style fd virtualization) ────────────
//
// Every cell process (main + each fork child) delegates to the SAME supervisor,
// which has ONE host fd table — so without indirection a child's `dup2`/`pipe`/
// `close` would clobber its parent's (and the supervisor's own) fds, and Unix
// fd semantics (each process owns its table, copied on fork) would be wrong.
// The supervisor therefore keeps a per-pid table mapping GUEST fd → host fd:
//   * fd-CREATING syscalls (open/pipe/socket/dup/…) allocate the lowest free
//     guest fd in the caller's table and return THAT;
//   * fd-TAKING syscalls translate guest → host first (see `service`'s prelude);
//   * on fork, the parent stamps its pid into the child's ring slot
//     (`fork_parent`) BEFORE forking, and the child's servicer clones the
//     parent's table — dup'ing each host fd, so the file DESCRIPTION (offset,
//     flags) is shared while the tables stay independent: exact fork semantics;
//   * on process death (`CTL_REAP`) the table's host fds are closed — which is
//     what delivers a pipe reader its EOF.
/// What a guest fd resolves to (C1b, native-execution VM): a real host fd (files,
/// pipes, egress sockets — the default for everything today) OR an owned loopback
/// socket handled entirely by the in-process `netstack::LoopNet` (no host fd, no
/// host syscall). The vast majority of fds are `Host`; a fd becomes `Loop` only
/// when `bind`/`connect` targets a loopback address.
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
#[allow(dead_code)] // FdVal::Loop is constructed by the C1b-2 loopback routing
enum FdVal {
    Host(i32),
    Loop(netstack::SockId),
}
fn fdt() -> &'static Mutex<HashMap<i32, BTreeMap<i32, FdVal>>> {
    static T: OnceLock<Mutex<HashMap<i32, BTreeMap<i32, FdVal>>>> = OnceLock::new();
    T.get_or_init(|| Mutex::new(HashMap::new()))
}
fn fd_desc_flags() -> &'static Mutex<HashMap<(i32, i32), i32>> {
    static T: OnceLock<Mutex<HashMap<(i32, i32), i32>>> = OnceLock::new();
    T.get_or_init(|| Mutex::new(HashMap::new()))
}
fn guest_nofile_limits() -> &'static Mutex<HashMap<i32, (u64, u64)>> {
    static T: OnceLock<Mutex<HashMap<i32, (u64, u64)>>> = OnceLock::new();
    T.get_or_init(|| Mutex::new(HashMap::new()))
}
fn pending_nofile_limits() -> &'static Mutex<HashMap<u64, (u64, u64)>> {
    static T: OnceLock<Mutex<HashMap<u64, (u64, u64)>>> = OnceLock::new();
    T.get_or_init(|| Mutex::new(HashMap::new()))
}
fn guest_nofile_limit(pid: i32) -> (u64, u64) {
    guest_nofile_limits()
        .lock()
        .unwrap()
        .get(&pid)
        .copied()
        .unwrap_or_else(|| {
            (
                GUEST_NOFILE_DEFAULT_CUR.load(Ordering::Relaxed),
                GUEST_NOFILE_DEFAULT_MAX.load(Ordering::Relaxed),
            )
        })
}
fn set_guest_nofile_limit(pid: i32, cur: u64, max: u64) {
    guest_nofile_limits()
        .lock()
        .unwrap()
        .insert(pid, (cur, max));
}
fn nice_values() -> &'static Mutex<HashMap<i32, i32>> {
    static T: OnceLock<Mutex<HashMap<i32, i32>>> = OnceLock::new();
    T.get_or_init(|| Mutex::new(HashMap::new()))
}
fn host_fd_flags(hfd: i32) -> i32 {
    let flags = unsafe { host(SYS_FCNTL, hfd as u64, F_GETFD, 0, 0, 0, 0) };
    if flags >= 0 {
        flags as i32
    } else {
        0
    }
}
fn fd_default_desc_flags(v: FdVal) -> i32 {
    match v {
        FdVal::Host(h) => host_fd_flags(h),
        FdVal::Loop(_) => 0,
    }
}
fn fd_get_desc_flags(pid: i32, g: i32, v: FdVal) -> i32 {
    fd_desc_flags()
        .lock()
        .unwrap()
        .get(&(pid, g))
        .copied()
        .unwrap_or_else(|| fd_default_desc_flags(v))
}
fn fd_set_desc_flags(pid: i32, g: i32, flags: i32) {
    let v = fdt()
        .lock()
        .unwrap()
        .get(&pid)
        .and_then(|m| m.get(&g))
        .copied();
    if let Some(FdVal::Host(h)) = v {
        unsafe {
            host(SYS_FCNTL, h as u64, F_SETFD, flags as u64, 0, 0, 0);
        }
    }
    fd_desc_flags().lock().unwrap().insert((pid, g), flags);
}
fn fd_guest_for_host(pid: i32, hfd: i32) -> Option<i32> {
    fdt().lock().unwrap().get(&pid)?.iter().find_map(|(&g, v)| {
        if *v == FdVal::Host(hfd) {
            Some(g)
        } else {
            None
        }
    })
}
/// guest fd → host fd for `pid` (`None` if unmapped OR a loop socket ⇒ a caller on
/// the host path returns -EBADF; loop fds are routed away before reaching it).
fn fd_host(pid: i32, gfd: i32) -> Option<i32> {
    match fdt().lock().unwrap().get(&pid)?.get(&gfd)? {
        FdVal::Host(h) => Some(*h),
        FdVal::Loop(_) => None,
    }
}
/// guest fd → owned loopback `SockId` for `pid` (`None` if unmapped or a host fd).
/// The `service()` loop fast-path consults this first to route loop-socket ops.
#[allow(dead_code)] // consulted by the C1b-2 loop fast-path
fn fd_loop(pid: i32, gfd: i32) -> Option<netstack::SockId> {
    match fdt().lock().unwrap().get(&pid)?.get(&gfd)? {
        FdVal::Loop(s) => Some(*s),
        FdVal::Host(_) => None,
    }
}
/// Install `hfd` at the lowest free guest fd ≥ `min` (F_DUPFD's contract; 0 for
/// the common case) in `pid`'s table; returns the guest fd.
fn fd_install(pid: i32, hfd: i32, min: i32) -> i64 {
    fd_install_val(pid, FdVal::Host(hfd), min)
}
/// Install an owned loopback socket at the lowest free guest fd ≥ `min`.
#[allow(dead_code)] // called by the C1b-2 connect/bind loopback switch
fn fd_install_loop(pid: i32, sid: netstack::SockId, min: i32) -> i64 {
    fd_install_val(pid, FdVal::Loop(sid), min)
}
fn fd_install_val(pid: i32, v: FdVal, min: i32) -> i64 {
    let flags = fd_default_desc_flags(v);
    fd_install_val_with_flags(pid, v, min, flags)
}
fn fd_install_val_with_flags(pid: i32, v: FdVal, min: i32, flags: i32) -> i64 {
    let nofile_cur = guest_nofile_limit(pid).0;
    let mut t = fdt().lock().unwrap();
    let m = t.entry(pid).or_default();
    let mut g = min.max(0);
    if g < 3 {
        // Keep the standard streams reserved for the life of the process. Real
        // container/VM launches enter with 0/1/2 wired to pipes, a tty, or
        // /dev/null; Chromium and other large runtimes also have stdio cleanup
        // paths that may repeatedly close/probe these slots. If one of those
        // slots is later reused for an unrelated owned fd, userspace fd
        // ownership trackers can see a raw stdio close as a close of that owned
        // resource. Explicit dup2/dup3 to 0/1/2 still goes through
        // fd_install_val_at_with_flags and preserves normal redirection.
        g = 3;
    }
    while m.contains_key(&g) {
        g += 1;
    }
    if (g as u64) >= nofile_cur {
        drop(t);
        fd_close_val(v);
        return -24; // -EMFILE
    }
    m.insert(g, v);
    drop(t);
    fd_set_desc_flags(pid, g, flags);
    g as i64
}
fn fd_close_val(v: FdVal) {
    match v {
        FdVal::Host(p) => unsafe {
            fd_forget_host_side_tables(p);
            host(SYS_CLOSE, p as u64, 0, 0, 0, 0, 0);
        },
        FdVal::Loop(s) => loop_close(s),
    }
}

fn fd_forget_host_side_tables(h: i32) {
    synth_fd_dirs().lock().unwrap().remove(&h);
    synth_proc_dirs().lock().unwrap().remove(&h);
    signalfd_close(h);
    unix_alias_placeholders_remove(h);
    if !host_v6_route() {
        dns_dest_fds().lock().unwrap().remove(&h);
    }
    epoll_loops().lock().unwrap().remove(&h);
}
fn fd_install_val_at(pid: i32, g: i32, v: FdVal) {
    let flags = fd_default_desc_flags(v);
    fd_install_val_at_with_flags(pid, g, v, flags);
}
fn fd_install_val_at_with_flags(pid: i32, g: i32, v: FdVal, flags: i32) {
    let prev = fdt().lock().unwrap().entry(pid).or_default().insert(g, v);
    fd_set_desc_flags(pid, g, flags);
    if let Some(prev) = prev {
        fd_close_val(prev);
    }
}
/// Install `hfd` at EXACTLY guest fd `g` (dup2), closing any previous occupant
/// (a host fd via `close`, or an owned loopback socket via `loop_close`).
fn fd_install_at(pid: i32, g: i32, hfd: i32) {
    fd_install_val_at(pid, g, FdVal::Host(hfd));
}
fn fd_detach_stdio_to_dev_null(pid: i32) {
    let rd = unsafe {
        host(
            SYS_OPENAT,
            (-100i64) as u64,
            b"/dev/null\0".as_ptr() as u64,
            libc::O_RDONLY as u64,
            0,
            0,
            0,
        )
    };
    if rd >= 0 {
        fd_install_at(pid, 0, rd as i32);
    }
    for g in [1, 2] {
        let wr = unsafe {
            host(
                SYS_OPENAT,
                (-100i64) as u64,
                b"/dev/null\0".as_ptr() as u64,
                libc::O_WRONLY as u64,
                0,
                0,
                0,
            )
        };
        if wr >= 0 {
            fd_install_at(pid, g, wr as i32);
        }
    }
}
fn fd_install_loop_dup(pid: i32, sid: netstack::SockId, min: i32) -> i64 {
    loop_state().lock().unwrap().net.dup(sid);
    fd_install_val(pid, FdVal::Loop(sid), min)
}
fn fd_install_loop_dup_at(pid: i32, sid: netstack::SockId, g: i32) {
    loop_state().lock().unwrap().net.dup(sid);
    fd_install_val_at(pid, g, FdVal::Loop(sid));
}
/// Remove guest fd `g` from `pid`'s table, returning what it mapped to (a host fd
/// to `close`, or an owned loopback `SockId` to tear down via `loop_close`).
fn fd_remove(pid: i32, g: i32) -> Option<FdVal> {
    fd_desc_flags().lock().unwrap().remove(&(pid, g));
    fdt().lock().unwrap().get_mut(&pid)?.remove(&g)
}

fn signalfd_writers() -> &'static Mutex<HashMap<i32, i32>> {
    static S: OnceLock<Mutex<HashMap<i32, i32>>> = OnceLock::new();
    S.get_or_init(|| Mutex::new(HashMap::new()))
}

fn signalfd_close(read_h: i32) {
    if let Some(write_h) = signalfd_writers().lock().unwrap().remove(&read_h) {
        unsafe { host(SYS_CLOSE, write_h as u64, 0, 0, 0, 0, 0) };
    }
}

fn signalfd_notify_sigchld(dead_pid: i32) {
    let writers: Vec<i32> = signalfd_writers()
        .lock()
        .unwrap()
        .values()
        .copied()
        .collect();
    if writers.is_empty() {
        return;
    }
    let mut rec = [0u8; 128];
    rec[0..4].copy_from_slice(&(libc::SIGCHLD as u32).to_le_bytes());
    rec[8..12].copy_from_slice(&1u32.to_le_bytes()); // CLD_EXITED
    rec[12..16].copy_from_slice(&(dead_pid as u32).to_le_bytes());
    for write_h in writers {
        unsafe {
            let _ = host(
                SYS_WRITE,
                write_h as u64,
                rec.as_ptr() as u64,
                rec.len() as u64,
                0,
                0,
                0,
            );
        }
    }
}

/// Fork-time snapshots, keyed by the child's ring slot: taken in the PARENT's
/// context (CTL_FORK_TABLE, synchronous, pre-fork), adopted by the child's
/// servicer on its first request. The child's table is the parent's AT FORK
/// TIME with every host fd dup'd — the file DESCRIPTION (offset, flags) is
/// shared while the tables stay independent: exact fork semantics.
fn pending() -> &'static Mutex<HashMap<u64, BTreeMap<i32, FdVal>>> {
    static T: OnceLock<Mutex<HashMap<u64, BTreeMap<i32, FdVal>>>> = OnceLock::new();
    T.get_or_init(|| Mutex::new(HashMap::new()))
}
fn pending_fd_flags() -> &'static Mutex<HashMap<u64, BTreeMap<i32, i32>>> {
    static T: OnceLock<Mutex<HashMap<u64, BTreeMap<i32, i32>>>> = OnceLock::new();
    T.get_or_init(|| Mutex::new(HashMap::new()))
}
fn pending_creds() -> &'static Mutex<HashMap<u64, CellCredSnapshot>> {
    static T: OnceLock<Mutex<HashMap<u64, CellCredSnapshot>>> = OnceLock::new();
    T.get_or_init(|| Mutex::new(HashMap::new()))
}
fn pending_umasks() -> &'static Mutex<HashMap<u64, u32>> {
    static T: OnceLock<Mutex<HashMap<u64, u32>>> = OnceLock::new();
    T.get_or_init(|| Mutex::new(HashMap::new()))
}
fn fd_snapshot(parent: i32, slot: u64) {
    shm_snapshot(parent, slot);
    let src = fdt()
        .lock()
        .unwrap()
        .get(&parent)
        .cloned()
        .unwrap_or_default();
    let mut dst = BTreeMap::new();
    let mut dst_flags = BTreeMap::new();
    for (g, v) in src {
        let desc_flags = fd_get_desc_flags(parent, g, v);
        match v {
            FdVal::Host(h) => {
                let dup_cmd = if (desc_flags & FD_CLOEXEC as i32) != 0 {
                    F_DUPFD_CLOEXEC
                } else {
                    libc::F_DUPFD as u64
                };
                let d = unsafe { host(SYS_FCNTL, h as u64, dup_cmd, 0, 0, 0, 0) };
                if d >= 0 {
                    dst.insert(g, FdVal::Host(d as i32));
                    dst_flags.insert(g, desc_flags);
                    // #36: a dup'd epoll fd SHARES the kernel epoll object (so the
                    // loop readiness eventfds added to it are still watched), but the
                    // loop-watch side table is keyed by fd NUMBER — re-key it onto the
                    // dup so the forked instance's epoll_wait still finds the loop
                    // watches (warm-restore of an epoll event loop).
                    let mut el = epoll_loops().lock().unwrap();
                    if let Some(w) = el.get(&h).cloned() {
                        el.insert(d as i32, w);
                    }
                }
            }
            // #36: the child inherits this owned-loopback socket (e.g. a fork-from-warm
            // instance inheriting the warm daemon's LISTENER). Bump its cross-fork
            // refcount so a later `close` on EITHER end only tears the socket down (and
            // deregisters its listener endpoint) when the LAST reference goes — this is
            // what lets a re-acquired daemon keep serving after a sibling is released.
            FdVal::Loop(s) => {
                loop_state().lock().unwrap().net.dup(s);
                dst.insert(g, FdVal::Loop(s));
                dst_flags.insert(g, desc_flags);
            }
        }
    }
    if unsafe { TRACE } {
        logn(b"  [fd_snapshot ", parent as i64, b"");
        logn(b" slot=", slot as i64, b"");
        logn(b" fds=", dst.len() as i64, b"]\n");
    }
    // Close any STALE dups already parked on this slot before overwriting. A slot is
    // recycled by `alloc_slot` (its SLOT_BM bit) WITHOUT draining pending[], so a prior
    // fork child that took a snapshot here but never adopted it (a cell-local-only
    // straggler freed before its first delegated request) leaves its dups behind. The
    // bare `insert` would drop the old BTreeMap and LEAK its host fds — and those dups
    // include the exec_capture pipe's write-end, so the library's reader never EOFs
    // (the FORK_STORM soak leaked ~130 such write-ends → hard hang). The prior owner is
    // definitively gone (its slot bit was cleared), so closing its fds is always safe.
    if let Some(old) = pending().lock().unwrap().insert(slot, dst) {
        for (_, v) in old {
            fd_close_val(v);
        }
    }
    pending_fd_flags().lock().unwrap().insert(slot, dst_flags);
    pending_creds()
        .lock()
        .unwrap()
        .insert(slot, supervisor_cred_for(parent));
    pending_umasks()
        .lock()
        .unwrap()
        .insert(slot, supervisor_umask_for(parent));
    // The child also inherits the parent's cwd + exe (/proc/self/exe).
    pending_cwd().lock().unwrap().insert(slot, cwd_of(parent));
    pending_exe().lock().unwrap().insert(slot, exe_for(parent));
    pending_cmdline()
        .lock()
        .unwrap()
        .insert(slot, cmdline_for(parent));
    pending_nofile_limits()
        .lock()
        .unwrap()
        .insert(slot, guest_nofile_limit(parent));
    // Shadow the owned process tree: allocate the child's vpid keyed by `slot`,
    // parented to the forking process (native-execution VM, C2a).
    proctree::alloc_for_slot(slot, parent);
}
/// First request from a fork/spawn child on `slot`: the snapshot becomes the
/// child's fd table (and the captured cwd its cwd).
fn fd_adopt(slot: u64, child: i32) {
    shm_adopt(slot, child);
    if let Some(m) = pending().lock().unwrap().remove(&slot) {
        let flags = pending_fd_flags()
            .lock()
            .unwrap()
            .remove(&slot)
            .unwrap_or_default();
        // Same overwrite hazard as fd_snapshot but keyed by pid: if `child`'s pid was
        // recycled and a same-pid predecessor's fdt entry was never dropped, the bare
        // insert would drop its BTreeMap and leak those host fds. Close the displaced
        // table (the predecessor is dead — pids only recycle after exit).
        if let Some(old) = fdt().lock().unwrap().insert(child, m) {
            for (_, v) in old {
                fd_close_val(v);
            }
        }
        fd_desc_flags()
            .lock()
            .unwrap()
            .retain(|(p, _), _| *p != child);
        for (g, fl) in flags {
            fd_set_desc_flags(child, g, fl);
        }
    }
    if let Some(c) = pending_cwd().lock().unwrap().remove(&slot) {
        cwds().lock().unwrap().insert(child, c);
    }
    if let Some(c) = pending_creds().lock().unwrap().remove(&slot) {
        supervisor_creds().lock().unwrap().insert(child, c);
    }
    if let Some(mask) = pending_umasks().lock().unwrap().remove(&slot) {
        supervisor_umasks()
            .lock()
            .unwrap()
            .insert(child, mask & 0o777);
    }
    if let Some(e) = pending_exe().lock().unwrap().remove(&slot) {
        if !e.is_empty() {
            proc_exe().lock().unwrap().insert(child, e);
        }
    }
    if let Some(c) = pending_cmdline().lock().unwrap().remove(&slot) {
        if !c.is_empty() {
            proc_cmdline().lock().unwrap().insert(child, c);
        }
    }
    fdtrace_dump_table(child, b"ADOPT");
    if let Some(lim) = pending_nofile_limits().lock().unwrap().remove(&slot) {
        guest_nofile_limits().lock().unwrap().insert(child, lim);
    }
    // The child's host pid is now known — bind the slot's pending vpid (C2a).
    proctree::bind_slot(slot, child);
}
/// A cell process died: close its host fds, drop its table + cwd.
fn fd_drop(pid: i32) {
    if let Some(m) = fdt().lock().unwrap().remove(&pid) {
        for (_, v) in m {
            match v {
                FdVal::Host(h) => {
                    // #36: if this was an epoll fd, drop its loop-watch side table
                    // (no-op for non-epoll fds) so the entry doesn't linger across
                    // pool churn / fd-number reuse.
                    let had_epoll_loop = epoll_loops().lock().unwrap().contains_key(&h);
                    fd_forget_host_side_tables(h);
                    if had_epoll_loop && epdbg_on() {
                        ipc_logf(&[(b"FDDROP pid=", pid as i64), (b" h=", h as i64)], b"");
                    }
                    unsafe { host(SYS_CLOSE, h as u64, 0, 0, 0, 0, 0) };
                }
                FdVal::Loop(s) => loop_close(s),
            }
        }
    }
    supervisor_umasks().lock().unwrap().remove(&pid);
    fd_desc_flags()
        .lock()
        .unwrap()
        .retain(|(p, _), _| *p != pid);
    cwds().lock().unwrap().remove(&pid);
    proc_exe().lock().unwrap().remove(&pid);
    proc_cmdline().lock().unwrap().remove(&pid);
    guest_nofile_limits().lock().unwrap().remove(&pid);
    supervisor_creds().lock().unwrap().remove(&pid);
    // Drop this process from the owned tree (C2a). Idempotent: CTL_REAP can fire
    // from both the process's own exit_group and its parent's wait4.
    proctree::mark_dead(pid);
}

// ─── owned loopback plane (C1b): LoopNet + per-socket readiness eventfd ───────
//
// The pure `netstack::LoopNet` owns the byte channels; this layer pairs each
// loopback socket with a host EVENTFD used purely as a readiness signal so a
// guest blocked on a loop recv/accept waits via the SAME `wait_or_cancel` path as
// a host socket (cancel-aware for free). `send`/`shutdown`/`close`/`connect`
// signal the relevant peer's/listener's eventfd; data itself lives in LoopNet.
struct LoopState {
    net: netstack::LoopNet,
    /// SockId → readiness eventfd (host fd). Transient: re-created on restore.
    efds: HashMap<netstack::SockId, i32>,
    /// SockIds whose guest fd is O_NONBLOCK (set via SOCK_NONBLOCK / fcntl F_SETFL).
    nonblock: std::collections::HashSet<netstack::SockId>,
}
fn loop_state() -> &'static Mutex<LoopState> {
    static T: OnceLock<Mutex<LoopState>> = OnceLock::new();
    T.get_or_init(|| {
        Mutex::new(LoopState {
            net: netstack::LoopNet::new(),
            efds: HashMap::new(),
            nonblock: std::collections::HashSet::new(),
        })
    })
}
/// THIS servicer's cancel eventfd (or -1 in a non-servicer/unit context). Mirrors
/// the `host_cancellable` lookup so blocking loop ops are cancel-aware via
/// `wait_or_cancel`.
fn servicer_cancel_efd() -> i32 {
    let r = SERVICER_RING.with(|c| c.get());
    if r.is_null() {
        -1
    } else {
        unsafe { std::ptr::read_volatile(std::ptr::addr_of!((*r).cancel_efd)) }
    }
}
fn servicer_intr_efd() -> i32 {
    let r = SERVICER_RING.with(|c| c.get());
    if r.is_null() {
        -1
    } else {
        unsafe { std::ptr::read_volatile(std::ptr::addr_of!((*r).intr_efd)) }
    }
}
/// Create a loopback socket's readiness eventfd (level-triggered, nonblock).
fn loop_new_efd() -> i32 {
    unsafe { host(SYS_EVENTFD2, 0, EFD_NONBLOCK_CLOEXEC, 0, 0, 0, 0) as i32 }
}
/// Wake anyone blocked on `sid`'s readiness (write its level-triggered eventfd).
/// No-op if `sid` has no eventfd yet (a not-yet-accepted server end — `recv` will
/// see the buffered data on its first poll-free check anyway). Caller holds the
/// `LoopState` lock.
fn loop_wake(ls: &LoopState, sid: netstack::SockId) {
    if let Some(&efd) = ls.efds.get(&sid) {
        let one: u64 = 1;
        unsafe {
            host(
                SYS_WRITE,
                efd as u64,
                std::ptr::addr_of!(one) as u64,
                8,
                0,
                0,
                0,
            )
        };
    }
}
/// Tear down an owned loopback socket: drop it from the netstack (waking the peer,
/// which then reads EOF / EPIPEs) and close its readiness eventfd.
fn loop_close(sid: netstack::SockId) {
    // #36: close ONE reference. `net.close` tears the socket down (and returns true)
    // only when this was the last reference; if a fork sibling (the warm zygote, the
    // next acquire) still holds it, it returns false and we must NOT free the shared
    // readiness eventfd, wake the peer, or drop the epoll side tables.
    let torn_down = {
        let mut ls = loop_state().lock().unwrap();
        let peer = ls.net.peer_of(sid);
        let torn = ls.net.close(sid).unwrap_or(true); // EBADF (already gone) ⇒ done
        if torn {
            if let Some(efd) = ls.efds.remove(&sid) {
                unsafe { host(SYS_CLOSE, efd as u64, 0, 0, 0, 0, 0) };
            }
            if let Some(p) = peer {
                loop_wake(&ls, p);
            }
        }
        torn
    }; // drop loop_state BEFORE touching epoll_loops (lock order: epoll_loops→loop_state)
    if torn_down {
        // Remove the socket from any epoll side tables (the efd close above already
        // removed it from the kernel epolls).
        epoll_drop_sid(sid);
    }
}

/// Parse a guest sockaddr (AF_INET / AF_INET6) into a netstack endpoint. `None` for
/// non-IP families (AF_UNIX etc.) which stay on the host plane. Ports are network
/// byte order in the sockaddr; addresses are raw bytes.
fn ep_from_sockaddr(addr: &[u8]) -> Option<netstack::Endpoint> {
    if addr.len() < 2 {
        return None;
    }
    let fam = u16::from_le_bytes([addr[0], addr[1]]);
    match fam {
        2 => {
            // AF_INET: sin_port(2, BE) · sin_addr(4)
            if addr.len() < 8 {
                return None;
            }
            let port = u16::from_be_bytes([addr[2], addr[3]]);
            let mut ip = [0u8; 16];
            ip[..4].copy_from_slice(&addr[4..8]);
            Some(netstack::Endpoint {
                v6: false,
                ip,
                port,
            })
        }
        10 => {
            // AF_INET6: sin6_port(2, BE) · flowinfo(4) · sin6_addr(16)
            if addr.len() < 24 {
                return None;
            }
            let port = u16::from_be_bytes([addr[2], addr[3]]);
            let mut ip = [0u8; 16];
            ip.copy_from_slice(&addr[8..24]);
            Some(netstack::Endpoint { v6: true, ip, port })
        }
        _ => None,
    }
}
/// Is this endpoint a loopback address (the owned plane: 127.0.0.0/8 or ::1)?
fn ep_is_loopback(ep: &netstack::Endpoint) -> bool {
    if ep.v6 {
        ep.ip == [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1]
    } else {
        ep.ip[0] == 127
    }
}
/// SO_TYPE of a host socket fd == SOCK_STREAM? Only STREAM (TCP) loopback is routed
/// to the owned LoopNet (which is stream-only); DATAGRAM (UDP) loopback stays on the
/// host socket in the cell's netns (the host kernel handles UDP loopback there —
/// LoopNet has no datagram model). Default to false (host plane) if the query fails.
fn host_sock_is_stream(h: i32) -> bool {
    let mut ty: i32 = 0;
    let mut len: u32 = 4;
    let r = unsafe {
        host(
            SYS_GETSOCKOPT,
            h as u64,
            libc::SOL_SOCKET as u64,
            libc::SO_TYPE as u64,
            std::ptr::addr_of_mut!(ty) as u64,
            std::ptr::addr_of_mut!(len) as u64,
            0,
        )
    };
    r == 0 && ty == libc::SOCK_STREAM
}
/// Serialize an endpoint into a guest sockaddr at `addr_ptr`, writing the length
/// back to `len_ptr` (both guest pointers; 0 ⇒ skip). For accept/getsockname/
/// getpeername/recvfrom.
fn write_sockaddr(pid: i32, addr_ptr: u64, len_ptr: u64, ep: &netstack::Endpoint) {
    if addr_ptr == 0 {
        return;
    }
    let mut cap = 128usize;
    if len_ptr != 0 {
        let mut lb = [0u8; 4];
        vm_read(pid, len_ptr, &mut lb);
        cap = (u32::from_le_bytes(lb) as usize).min(128);
    }
    let sa = if ep.v6 {
        let mut s = vec![0u8; 28];
        s[0..2].copy_from_slice(&10u16.to_le_bytes());
        s[2..4].copy_from_slice(&ep.port.to_be_bytes());
        s[8..24].copy_from_slice(&ep.ip);
        s
    } else {
        let mut s = vec![0u8; 16];
        s[0..2].copy_from_slice(&2u16.to_le_bytes());
        s[2..4].copy_from_slice(&ep.port.to_be_bytes());
        s[4..8].copy_from_slice(&ep.ip[..4]);
        s
    };
    let n = sa.len().min(cap);
    if n > 0 {
        vm_write(pid, addr_ptr, &sa[..n]);
    }
    if len_ptr != 0 {
        vm_write(pid, len_ptr, &(sa.len() as u32).to_le_bytes());
    }
}
/// Ensure guest fd `gfd` is an owned loopback socket: reuse its `SockId` if already
/// `Loop`, else close the eager host socket `socket()` created and switch the fd to
/// a fresh `LoopNet` socket (+ its readiness eventfd).
fn loop_socket_for(pid: i32, gfd: i32) -> netstack::SockId {
    if let Some(sid) = fd_loop(pid, gfd) {
        return sid;
    }
    // Inherit O_NONBLOCK from the eager host socket BEFORE closing it: clients set
    // the socket non-blocking (fcntl F_SETFL) *before* a loopback connect (libpq does
    // this for its async connect + the pqReadData "1 packet per recv" drain read), and
    // that flag must carry onto the owned LoopNet socket — else a non-blocking drain
    // recv on an empty rx BLOCKS instead of returning EAGAIN (psql/libpq hung waiting
    // for a connection it had already fully read: complete ReadyForQuery in hand).
    let mut nonblock = false;
    if let Some(h) = fd_host(pid, gfd) {
        let fl = unsafe { host(SYS_FCNTL, h as u64, libc::F_GETFL as u64, 0, 0, 0, 0) };
        nonblock = fl >= 0 && (fl as u64 & libc::O_NONBLOCK as u64) != 0;
        unsafe { host(SYS_CLOSE, h as u64, 0, 0, 0, 0, 0) };
    }
    let sid = loop_state().lock().unwrap().net.socket();
    let efd = loop_new_efd();
    {
        let mut ls = loop_state().lock().unwrap();
        ls.efds.insert(sid, efd);
        if nonblock {
            ls.nonblock.insert(sid);
        }
    }
    fdt()
        .lock()
        .unwrap()
        .entry(pid)
        .or_default()
        .insert(gfd, FdVal::Loop(sid));
    sid
}
/// Blocking (unless `nonblock`) recv on a loop socket → guest buffer. Returns bytes
/// read (0 = EOF), -EAGAIN (nonblock + empty), -errno, or CANCEL_SENTINEL if this
/// servicer's cell was reaped while it waited.
/// Core recv: returns (status, data). status = bytes received on success, or
/// -EAGAIN / CANCEL_SENTINEL / -err. Blocks (unless `nonblock`) on the socket's
/// readiness eventfd. The single recv primitive behind read/recvfrom/readv/recvmsg.
fn loop_recv_bytes(sid: netstack::SockId, max: usize, nonblock: bool) -> (i64, Vec<u8>) {
    let max = max.min(CAP as usize);
    loop {
        let res = loop_state().lock().unwrap().net.recv(sid, max);
        match res {
            Ok(data) => return (data.len() as i64, data),
            Err(netstack::EAGAIN) => {
                if nonblock {
                    return (-(netstack::EAGAIN as i64), Vec::new());
                }
                let efd = match loop_state().lock().unwrap().efds.get(&sid).copied() {
                    Some(e) => e,
                    None => return (-9, Vec::new()),
                };
                let w =
                    unsafe { wait_or_cancel(efd, servicer_cancel_efd(), servicer_intr_efd(), -1) };
                if w == -1 {
                    return (CANCEL_SENTINEL, Vec::new());
                }
                if w == -3 {
                    return (EINTR, Vec::new());
                }
                // Drain the level-triggered eventfd before re-checking recv.
                let mut sink = [0u8; 8];
                unsafe { host(SYS_READ, efd as u64, sink.as_mut_ptr() as u64, 8, 0, 0, 0) };
            }
            Err(e) => return (-(e as i64), Vec::new()),
        }
    }
}
fn loop_recv(pid: i32, sid: netstack::SockId, buf_ptr: u64, len: u64, nonblock: bool) -> i64 {
    let (st, data) = loop_recv_bytes(sid, len as usize, nonblock);
    if st >= 0 && !data.is_empty() {
        vm_write(pid, buf_ptr, &data);
    }
    st
}
/// Core send: append `buf` to the peer's rx + wake it. Short write under
/// backpressure; -EAGAIN if the peer rx is full; -EPIPE if the peer is gone. The
/// single send primitive behind write/sendto/writev/sendmsg.
fn loop_send_bytes(sid: netstack::SockId, buf: &[u8]) -> i64 {
    let n = buf.len().min(CAP as usize);
    let mut ls = loop_state().lock().unwrap();
    match ls.net.send(sid, &buf[..n]) {
        Ok(sent) => {
            if let Some(p) = ls.net.peer_of(sid) {
                loop_wake(&ls, p);
            }
            sent as i64
        }
        Err(e) => -(e as i64),
    }
}
fn loop_send(pid: i32, sid: netstack::SockId, buf_ptr: u64, len: u64) -> i64 {
    let n = (len as usize).min(CAP as usize);
    let mut buf = vec![0u8; n];
    vm_read(pid, buf_ptr, &mut buf);
    loop_send_bytes(sid, &buf)
}
/// `sendfile(out_loopsock, in_fd, offset*, count)` where the OUT fd is an owned
/// loop socket: nginx serves static files with `sendfile()` (`sendfile on;`), so
/// without this the response BODY (after writev'd headers) fell through to the
/// host path on a fd with no host backing → the client saw headers then
/// "connection closed prematurely". Reads up to `count` bytes from the host file
/// `in_gfd` (at `*offset` if non-NULL, else the file's own cursor), loop-sends
/// them, and advances `*offset`. Returns bytes sent (a SHORT count under loop
/// backpressure — nginx re-issues), or -errno.
fn loop_sendfile(
    pid: i32,
    out_sid: netstack::SockId,
    in_gfd: i32,
    off_ptr: u64,
    count: u64,
) -> i64 {
    let in_h = match fd_host(pid, in_gfd) {
        Some(h) => h,
        None => return -9, // -EBADF
    };
    let n = (count as usize).min(CAP as usize);
    if n == 0 {
        return 0;
    }
    let mut buf = vec![0u8; n];
    // Read the file region. With an explicit offset use pread (no cursor move);
    // else a plain read advances the host file's own offset (the NULL-offset
    // sendfile contract).
    let got = if off_ptr != 0 {
        let mut ob = [0u8; 8];
        vm_read(pid, off_ptr, &mut ob);
        let off = u64::from_le_bytes(ob);
        unsafe {
            host(
                SYS_PREAD64,
                in_h as u64,
                buf.as_mut_ptr() as u64,
                n as u64,
                off,
                0,
                0,
            )
        }
    } else {
        unsafe {
            host(
                SYS_READ,
                in_h as u64,
                buf.as_mut_ptr() as u64,
                n as u64,
                0,
                0,
                0,
            )
        }
    };
    if got <= 0 {
        return got; // EOF (0) or -errno
    }
    let sent = loop_send_bytes(out_sid, &buf[..got as usize]);
    if sent > 0 && off_ptr != 0 {
        // Advance the in/out offset cursor by the bytes actually sent (pread did
        // not move the file offset, so the caller relies on this write-back).
        let mut ob = [0u8; 8];
        vm_read(pid, off_ptr, &mut ob);
        let new_off = u64::from_le_bytes(ob).wrapping_add(sent as u64);
        vm_write(pid, off_ptr, &new_off.to_le_bytes());
    }
    sent
}
/// Sum the lengths of a guest iovec[] (each is {base@0, len@8}, 16 bytes; capped at
/// IOV_MAX=1024 entries). Used to size the recv for readv/recvmsg.
fn iov_total(pid: i32, iov_ptr: u64, iovcnt: u64) -> usize {
    let cnt = (iovcnt as usize).min(1024);
    let mut total = 0usize;
    for i in 0..cnt {
        let mut iv = [0u8; 16];
        vm_read(pid, iov_ptr + (i * 16) as u64, &mut iv);
        total = total.saturating_add(u64::from_le_bytes(iv[8..16].try_into().unwrap()) as usize);
    }
    total
}
/// Gather a guest iovec[] into one contiguous buffer (capped at CAP).
fn gather_iov(pid: i32, iov_ptr: u64, iovcnt: u64) -> Vec<u8> {
    let cnt = (iovcnt as usize).min(1024);
    let mut out = Vec::new();
    for i in 0..cnt {
        if out.len() >= CAP as usize {
            break;
        }
        let mut iv = [0u8; 16];
        vm_read(pid, iov_ptr + (i * 16) as u64, &mut iv);
        let base = u64::from_le_bytes(iv[0..8].try_into().unwrap());
        let ilen = u64::from_le_bytes(iv[8..16].try_into().unwrap()) as usize;
        let take = ilen.min(CAP as usize - out.len());
        if take == 0 {
            continue;
        }
        let mut chunk = vec![0u8; take];
        vm_read(pid, base, &mut chunk);
        out.extend_from_slice(&chunk);
    }
    out
}
/// Scatter `data` across a guest iovec[]; returns bytes written out.
fn scatter_iov(pid: i32, iov_ptr: u64, iovcnt: u64, data: &[u8]) -> usize {
    let cnt = (iovcnt as usize).min(1024);
    let mut off = 0usize;
    for i in 0..cnt {
        if off >= data.len() {
            break;
        }
        let mut iv = [0u8; 16];
        vm_read(pid, iov_ptr + (i * 16) as u64, &mut iv);
        let base = u64::from_le_bytes(iv[0..8].try_into().unwrap());
        let ilen = u64::from_le_bytes(iv[8..16].try_into().unwrap()) as usize;
        let take = ilen.min(data.len() - off);
        if take == 0 {
            continue;
        }
        vm_write(pid, base, &data[off..off + take]);
        off += take;
    }
    off
}
fn loop_writev(pid: i32, sid: netstack::SockId, iov_ptr: u64, iovcnt: u64) -> i64 {
    loop_send_bytes(sid, &gather_iov(pid, iov_ptr, iovcnt))
}
fn loop_readv(pid: i32, sid: netstack::SockId, iov_ptr: u64, iovcnt: u64, nonblock: bool) -> i64 {
    let (st, data) = loop_recv_bytes(sid, iov_total(pid, iov_ptr, iovcnt), nonblock);
    if st < 0 {
        return st;
    }
    scatter_iov(pid, iov_ptr, iovcnt, &data) as i64
}
/// sendmsg/recvmsg on a connected loopback socket: only the data vector matters
/// (msg_name is irrelevant for a connected pair; ancillary/msg_control is not
/// supported on owned loopback). msghdr x86_64: msg_iov @16, msg_iovlen @24.
fn loop_sendmsg(pid: i32, sid: netstack::SockId, msg_ptr: u64) -> i64 {
    let mut mh = [0u8; 32];
    vm_read(pid, msg_ptr, &mut mh);
    let iov = u64::from_le_bytes(mh[16..24].try_into().unwrap());
    let iovlen = u64::from_le_bytes(mh[24..32].try_into().unwrap());
    loop_send_bytes(sid, &gather_iov(pid, iov, iovlen))
}
fn loop_recvmsg(pid: i32, sid: netstack::SockId, msg_ptr: u64, nonblock: bool) -> i64 {
    let mut mh = [0u8; 32];
    vm_read(pid, msg_ptr, &mut mh);
    let iov = u64::from_le_bytes(mh[16..24].try_into().unwrap());
    let iovlen = u64::from_le_bytes(mh[24..32].try_into().unwrap());
    let (st, data) = loop_recv_bytes(sid, iov_total(pid, iov, iovlen), nonblock);
    if st < 0 {
        return st;
    }
    scatter_iov(pid, iov, iovlen, &data) as i64
}
/// Blocking (unless `nonblock`) accept on a loop listener → install + return a new
/// loop fd for the accepted connection; write the peer addr if requested.
fn loop_accept(
    pid: i32,
    sid: netstack::SockId,
    addr_ptr: u64,
    len_ptr: u64,
    nonblock: bool,
) -> i64 {
    loop {
        let res = loop_state().lock().unwrap().net.accept(sid);
        match res {
            Ok(server) => {
                let efd = loop_new_efd();
                loop_state().lock().unwrap().efds.insert(server, efd);
                if addr_ptr != 0 {
                    if let Some(ep) = loop_state().lock().unwrap().net.peer_addr(server) {
                        write_sockaddr(pid, addr_ptr, len_ptr, &ep);
                    }
                }
                return fd_install_loop(pid, server, 0);
            }
            Err(netstack::EAGAIN) => {
                if nonblock {
                    return -(netstack::EAGAIN as i64);
                }
                let efd = match loop_state().lock().unwrap().efds.get(&sid).copied() {
                    Some(e) => e,
                    None => return -9,
                };
                let w =
                    unsafe { wait_or_cancel(efd, servicer_cancel_efd(), servicer_intr_efd(), -1) };
                if w == -1 {
                    return CANCEL_SENTINEL;
                }
                if w == -3 {
                    return EINTR;
                }
                let mut sink = [0u8; 8];
                unsafe { host(SYS_READ, efd as u64, sink.as_mut_ptr() as u64, 8, 0, 0, 0) };
            }
            Err(e) => return -(e as i64),
        }
    }
}
/// Dispatch a syscall whose primary fd (`a`) is the owned loopback socket `sid`.
/// Handles the Stage-1 blocking subset; an unhandled op returns -EOPNOTSUPP.
fn loop_service(
    pid: i32,
    nr: i64,
    gfd: i32,
    sid: netstack::SockId,
    b: u64,
    c: u64,
    d: u64,
    e: u64,
    f: u64,
) -> i64 {
    let nb = loop_state().lock().unwrap().nonblock.contains(&sid);
    match nr {
        SYS_READ => loop_recv(pid, sid, b, c, nb),
        SYS_RECVFROM => {
            let nonblock = nb || (d & 0x40) != 0; // MSG_DONTWAIT
            let r = loop_recv(pid, sid, b, c, nonblock);
            if r >= 0 && e != 0 {
                if let Some(ep) = loop_state().lock().unwrap().net.peer_addr(sid) {
                    write_sockaddr(pid, e, f, &ep);
                }
            }
            r
        }
        SYS_WRITE | SYS_SENDTO => loop_send(pid, sid, b, c),
        // Vectored I/O: node/libuv write the HTTP response via writev (headers+body
        // gathered) and read via readv; without these a loop conn socket's writev fell
        // through to the host path on a fd with no host backing → no response → the
        // client saw "socket hang up". sendmsg/recvmsg complete the set.
        SYS_WRITEV => loop_writev(pid, sid, b, c),
        SYS_READV => loop_readv(pid, sid, b, c, nb),
        // sendfile(out=loop, in_fd@b, offset*@c, count@d) — static-file response body.
        SYS_SENDFILE => loop_sendfile(pid, sid, b as i32, c, d),
        SYS_SENDMSG => loop_sendmsg(pid, sid, b),
        SYS_RECVMSG => loop_recvmsg(pid, sid, b, nb || (c & 0x40) != 0), // MSG_DONTWAIT
        SYS_LISTEN => loop_state()
            .lock()
            .unwrap()
            .net
            .listen(sid)
            .map(|_| 0)
            .unwrap_or_else(|e| -(e as i64)),
        SYS_ACCEPT => loop_accept(pid, sid, b, c, nb),
        SYS_ACCEPT4 => loop_accept(pid, sid, b, c, nb || (d & 0x800) != 0), // SOCK_NONBLOCK
        SYS_SHUTDOWN => {
            let mut ls = loop_state().lock().unwrap();
            let peer = ls.net.peer_of(sid);
            let r = ls
                .net
                .shutdown(sid, b as i32)
                .map(|_| 0)
                .unwrap_or_else(|e| -(e as i64));
            if let Some(p) = peer {
                loop_wake(&ls, p);
            }
            r
        }
        SYS_GETSOCKNAME => match loop_state().lock().unwrap().net.local_addr(sid) {
            Some(ep) => {
                write_sockaddr(pid, b, c, &ep);
                0
            }
            None => -(netstack::ENOTCONN as i64),
        },
        SYS_GETPEERNAME => match loop_state().lock().unwrap().net.peer_addr(sid) {
            Some(ep) => {
                write_sockaddr(pid, b, c, &ep);
                0
            }
            None => -(netstack::ENOTCONN as i64),
        },
        SYS_SETSOCKOPT => 0, // accept + ignore (loopback ignores SO_* tuning)
        SYS_GETSOCKOPT => {
            // Report SO_ERROR / any opt as 0 (no async error on owned loopback).
            if d != 0 && e != 0 {
                let mut lb = [0u8; 4];
                vm_read(pid, e, &mut lb);
                if u32::from_le_bytes(lb) as usize >= 4 {
                    vm_write(pid, d, &0i32.to_le_bytes());
                    vm_write(pid, e, &4u32.to_le_bytes());
                }
            }
            0
        }
        SYS_DUP => fd_install_loop_dup(pid, sid, 0),
        SYS_DUP2 | SYS_DUP3 => {
            let gnew = b as i32;
            if nr == SYS_DUP3 && (c & !(libc::O_CLOEXEC as u64)) != 0 {
                return -22; // -EINVAL
            }
            if gfd == gnew {
                if nr == SYS_DUP2 {
                    gnew as i64
                } else {
                    -22 // dup3: old == new is EINVAL
                }
            } else {
                loop_state().lock().unwrap().net.dup(sid);
                let flags = if nr == SYS_DUP3 && (c & libc::O_CLOEXEC as u64) != 0 {
                    FD_CLOEXEC as i32
                } else {
                    0
                };
                fd_install_val_at_with_flags(pid, gnew, FdVal::Loop(sid), flags);
                gnew as i64
            }
        }
        SYS_FCNTL => match b {
            0 => fd_install_loop_dup(pid, sid, c as i32),
            1030 => {
                loop_state().lock().unwrap().net.dup(sid);
                fd_install_val_with_flags(pid, FdVal::Loop(sid), c as i32, FD_CLOEXEC as i32)
            }
            F_GETFD => fd_get_desc_flags(pid, gfd, FdVal::Loop(sid)) as i64,
            F_SETFD => {
                fd_set_desc_flags(pid, gfd, c as i32);
                0
            }
            3 => {
                // F_GETFL → O_RDWR (2) | O_NONBLOCK if set.
                if nb {
                    2 | 0x800
                } else {
                    2
                }
            }
            4 => {
                // F_SETFL → track O_NONBLOCK.
                let mut ls = loop_state().lock().unwrap();
                if (c & 0x800) != 0 {
                    ls.nonblock.insert(sid);
                } else {
                    ls.nonblock.remove(&sid);
                }
                0
            }
            _ => 0,
        },
        _ => -95, // -EOPNOTSUPP: unhandled op on a loop socket (Stage-1 subset)
    }
}
// ─── per-process guest cwd ───────────────────────────────────────────────────
//
// The supervisor resolves every relative guest path against the CALLING pid's
// cwd (not the supervisor's own, and not always the rootfs root). Like the fd
// tables this is per-pid, copied on fork. The stored cwd is guest-absolute and
// symlink-CANONICAL (resolved through the kernel at `chdir` time via a
// `/proc/self/fd` readlink), so a later `cd ..` across a symlinked dir matches
// POSIX. Default `/` until the process `chdir`s.
fn cwds() -> &'static Mutex<HashMap<i32, Vec<u8>>> {
    static T: OnceLock<Mutex<HashMap<i32, Vec<u8>>>> = OnceLock::new();
    T.get_or_init(|| Mutex::new(HashMap::new()))
}
/// This pid's guest cwd (no trailing NUL; `/`-rooted, no trailing slash).
fn cwd_of(pid: i32) -> Vec<u8> {
    cwds()
        .lock()
        .unwrap()
        .get(&pid)
        .cloned()
        .unwrap_or_else(|| b"/".to_vec())
}
/// Fork-time cwd snapshots, keyed by the child's ring slot (parallel to the fd
/// `pending()` map). Captured pre-fork in the parent, adopted by the child.
fn pending_cwd() -> &'static Mutex<HashMap<u64, Vec<u8>>> {
    static T: OnceLock<Mutex<HashMap<u64, Vec<u8>>>> = OnceLock::new();
    T.get_or_init(|| Mutex::new(HashMap::new()))
}
/// Per-slot pending `/proc/self/exe` for a fork child, snapshotted from the
/// parent at fork (keyed by slot) and applied to the child's `proc_exe` on adopt.
/// A fork child INHERITS the parent's exe until its own execve — load-bearing for
/// Chromium, which forks then re-execs `/proc/self/exe` (so the child must see
/// the browser binary, not the spawn-time `/bin/sh`, BEFORE that exec runs).
fn pending_exe() -> &'static Mutex<HashMap<u64, Vec<u8>>> {
    static T: OnceLock<Mutex<HashMap<u64, Vec<u8>>>> = OnceLock::new();
    T.get_or_init(|| Mutex::new(HashMap::new()))
}
/// Per-slot pending guest cmdline for a fork child (parallel to `pending_exe`):
/// snapshotted from the parent at fork, adopted into the child's `proc_cmdline`
/// on first delegated request. A fork child inherits the parent's argv until its
/// own execve overwrites it via CTL_SET_CMDLINE.
fn pending_cmdline() -> &'static Mutex<HashMap<u64, Vec<u8>>> {
    static T: OnceLock<Mutex<HashMap<u64, Vec<u8>>>> = OnceLock::new();
    T.get_or_init(|| Mutex::new(HashMap::new()))
}
/// The canonical host path of an fd (via `/proc/self/fd/<fd>` readlink — the
/// kernel's symlink-resolved path). `None` on failure.
fn readlink_fd(fd: i32) -> Option<Vec<u8>> {
    let mut link = b"/proc/self/fd/".to_vec();
    let mut nb = [0u8; 24];
    let i = fmt_i64(fd as i64, &mut nb);
    link.extend_from_slice(&nb[i..]);
    link.push(0);
    let mut buf = [0u8; 4096];
    let n = unsafe {
        host(
            SYS_READLINK,
            link.as_ptr() as u64,
            buf.as_mut_ptr() as u64,
            buf.len() as u64,
            0,
            0,
            0,
        )
    };
    if n <= 0 {
        None
    } else {
        Some(buf[..n as usize].to_vec())
    }
}

/// Given an O_PATH fd to a directory, return its GUEST-absolute path: the
/// canonical host path mapped back through the bind-mounts (if inside one) or
/// the rootfs prefix. Used by `chdir` so a later `..` traversal is correct.
/// `None` on any failure.
fn canonical_guest_cwd(fd: i32) -> Option<Vec<u8>> {
    let hp = readlink_fd(fd)?;
    host_to_guest_path(hp.as_slice())
}

/// Map a canonical HOST path back to the GUEST-visible path: reverse the
/// bind-mounts (a path inside a mount → its guest prefix), else strip the rootfs
/// prefix. `None` if the host path is outside the rootfs/mounts (which, for a
/// confined fd, shouldn't happen). Used by `chdir` and `/proc/self/fd` readlink.
fn host_to_guest_path(host_path: &[u8]) -> Option<Vec<u8>> {
    {
        let mt = mounts().lock().unwrap();
        for m in mt.iter() {
            if let Some(g) = map_host_into(&m.host_canonical, host_path, &m.guest_prefix) {
                return Some(g);
            }
        }
    }
    let root = root_path().lock().unwrap();
    if root.is_empty() {
        // Non-rootfs (legacy spike) mode: the host path IS the guest path.
        return Some(host_path.to_vec());
    }
    // Strip the rootfs prefix: "<root>/sub/dir" → "/sub/dir"; "<root>" → "/".
    map_host_into(root.as_slice(), host_path, b"")
}

/// If `host_path` is `host_root` exactly or a subpath of it, return the guest
/// path `guest_prefix + remainder` (e.g. host `/vols/d/sub` under host_root
/// `/vols/d` with prefix `/data` → `/data/sub`; exact match → `guest_prefix`
/// or `/`). `None` if `host_path` isn't covered (a partial-component match like
/// `/vols/d2` is rejected).
fn map_host_into(host_root: &[u8], host_path: &[u8], guest_prefix: &[u8]) -> Option<Vec<u8>> {
    if !prefix_covers(host_root, host_path) {
        return None;
    }
    let rest = &host_path[host_root.len()..]; // "" or "/sub..."
    if rest.is_empty() {
        Some(if guest_prefix.is_empty() {
            b"/".to_vec()
        } else {
            guest_prefix.to_vec()
        })
    } else {
        let mut g = guest_prefix.to_vec();
        g.extend_from_slice(rest);
        Some(g)
    }
}

/// Pull a guest path and, if it is RELATIVE, join it onto the pid's cwd so it
/// resolves correctly. Absolute paths pass through unchanged. The result is
/// always handed to `open_path`/`opath`, whose `RESOLVE_IN_ROOT` does the real
/// (symlink/`..`) resolution — so this only has to supply the right base.
/// (When cwd is `/`, a relative `foo` becomes `/foo`, which `RESOLVE_IN_ROOT`
/// treats identically to `foo` — so this is a no-op for processes that never
/// `chdir`.) Returns a NUL-terminated path.
fn pull_cwd_path(pid: i32, remote: u64) -> Vec<u8> {
    let p = pull_path(pid, remote); // NUL-terminated
    if p.first() == Some(&b'/') {
        return p; // absolute
    }
    let mut full = cwd_of(pid);
    full.push(b'/');
    full.extend_from_slice(&p[..p.len().saturating_sub(1)]); // drop the NUL
    full.push(0);
    full
}

/// Reconstruct the absolute GUEST path that `openat(dirfd, rel)` targets, so a
/// dirfd-RELATIVE open of a SYNTHESIZED path (e.g. `/proc/self/*`) can be routed
/// through [`open_path`]'s per-process `/proc` view instead of a raw host `openat`
/// against the rootfs (where it would ENOENT). `dirfd_h` is the already-translated
/// HOST dirfd; `rel` is NUL-terminated (from [`pull_path`]); the result is too.
/// An absolute `rel` ignores the dirfd (POSIX). `None` if the dirfd path can't be
/// recovered (→ caller falls back to the plain host openat).
fn openat_abs_guest_path(dirfd_h: i32, rel: &[u8]) -> Option<Vec<u8>> {
    let r = match rel.split_last() {
        Some((0, head)) => head,
        _ => rel,
    };
    if r.first() == Some(&b'/') {
        let mut a = r.to_vec();
        a.push(0);
        return Some(a);
    }
    if synth_proc_dirs().lock().unwrap().contains_key(&dirfd_h) {
        let mut abs = b"/proc".to_vec();
        if !r.is_empty() {
            abs.push(b'/');
            abs.extend_from_slice(r);
        }
        abs.push(0);
        return Some(abs);
    }
    let host_dir = readlink_fd(dirfd_h)?;
    let mut abs = if let Some(abs) = host_to_guest_path(&host_dir) {
        abs
    } else {
        let rest = host_dir.strip_prefix(b"/proc/")?;
        let first_slash = rest.iter().position(|&b| b == b'/');
        let pid_part = first_slash.map_or(rest, |i| &rest[..i]);
        let pid = parse_u32(pid_part)? as i32;
        if pid <= 0
            || (pid != MAIN_CELL_PID.load(Ordering::Relaxed) as i32
                && proctree::vpid_for(pid).is_none())
        {
            return None;
        }
        host_dir
    };
    if abs.last() != Some(&b'/') {
        abs.push(b'/');
    }
    abs.extend_from_slice(r);
    abs.push(0);
    Some(abs)
}

/// Recycle the dead process's ring slots (called by the servicer AFTER it has
/// responded to the CTL_REAP, so the caller's own slot is quiescent by then).
///
/// A QUIESCENT slot (request == response) is freed immediately — its servicer is
/// idle. A WEDGED slot (request != response) is one whose owning cell died blocked
/// in a delegated syscall, leaving its servicer parked in a host fd-wait — it used
/// to LEAK forever (after ~MAX_SLOTS leaks, fork returns -EAGAIN and the sandbox
/// wedges). Part C reclaims it CANCEL-ON-REAP: `pid` is DEFINITIVELY DEAD here
/// (CTL_REAP is sent only after the cell host(wait4)'d the child or it exit_group'd
/// — see CTL_REAP), so its stranded servicer is blocked against a dead peer and
/// can't produce a real result. Write a token to the servicer's CANCEL EVENTFD
/// (`cancel_efd`): its blocking poll — which always watches that eventfd alongside
/// the target fd — returns at once (level-triggered ⇒ no missed-wakeup window), it
/// returns CANCEL_SENTINEL, and tears its OWN slot down (fd_drop + free) in its OWN
/// thread. Race-free, no signals, no force response.store.
fn free_slots_of(pid: i32) {
    // Skip slot 0: it's the main cell's IMPLICIT slot (%gs=0), always reserved
    // (bit 0 preset, never alloc'd). Freeing it would let the next clone/fork get
    // slot 0 and collide with the main cell — harmless in the per-exec model
    // (the supervisor dies with the cell) but fatal under the PERSISTENT
    // supervisor, where slot 0 is reused by the next exec's cell.
    for i in 1..MAX_SLOTS as u64 {
        let r = ring_at(i);
        unsafe {
            if std::ptr::read_volatile(std::ptr::addr_of!((*r).pid)) != pid {
                continue;
            }
            let req = ring_word(std::ptr::addr_of_mut!((*r).request)).load(Ordering::Acquire);
            let resp = ring_word(std::ptr::addr_of_mut!((*r).response)).load(Ordering::Acquire);
            if req == resp {
                // Drain any un-adopted fork snapshot BEFORE freeing the slot. A fork
                // child that claimed this slot + took its fd snapshot (pending[i]) but
                // was reaped before its FIRST delegated request (e.g. SIGKILL'd, then
                // wait4'd by its parent → CTL_REAP → here) never ran fd_adopt, so its
                // inherited dups — INCLUDING the exec_capture pipe's write-end — still
                // sit in pending[i], not fdt. free_slot only clears the bit/pid; without
                // this drain those dups leak (the slot may never be reused, so the
                // overwrite-close in fd_snapshot can't catch it) and the capture pipe
                // never EOFs → the library reader hangs. No-op once the child adopted.
                pending_drain_slot(i);
                free_slot(i as u32);
                continue;
            }
            // Wedged: the servicer is (or is about to be) blocked in a host fd-wait
            // for a now-dead peer. A CTL slot never blocks indefinitely (control ops
            // are synchronous bookkeeping), so only a PROCESS/DELEG slot needs the
            // cancel. Write its cancel eventfd: level-triggered, so the servicer's
            // current OR next poll() sees it and abandons + frees the slot itself.
            let kind = std::ptr::read_volatile(std::ptr::addr_of!((*r).kind));
            let efd = std::ptr::read_volatile(std::ptr::addr_of!((*r).cancel_efd));
            if (kind != RING_PROCESS && kind != RING_DELEG) || efd < 0 {
                continue;
            }
            let one: u64 = 1;
            std::ptr::write_volatile(
                std::ptr::addr_of_mut!((*r).cancel_mode),
                CANCEL_REAP_PROCESS,
            );
            host(
                SYS_WRITE,
                efd as u64,
                std::ptr::addr_of!(one) as u64,
                8,
                0,
                0,
                0,
            );
            std::ptr::write_volatile(std::ptr::addr_of_mut!((*r).cancel_pending), 1);
            // Bounded confirmation: the servicer frees the slot itself once it sees
            // the eventfd. If instead its blocking call returned NORMALLY just as we
            // wrote (a read EOF on the dead peer), it goes quiescent (req == resp) with
            // the token still pending — free it directly (the servicer drains the
            // stale token at its next request). Either way the slot is reclaimed.
            for _ in 0..64 {
                if !slot_is_set(i) {
                    break; // the servicer abandoned + freed it
                }
                let rq = ring_word(std::ptr::addr_of_mut!((*r).request)).load(Ordering::Acquire);
                let rp = ring_word(std::ptr::addr_of_mut!((*r).response)).load(Ordering::Acquire);
                if rq == rp {
                    pending_drain_slot(i); // see the quiescent branch above (no-op once adopted)
                    free_slot(i as u32);
                    break;
                }
                let ts = [0i64, 1_000_000]; // 1ms
                host(SYS_NANOSLEEP, ts.as_ptr() as u64, 0, 0, 0, 0, 0);
            }
        }
    }
}

// ─── deadlock-breaking fd-leak sweep (a low-rate /proc liveness sweep thread) ──
//
// A cell that dies WITHOUT delegating CTL_REAP — signal-killed (USR1 default /
// SIGPIPE / the SIGSYS wall), or raw `exit(60)` — leaks its ADOPTED host fds, incl.
// a pipe WRITE-end dup the supervisor holds for it, so a delegated reader (the
// shell's `x=$(echo)` read) NEVER EOFs. CTL_REAP (exit_group / parent wait4),
// CANCEL_SENTINEL, and the pool's sweep_stragglers close those fds only for cells
// that exit cleanly or are reaped. The unclosed gap is the DEADLOCK case: a
// `$(echo)` child signal-killed while its parent shell is BLOCKED in the very read
// that needs the child's write-end to EOF — the shell can't reach its wait4, and the
// child is a GRANDCHILD of the supervisor (its SIGCHLD never reaches us), so nobody
// closes OUR write-end dup. This thread walks the ring-recorded cell pids at a low
// rate and, for any /proc says is a zombie, closes its adopted fds → the read EOFs →
// the shell unblocks and wait4's the zombie (a harmless re-drop). Deliberately MINIMAL:
// it ONLY closes fds in NORMAL context (Mutex/close-safe) and NEVER waitpid's — it is
// NOT a subreaper/reaper (that model regressed fork churn); the guest keeps its own
// children + wait4 status untouched, and on_sigchld still drives main-cell teardown.

/// Is host `pid` a zombie ('Z'/'X') or gone? Reads `/proc/<pid>/stat` (supervisor
/// shares the host pid+mount ns). Unreadable ⇒ gone ⇒ dead. A LIVE pid (R/S/D/T…)
/// returns false, so the sweep never closes a merely-blocked cell's fds.
fn pid_is_dead(pid: i32) -> bool {
    let mut nb = [0u8; 24];
    let i = fmt_i64(pid as i64, &mut nb);
    let mut p = Vec::with_capacity(24);
    p.extend_from_slice(b"/proc/");
    p.extend_from_slice(&nb[i..]);
    p.extend_from_slice(b"/stat\0");
    let fd = unsafe { host(SYS_OPENAT, (-100i64) as u64, p.as_ptr() as u64, 0, 0, 0, 0) };
    if fd < 0 {
        return true;
    }
    let mut buf = [0u8; 256];
    let got = unsafe {
        host(
            SYS_READ,
            fd as u64,
            buf.as_mut_ptr() as u64,
            buf.len() as u64,
            0,
            0,
            0,
        )
    };
    unsafe { host(SYS_CLOSE, fd as u64, 0, 0, 0, 0, 0) };
    if got <= 0 {
        return true;
    }
    let s = &buf[..got as usize];
    // `<pid> (<comm>) <state> …` — comm can hold ')'/spaces, so scan to the LAST ')'.
    if let Some(rp) = s.iter().rposition(|&b| b == b')') {
        if let Some(&st) = s.get(rp + 2) {
            return st == b'Z' || st == b'X' || st == b'x';
        }
    }
    false
}

/// Close the un-adopted fork-snapshot dups for ring `slot` (a child that died before
/// its first request never ran fd_adopt, so its dups still sit in `pending[slot]`).
fn pending_drain_slot(slot: u64) {
    if let Some(m) = pending().lock().unwrap().remove(&slot) {
        for (_, v) in m {
            fd_close_val(v);
        }
    }
    pending_fd_flags().lock().unwrap().remove(&slot);
    pending_creds().lock().unwrap().remove(&slot);
    pending_cwd().lock().unwrap().remove(&slot);
    pending_exe().lock().unwrap().remove(&slot);
    pending_cmdline().lock().unwrap().remove(&slot);
    pending_shm_maps().lock().unwrap().remove(&slot);
}

/// A cell pid is DEAD: close every host fd it owns (delivering pipe EOFs), drain any
/// un-adopted fork snapshot, and reclaim its ring slots. Idempotent — a later
/// CTL_REAP for the same pid re-runs these as no-ops. Does NOT waitpid (the guest's
/// own parent owns the exit status).
fn reap_dead_pid(pid: i32) {
    if pid <= 1 {
        return;
    }
    shm_reap_pid(pid);
    fd_drop(pid);
    for i in 0..MAX_SLOTS as u64 {
        unsafe {
            let r = ring_at(i);
            if std::ptr::read_volatile(std::ptr::addr_of!((*r).pid)) == pid
                && std::ptr::read_volatile(std::ptr::addr_of!((*r).fork_parent)) != 0
            {
                pending_drain_slot(i);
            }
        }
    }
    free_slots_of(pid);
}

/// One pass: reap_dead_pid every DISTINCT ring-recorded cell pid that /proc says is
/// dead. Skips the empty sentinel (≤1), the MAIN cell (its death is on_sigchld's
/// teardown trigger), and in-transit slots (pid 0 after free_slot clears it).
fn sweep_dead_cells() {
    let main = MAIN_CELL_PID.load(Ordering::Relaxed) as i32;
    let mut seen: [i32; MAX_SLOTS] = [0; MAX_SLOTS];
    let mut n = 0usize;
    for i in 1..MAX_SLOTS as u64 {
        let p = unsafe { std::ptr::read_volatile(std::ptr::addr_of!((*ring_at(i)).pid)) };
        if p <= 1 || p == main || seen[..n].contains(&p) {
            continue;
        }
        seen[n] = p;
        n += 1;
        if pid_is_dead(p) {
            reap_dead_pid(p);
        }
    }
}

/// The fd-leak sweeper thread (NORMAL context). 20ms cadence keeps an idle sandbox
/// at ~0 CPU while bounding the EOF latency of the deadlock case.
fn fd_leak_sweeper() -> ! {
    loop {
        sweep_dead_cells();
        let ts = [0i64, 20_000_000]; // 20ms
        unsafe { host(SYS_NANOSLEEP, ts.as_ptr() as u64, 0, 0, 0, 0, 0) };
    }
}

fn install_child_subreaper(context: &'static [u8]) {
    let rc = unsafe { libc::prctl(PR_SET_CHILD_SUBREAPER_SUPERVISOR, 1, 0, 0, 0) };
    ipc_logf(&[(b"SUBREAPER rc=", rc as i64)], context);
}

fn log_adopted_orphan_reap(pid: i32, status: c_int) {
    ipc_logf(
        &[
            (b"SUBREAPER_REAP pid=", pid as i64),
            (b" status=", status as i64),
        ],
        b"",
    );
}

fn adopted_orphan_reaper(launcher_pid: i32) -> ! {
    loop {
        loop {
            let mut status: c_int = 0;
            let r = unsafe { libc::waitpid(-1, &mut status, libc::WNOHANG) };
            if r <= 0 {
                break;
            }
            if r == launcher_pid {
                ipc_logf(
                    &[
                        (b"SUBREAPER_LAUNCHER_EXIT pid=", r as i64),
                        (b" status=", status as i64),
                    ],
                    b"",
                );
                unsafe { libc::_exit(127) };
            }
            log_adopted_orphan_reap(r, status);
            reap_dead_pid(r);
        }
        let ts = [0i64, 20_000_000]; // 20ms
        unsafe { host(SYS_NANOSLEEP, ts.as_ptr() as u64, 0, 0, 0, 0, 0) };
    }
}

/// Last-resort VM teardown for the single-threaded launcher. If the persistent
/// supervisor disappears, the launcher otherwise exits and its forked guest
/// process tree reparents to init. That is normal Linux process behavior, but it
/// is not VM behavior: dropping a Supermachine VM must tear down every guest
/// process it owns, including browser trees that outlive the foreground runner.
fn launcher_kill_owned_processes_on_eof() {
    unsafe {
        let me = libc::getpid();
        let mut victims: [i32; MAX_SLOTS] = [0; MAX_SLOTS];
        let mut n = 0usize;
        for i in 0..MAX_SLOTS as u64 {
            let p = std::ptr::read_volatile(std::ptr::addr_of!((*ring_at(i)).pid));
            if p <= 1 || p == me || victims[..n].contains(&p) {
                continue;
            }
            victims[n] = p;
            n += 1;
        }

        // Kill both the recorded pid and, when it is a process-group leader, its
        // group. Chromium/Playwright regularly creates a broad process tree; the
        // per-pid pass catches ring-owned children that moved into another group.
        for &p in &victims[..n] {
            if libc::getpgid(p) == p {
                libc::kill(-p, libc::SIGKILL);
            }
            libc::kill(p, libc::SIGKILL);
        }

        // Reap direct children we can reap. Grandchildren may already have been
        // reparented, but the explicit per-pid SIGKILL above is the important VM
        // boundary; this loop prevents direct-child zombies when the launcher is
        // still alive long enough to collect them.
        loop {
            let mut st: c_int = 0;
            let r = libc::waitpid(-1, &mut st, libc::WNOHANG);
            if r <= 0 {
                break;
            }
            reap_dead_pid(r);
        }
    }
}

/// Does `target` (a real host pid) belong to THIS sandbox? True iff it occupies a
/// ring slot — i.e. it's the main cell or a forked/cloned child of this sandbox
/// tree (the supervisor seeds slot 0 with the main cell's pid and stamps each
/// fork/clone child's slot with its pid; see `Ring.pid`). This is the SAME
/// ownership test `service()`'s SYS_KILL arm applies, but readable CELL-SIDE: the
/// ring table lives in the SHARED mapping, so the cell sees every slot's pid. Used
/// by the cell-local setpgid/getpgid/getsid arms to refuse re-homing or probing a
/// host / supervisor / other-tenant process group. A `target <= 0` (self / pgrp /
/// broadcast) is NOT "owned" here — callers handle the self case explicitly.
fn pid_in_sandbox(target: i32) -> bool {
    if target <= 0 {
        return false;
    }
    if proctree::vpid_for(target).is_some() {
        return true;
    }
    (0..MAX_SLOTS as u64).any(|i| unsafe {
        std::ptr::read_volatile(std::ptr::addr_of!((*ring_at(i)).pid)) == target
    })
}

fn interrupt_slots_for_pid(target: i32) {
    if target <= 0 {
        return;
    }
    unsafe {
        let one: u64 = 1;
        for i in 0..MAX_SLOTS as u64 {
            let r = ring_at(i);
            if std::ptr::read_volatile(std::ptr::addr_of!((*r).pid)) != target {
                continue;
            }
            let efd = std::ptr::read_volatile(std::ptr::addr_of!((*r).intr_efd));
            if efd >= 0 {
                host(
                    SYS_WRITE,
                    efd as u64,
                    std::ptr::addr_of!(one) as u64,
                    8,
                    0,
                    0,
                    0,
                );
                std::ptr::write_volatile(std::ptr::addr_of_mut!((*r).intr_pending), 1);
            }
        }
    }
}

#[cfg(all(target_os = "linux", target_arch = "x86_64"))]
fn slots_quiescent_for_pid(target: i32) -> bool {
    if target <= 0 {
        return true;
    }
    for i in 0..MAX_SLOTS as u64 {
        let r = ring_at(i);
        unsafe {
            if std::ptr::read_volatile(std::ptr::addr_of!((*r).pid)) != target {
                continue;
            }
            let req = ring_word(std::ptr::addr_of_mut!((*r).request)).load(Ordering::Acquire);
            let resp = ring_word(std::ptr::addr_of_mut!((*r).response)).load(Ordering::Acquire);
            if req != resp {
                return false;
            }
        }
    }
    true
}

#[cfg(all(target_os = "linux", target_arch = "x86_64"))]
fn launcher_quiesce_live_targets(targets: &[LauncherCaptureTarget]) -> std::io::Result<()> {
    for _ in 0..3 {
        for target in targets {
            if !slots_quiescent_for_pid(target.host_pid) {
                interrupt_slots_for_pid(target.host_pid);
            }
        }
        let ts = [0i64, 1_000_000]; // 1ms
        unsafe {
            host(SYS_NANOSLEEP, ts.as_ptr() as u64, 0, 0, 0, 0, 0);
        }
    }
    Ok(())
}

fn read_task_tids(tgid: i32, out: &mut Vec<i32>) -> bool {
    out.clear();
    if tgid <= 0 {
        return false;
    }
    let mut nb = [0u8; 24];
    let i = fmt_i64(tgid as i64, &mut nb);
    let mut path = Vec::with_capacity(32);
    path.extend_from_slice(b"/proc/");
    path.extend_from_slice(&nb[i..]);
    path.extend_from_slice(b"/task\0");
    const O_RDONLY: u64 = 0;
    const O_DIRECTORY: u64 = 0o200000;
    const AT_FDCWD: u64 = (-100i64) as u64;
    let fd = unsafe {
        host(
            SYS_OPENAT,
            AT_FDCWD,
            path.as_ptr() as u64,
            O_RDONLY | O_DIRECTORY,
            0,
            0,
            0,
        )
    };
    if fd < 0 {
        return false;
    }
    let mut buf = [0u8; 8192];
    loop {
        let n = unsafe {
            host(
                SYS_GETDENTS64,
                fd as u64,
                buf.as_mut_ptr() as u64,
                buf.len() as u64,
                0,
                0,
                0,
            )
        };
        if n <= 0 {
            break;
        }
        let mut off = 0usize;
        while off + 19 < n as usize {
            let reclen = u16::from_ne_bytes([buf[off + 16], buf[off + 17]]) as usize;
            if reclen == 0 {
                break;
            }
            let mut tid: i64 = 0;
            let mut valid = false;
            let mut p = off + 19;
            while p < n as usize {
                let c = buf[p];
                if c == 0 {
                    break;
                }
                if !c.is_ascii_digit() {
                    valid = false;
                    break;
                }
                tid = tid * 10 + (c - b'0') as i64;
                valid = true;
                p += 1;
            }
            if valid && tid > 0 && tid <= i32::MAX as i64 {
                out.push(tid as i32);
            }
            off += reclen;
        }
    }
    unsafe { host(SYS_CLOSE, fd as u64, 0, 0, 0, 0, 0) };
    true
}

unsafe fn clear_or_free_thread_slot(slot: u64) {
    if slot == 0 {
        clear_ring_owner(0);
        std::ptr::write_volatile(
            std::ptr::addr_of_mut!((*ring_at(0)).cancel_mode),
            CANCEL_REAP_PROCESS,
        );
    } else {
        free_slot(slot as u32);
    }
}

fn reclaim_dethread_slots(tgid: i32, keep_slot: u64, keep_tid: i32) {
    if tgid <= 0 {
        return;
    }
    for i in 0..MAX_SLOTS as u64 {
        let r = ring_at(i);
        unsafe {
            if std::ptr::read_volatile(std::ptr::addr_of!((*r).pid)) != tgid {
                continue;
            }
            let tid = std::ptr::read_volatile(std::ptr::addr_of!((*r).tid));
            if i == keep_slot || (keep_tid > 0 && tid == keep_tid) {
                continue;
            }
            let req = ring_word(std::ptr::addr_of_mut!((*r).request)).load(Ordering::Acquire);
            let resp = ring_word(std::ptr::addr_of_mut!((*r).response)).load(Ordering::Acquire);
            if req == resp {
                clear_or_free_thread_slot(i);
                continue;
            }
            let kind = std::ptr::read_volatile(std::ptr::addr_of!((*r).kind));
            let efd = std::ptr::read_volatile(std::ptr::addr_of!((*r).cancel_efd));
            if (kind != RING_PROCESS && kind != RING_DELEG) || efd < 0 {
                continue;
            }
            std::ptr::write_volatile(std::ptr::addr_of_mut!((*r).cancel_mode), CANCEL_SLOT_ONLY);
            let one: u64 = 1;
            host(
                SYS_WRITE,
                efd as u64,
                std::ptr::addr_of!(one) as u64,
                8,
                0,
                0,
                0,
            );
            std::ptr::write_volatile(std::ptr::addr_of_mut!((*r).cancel_pending), 1);
            for _ in 0..64 {
                if i != 0 && !slot_is_set(i) {
                    break;
                }
                let rq = ring_word(std::ptr::addr_of_mut!((*r).request)).load(Ordering::Acquire);
                let rp = ring_word(std::ptr::addr_of_mut!((*r).response)).load(Ordering::Acquire);
                if rq == rp {
                    clear_or_free_thread_slot(i);
                    break;
                }
                let ts = [0i64, 1_000_000]; // 1ms
                host(SYS_NANOSLEEP, ts.as_ptr() as u64, 0, 0, 0, 0, 0);
            }
        }
    }
}

fn supervisor_dethread_for_exec(keep_slot: u64, keep_tid: i32, tgid: i32) -> i64 {
    if keep_slot as usize >= MAX_SLOTS || keep_tid <= 0 || tgid <= 0 {
        return -22; // -EINVAL
    }
    let mut tids = Vec::new();
    for _round in 0..200 {
        if !read_task_tids(tgid, &mut tids) {
            return -3; // -ESRCH
        }
        let mut remaining = false;
        for &tid in &tids {
            if tid == keep_tid {
                continue;
            }
            remaining = true;
            unsafe {
                host(
                    SYS_TGKILL,
                    tgid as u64,
                    tid as u64,
                    SIG_DETHREAD as u64,
                    0,
                    0,
                    0,
                );
            }
        }
        if !remaining {
            reclaim_dethread_slots(tgid, keep_slot, keep_tid);
            return 0;
        }
        let ts = [0i64, 100_000]; // 100us
        unsafe { host(SYS_NANOSLEEP, ts.as_ptr() as u64, 0, 0, 0, 0, 0) };
    }
    -11 // -EAGAIN: sibling did not quiesce promptly
}

// ─── rootfs confinement (the VFS data plane) ─────────────────────────────────
//
// When `--root <dir>` is given, the supervisor confines EVERY guest path to that
// rootfs with `openat2(rootfd, path, RESOLVE_IN_ROOT)` — the kernel treats the
// dirfd as `/`, so absolute paths and `..` are clamped and symlinks resolve
// within the image. The cell therefore sees ONLY its rootfs, never the host fs —
// the same guarantee the ext4 VFS gives in production. fd-based ops (read/write/
// fstat/…) already operate on fds we opened in-root, so they need no change.
const SYS_OPENAT2: i64 = 437;
const SYS_FSTATFS: i64 = 138;
const O_PATH: u64 = 0x20_0000;
const O_NOFOLLOW: u64 = 0x2_0000;
const O_DIRECTORY: u64 = 0x1_0000;
const AT_EMPTY_PATH: u64 = 0x1000;
const RESOLVE_IN_ROOT: u64 = 0x10;
static ROOT_FD: std::sync::atomic::AtomicI32 = std::sync::atomic::AtomicI32::new(-1);
/// The host path of the rootfs dir (set alongside `ROOT_FD`). Used to strip the
/// host prefix off a `/proc/self/fd` readlink so `chdir` can store the guest-
/// absolute, symlink-canonical cwd.
fn root_path() -> &'static Mutex<Vec<u8>> {
    static P: OnceLock<Mutex<Vec<u8>>> = OnceLock::new();
    P.get_or_init(|| Mutex::new(Vec::new()))
}

// ─── bind-mounts / volumes ───────────────────────────────────────────────────
//
// A mount maps a guest path PREFIX (e.g. `/data`) to a host directory, served by
// the same `openat2(RESOLVE_IN_ROOT)` confinement as the rootfs — just with the
// mount's dirfd as the root. So a volume is a writable host dir the guest sees at
// a chosen path, and a read-only mount rejects writes. Each mount is its own
// confined root: `..` is clamped within it (a chroot-like boundary), never
// escaping to the host fs. Set in `sandbox_main` (the supervisor) before the
// cell fork; the cell never holds these fds.
struct Mount {
    guest_prefix: Vec<u8>, // e.g. b"/data" (no trailing slash, except b"/" is invalid)
    dirfd: i32,
    host_canonical: Vec<u8>, // canonical host path (for chdir reverse-mapping)
    readonly: bool,
}
fn mounts() -> &'static Mutex<Vec<Mount>> {
    static M: OnceLock<Mutex<Vec<Mount>>> = OnceLock::new();
    M.get_or_init(|| Mutex::new(Vec::new()))
}
/// Does `guest_prefix` cover absolute guest path `p` (component-aware: `/data`
/// matches `/data` and `/data/x`, NOT `/database`)?
fn prefix_covers(prefix: &[u8], p: &[u8]) -> bool {
    p.len() >= prefix.len()
        && &p[..prefix.len()] == prefix
        && (p.len() == prefix.len() || p[prefix.len()] == b'/')
}
/// Pick the dirfd + remainder + read-only flag for guest-absolute path `p`: the
/// longest matching mount, else the rootfs. The remainder is what's passed to
/// `openat2(dirfd, …, RESOLVE_IN_ROOT)` (leading `/` is fine — it's reinterpreted
/// as the dirfd). Returns `(dirfd, remainder_bytes_with_nul, readonly)`.
fn resolve_mount(p: &[u8]) -> (i32, Vec<u8>, bool) {
    let root = ROOT_FD.load(Ordering::Relaxed);
    let mt = mounts().lock().unwrap();
    let mut best: Option<&Mount> = None;
    for m in mt.iter() {
        if prefix_covers(&m.guest_prefix, p)
            && best.map_or(true, |b| m.guest_prefix.len() > b.guest_prefix.len())
        {
            best = Some(m);
        }
    }
    match best {
        Some(m) => {
            let rest = &p[m.guest_prefix.len()..]; // "" or "/sub/..."
            let mut r = if rest.is_empty() {
                b"/".to_vec()
            } else {
                rest.to_vec()
            };
            r.push(0);
            (m.dirfd, r, m.readonly)
        }
        None => {
            let mut r = p.to_vec();
            r.push(0);
            (root, r, false)
        }
    }
}
/// Map a guest-absolute path to its canonical HOST path (the inverse of
/// `host_to_guest_path`): a path under a bind-mount → that mount's host root +
/// remainder; otherwise the rootfs prefix + the path. `None` if no rootfs is set
/// (legacy spike mode → the guest path IS the host path, returned as-is).
fn guest_to_host_path(gpath: &[u8]) -> Vec<u8> {
    {
        let mt = mounts().lock().unwrap();
        for m in mt.iter() {
            if prefix_covers(&m.guest_prefix, gpath) {
                let rest = &gpath[m.guest_prefix.len()..];
                let mut h = m.host_canonical.clone();
                h.extend_from_slice(rest);
                return h;
            }
        }
    }
    let root = root_path().lock().unwrap();
    if root.is_empty() {
        return gpath.to_vec();
    }
    let mut h = root.clone();
    h.extend_from_slice(gpath);
    h
}

/// Map a guest pathname socket target to a host pathname, resolving parent
/// symlinks inside the guest root. Unlike plain [`guest_to_host_path`], this
/// mirrors the kernel's chroot-style behavior for paths such as `/var/run/x`
/// where `/var/run` is an absolute symlink to `/run`: the resolved parent must
/// be `<rootfs>/run`, not the host's `/run`.
fn guest_to_host_resolved_parent_path(gpath: &[u8]) -> Option<Vec<u8>> {
    if gpath.first() != Some(&b'/') {
        return None;
    }
    if ROOT_FD.load(Ordering::Relaxed) < 0 {
        return Some(guest_to_host_path(gpath));
    }

    let mut nul = gpath.to_vec();
    nul.push(0);
    let (parent, base) = split_parent_base(&nul);
    let parent_bare = match parent.split_last() {
        Some((0, head)) => head,
        _ => parent.as_slice(),
    };
    let base_bare = match base.split_last() {
        Some((0, head)) => head,
        _ => base.as_slice(),
    };
    if base_bare.is_empty() || parent_bare.is_empty() {
        return None;
    }

    let (dirfd, resolved, _) = resolve_mount(parent_bare);
    if dirfd < 0 {
        return None;
    }
    let how = OpenHow {
        flags: O_PATH | O_DIRECTORY,
        mode: 0,
        resolve: RESOLVE_IN_ROOT,
    };
    let pfd = unsafe {
        host(
            SYS_OPENAT2,
            dirfd as u64,
            resolved.as_ptr() as u64,
            &how as *const _ as u64,
            std::mem::size_of::<OpenHow>() as u64,
            0,
            0,
        )
    };
    if pfd < 0 {
        return None;
    }
    let mut host_parent = readlink_fd(pfd as i32)?;
    unsafe {
        host(SYS_CLOSE, pfd as u64, 0, 0, 0, 0, 0);
    }
    if host_parent.last() != Some(&b'/') {
        host_parent.push(b'/');
    }
    host_parent.extend_from_slice(base_bare);
    Some(host_parent)
}
/// For an AF_UNIX `sockaddr_un` with a PATHNAME `sun_path` (filesystem socket, as
/// used by Chromium's ProcessSingleton), rewrite the path to its confined HOST
/// path so the SUPERVISOR (which does the bind/connect, and is NOT chroot'd into
/// the rootfs) targets the same node the cell created inside the rootfs. Abstract
/// (leading-NUL) and relative sun_paths, and non-AF_UNIX addrs, are left as-is.
/// `None` ⇒ no rewrite needed/possible (caller uses the original addr).
fn unix_socket_aliases() -> &'static Mutex<HashMap<Vec<u8>, Vec<u8>>> {
    static A: OnceLock<Mutex<HashMap<Vec<u8>, Vec<u8>>>> = OnceLock::new();
    A.get_or_init(|| Mutex::new(HashMap::new()))
}

fn unix_socket_alias(host_path: &[u8]) -> Option<Vec<u8>> {
    use std::hash::{Hash, Hasher};
    use std::os::unix::ffi::OsStrExt;

    let mut aliases = unix_socket_aliases().lock().unwrap();
    if let Some(alias) = aliases.get(host_path) {
        return Some(alias.clone());
    }
    let mut hasher = std::collections::hash_map::DefaultHasher::new();
    host_path.hash(&mut hasher);
    let dir = std::env::temp_dir().join(format!("supermachine-unix-{}", unsafe {
        host(SYS_GETPID, 0, 0, 0, 0, 0, 0)
    }));
    std::fs::create_dir_all(&dir).ok()?;
    let alias = dir.join(format!("{:016x}.sock", hasher.finish()));
    let bytes = alias.as_os_str().as_bytes().to_vec();
    if bytes.len() + 1 > 108 {
        return None;
    }
    aliases.insert(host_path.to_vec(), bytes.clone());
    Some(bytes)
}

fn unix_socket_alias_for_guest_path(path: &[u8]) -> Option<Vec<u8>> {
    let bare = match path.split_last() {
        Some((0, head)) => head,
        _ => path,
    };
    let host = guest_to_host_resolved_parent_path(bare).unwrap_or_else(|| guest_to_host_path(bare));
    unix_socket_aliases().lock().unwrap().get(&host).cloned()
}

fn rewrite_unix_sun_path(addr: &[u8]) -> Option<Vec<u8>> {
    if addr.len() < 3 {
        return None;
    }
    if u16::from_le_bytes([addr[0], addr[1]]) != libc::AF_UNIX as u16 {
        return None;
    }
    let path = &addr[2..];
    if path.first() == Some(&0) {
        return None; // abstract namespace — no filesystem node
    }
    let end = path.iter().position(|&c| c == 0).unwrap_or(path.len());
    let gpath = &path[..end];
    if gpath.first() != Some(&b'/') {
        return None; // relative / empty — leave untouched
    }
    let mut host = guest_to_host_resolved_parent_path(gpath)?;
    if host.len() + 1 > 108 {
        host = unix_socket_alias(&host)?;
    }
    let mut out = vec![0u8; 2 + host.len() + 1];
    out[0..2].copy_from_slice(&(libc::AF_UNIX as u16).to_le_bytes());
    out[2..2 + host.len()].copy_from_slice(&host);
    Some(out)
}

fn unix_sockaddr_host_path(addr: &[u8]) -> Option<Vec<u8>> {
    if addr.len() < 3 {
        return None;
    }
    if u16::from_le_bytes([addr[0], addr[1]]) != libc::AF_UNIX as u16 {
        return None;
    }
    let path = &addr[2..];
    if path.first() == Some(&0) {
        return None;
    }
    let end = path.iter().position(|&c| c == 0).unwrap_or(path.len());
    let gpath = &path[..end];
    if gpath.first() != Some(&b'/') {
        return None;
    }
    guest_to_host_resolved_parent_path(gpath)
}

fn unix_sockaddr_path(addr: &[u8]) -> Option<&[u8]> {
    if addr.len() < 3 {
        return None;
    }
    if u16::from_le_bytes([addr[0], addr[1]]) != libc::AF_UNIX as u16 {
        return None;
    }
    let path = &addr[2..];
    if path.first() == Some(&0) {
        return None;
    }
    let end = path.iter().position(|&c| c == 0).unwrap_or(path.len());
    Some(&path[..end])
}

fn publish_unix_socket_alias_placeholder(host_path: &[u8]) {
    let Ok(c) = std::ffi::CString::new(host_path) else {
        return;
    };
    unsafe {
        libc::unlink(c.as_ptr());
        if libc::mknod(c.as_ptr(), libc::S_IFSOCK | 0o777, 0) != 0 {
            let fd = libc::open(
                c.as_ptr(),
                libc::O_CREAT | libc::O_EXCL | libc::O_RDONLY,
                0o666,
            );
            if fd >= 0 {
                libc::close(fd);
            }
        }
    }
}

fn unix_alias_placeholders() -> &'static Mutex<HashMap<i32, Vec<Vec<u8>>>> {
    static A: OnceLock<Mutex<HashMap<i32, Vec<Vec<u8>>>>> = OnceLock::new();
    A.get_or_init(|| Mutex::new(HashMap::new()))
}

fn unix_alias_placeholder_record(hfd: i32, host_path: &[u8]) {
    unix_alias_placeholders()
        .lock()
        .unwrap()
        .entry(hfd)
        .or_default()
        .push(host_path.to_vec());
}

fn unix_alias_placeholders_remove(hfd: i32) {
    let paths = unix_alias_placeholders().lock().unwrap().remove(&hfd);
    let Some(paths) = paths else {
        return;
    };
    for path in paths {
        let Ok(c) = std::ffi::CString::new(path) else {
            continue;
        };
        unsafe {
            libc::unlink(c.as_ptr());
        }
    }
}
// Write-intent open flags: reject these on a read-only mount.
const O_ACCMODE: u64 = 0o3;
const O_RDONLY: u64 = 0;
const O_WRONLY: u64 = 0o1;
const O_RDWR: u64 = 0o2;
const O_CREAT: u64 = 0o100;
const O_EXCL: u64 = 0o200;
const O_TRUNC: u64 = 0o1000;
const R_OK: u64 = 4;
const W_OK: u64 = 2;
const X_OK: u64 = 1;
const AT_SYMLINK_NOFOLLOW: u64 = 0x100;
const AT_EACCESS: u64 = 0x200;
fn is_write_open(flags: u64) -> bool {
    let acc = flags & O_ACCMODE;
    acc == O_WRONLY || acc == O_RDWR || (flags & (O_CREAT | O_TRUNC)) != 0
}

fn host_fstat_meta(fd: i32) -> Option<(u32, u32, u32)> {
    let mut st: libc::stat = unsafe { std::mem::zeroed() };
    let r = unsafe { host(SYS_FSTAT, fd as u64, &mut st as *mut _ as u64, 0, 0, 0, 0) };
    if r == 0 {
        Some((st.st_mode as u32, st.st_uid as u32, st.st_gid as u32))
    } else {
        None
    }
}

fn virtual_ids_for_access(pid: i32, use_effective: bool) -> (u32, u32) {
    let c = supervisor_cred_for(pid);
    if use_effective {
        (c.euid, c.egid)
    } else {
        (c.ruid, c.rgid)
    }
}

fn virtual_fs_ids(pid: i32) -> (u32, u32) {
    let c = supervisor_cred_for(pid);
    (c.fsuid, c.fsgid)
}

fn access_bits_allowed(mode: u32, st_uid: u32, st_gid: u32, uid: u32, gid: u32, req: u64) -> bool {
    if req == 0 {
        return true;
    }
    if uid == 0 {
        return (req & X_OK) == 0 || (mode & 0o111) != 0;
    }
    let shift = if uid == st_uid {
        6
    } else if gid == st_gid {
        3
    } else {
        0
    };
    let allowed = ((mode >> shift) & 0o7) as u64;
    (allowed & req) == req
}

fn faccess_fd(pid: i32, fd: i32, req: u64, use_effective: bool) -> i64 {
    if (req & !(R_OK | W_OK | X_OK)) != 0 {
        return -22; // -EINVAL
    }
    let Some((mode, st_uid, st_gid)) = host_fstat_meta(fd) else {
        return -9; // -EBADF
    };
    let (uid, gid) = virtual_ids_for_access(pid, use_effective);
    if access_bits_allowed(mode, st_uid, st_gid, uid, gid, req) {
        0
    } else {
        -13 // -EACCES
    }
}

fn chown_created_fd_to_virtual(pid: i32, fd: i32) {
    let (uid, gid) = virtual_fs_ids(pid);
    if uid == 0 && gid == 0 {
        return;
    }
    unsafe {
        host(SYS_FCHOWN, fd as u64, uid as u64, gid as u64, 0, 0, 0);
    }
}

fn chown_created_at_to_virtual(pid: i32, pfd: i32, base: &[u8], flags: u64) {
    let (uid, gid) = virtual_fs_ids(pid);
    if uid == 0 && gid == 0 {
        return;
    }
    unsafe {
        host(
            SYS_FCHOWNAT,
            pfd as u64,
            base.as_ptr() as u64,
            uid as u64,
            gid as u64,
            flags,
            0,
        );
    }
}

// ─── cgroup-v2 resource limits (guest is bounded as a unit) ──────────────────
const CGROUP_BASE: &str = "/sys/fs/cgroup";
/// The cell's cgroup dir, set by `sandbox_main` (supervisor) before the cell
/// fork and read by `cell_main` (which joins it pre-seal). Inherited via CoW.
fn cgroup_path() -> &'static Mutex<Option<std::path::PathBuf>> {
    static P: OnceLock<Mutex<Option<std::path::PathBuf>>> = OnceLock::new();
    P.get_or_init(|| Mutex::new(None))
}
/// The cell's resource caps, stashed by `setup_sandbox_env` from the `SandboxCfg`
/// (CoW-inherited by the cell; readable from the supervisor's servicer threads).
/// Read by the cgroup-aware `/proc` synthesizers (`proc_synth`/`open_proc_self`)
/// so `/proc/meminfo`, `/proc/cpuinfo` and friends reflect the cell's `memory.max`
/// / `cpu.max` instead of the host's totals — the lxcfs-equivalent fidelity a
/// container runtime relies on to size its worker/thread pools. Empty when no
/// caps are set (the synthesizers then fall back to the host's real `/proc`).
fn cell_limits() -> &'static Mutex<Limits> {
    static L: OnceLock<Mutex<Limits>> = OnceLock::new();
    L.get_or_init(|| Mutex::new(Limits::default()))
}
/// `(uid, gid)` the cell drops to before sealing, set by `sandbox_main` from the
/// `SandboxCfg` before the cell fork (CoW-inherited by the cell). `None` = no
/// drop (the guest runs as the sentry's own uid).
fn run_uid() -> &'static Mutex<Option<(u32, u32)>> {
    static U: OnceLock<Mutex<Option<(u32, u32)>>> = OnceLock::new();
    U.get_or_init(|| Mutex::new(None))
}
/// The guest environment (`KEY=VALUE` byte strings) + initial cwd, set by
/// `setup_sandbox_env` from the `SandboxCfg` before the cell fork (CoW-inherited).
/// Empty env = `cell_main` uses the built-in default (`PATH`/`PWD`/`LANG`).
fn guest_env() -> &'static Mutex<Vec<Vec<u8>>> {
    static E: OnceLock<Mutex<Vec<Vec<u8>>>> = OnceLock::new();
    E.get_or_init(|| Mutex::new(Vec::new()))
}
fn guest_cwd_init() -> &'static Mutex<Option<Vec<u8>>> {
    static C: OnceLock<Mutex<Option<Vec<u8>>>> = OnceLock::new();
    C.get_or_init(|| Mutex::new(None))
}
/// Ports a netns sandbox PUBLISHES: a guest `bind` to one of these is re-homed
/// into the host netns so the host can reach the listener (the rest stay isolated
/// in the sandbox netns). Set once in `setup_sandbox_env`.
fn published_ports() -> &'static Mutex<Vec<u16>> {
    static P: OnceLock<Mutex<Vec<u16>>> = OnceLock::new();
    P.get_or_init(|| Mutex::new(Vec::new()))
}
/// Does the host have a routable IPv6 default route? Reuses the shared probe
/// (`crate::utils::net::host_ipv6_route`): the `SUPERMACHINE_HOST_IPV6` env override
/// is honored first, else an AF_INET6 connect to a global anycast addr — the result
/// of which is cached process-wide (probed ONCE), so this is a cheap deref on the
/// hot path. Sharing the exact muxer/worker gate keeps the no-virt backend's v6
/// behavior identical to the virtio-vsock backend's. The cache is primed in
/// `setup_sandbox_env` BEFORE any netns unshare so it reflects the HOST route (the
/// route egress is re-homed to), not the route-less sandbox netns.
///
/// When this is `false`, the cell's `AF_INET6` egress is defused two ways: the
/// `SYS_SOCKET` arm refuses `AF_INET6` (musl/glibc fall back to A/v4), and the DNS
/// reply path rewrites AAAA answers to NODATA — so a dual-stack name lookup can't
/// hang a non-happy-eyeballs client (alpine `apk`, busybox `wget`) on a v6 address
/// the host can never reach.
fn host_v6_route() -> bool {
    crate::utils::net::host_ipv6_route()
}

fn resolv_nameserver_from(path: &str, allow_loopback: bool) -> Option<String> {
    let text = std::fs::read_to_string(path).ok()?;
    text.lines()
        .map(str::trim)
        .filter_map(|line| line.strip_prefix("nameserver "))
        .map(str::trim)
        .find(|ns| {
            if ns.is_empty() {
                return false;
            }
            if allow_loopback {
                return true;
            }
            ns.parse::<std::net::IpAddr>()
                .map(|ip| !(ip.is_loopback() || ip.is_unspecified()))
                .unwrap_or(true)
        })
        .map(str::to_string)
}

fn host_resolv_nameserver() -> String {
    // systemd-resolved commonly makes /etc/resolv.conf point at 127.0.0.53.
    // A sentry sandbox runs in its own netns by default, so that host-loopback
    // stub is unreachable. Prefer resolved's upstream file and skip loopback
    // stubs before falling back to the traditional path.
    resolv_nameserver_from("/run/systemd/resolve/resolv.conf", false)
        .or_else(|| resolv_nameserver_from("/etc/resolv.conf", false))
        .or_else(|| resolv_nameserver_from("/etc/resolv.conf", true))
        .unwrap_or_else(|| "127.0.0.53".to_string())
}
/// Per-host-fd record of "this socket's last connect/sendto destination was UDP
/// port 53" — i.e. it carries DNS. Set in the connect/sendto/sendmsg arms, read in
/// the recvfrom/recvmsg arms to gate the AAAA→NODATA rewrite, cleared on close.
/// Keyed by the HOST fd (stable across the guest's virtual fd table; servicers are
/// process-wide). Only populated when the host lacks a v6 route, so it stays empty
/// on the common (v6-capable) path.
fn dns_dest_fds() -> &'static Mutex<std::collections::HashSet<i32>> {
    static D: OnceLock<Mutex<std::collections::HashSet<i32>>> = OnceLock::new();
    D.get_or_init(|| Mutex::new(std::collections::HashSet::new()))
}
/// Note (or clear) whether host fd `hfd` is currently a DNS (port 53) flow, based
/// on the destination `sa` of a connect/sendto/sendmsg. No-op when the host has a
/// v6 route (the rewrite never engages, so we needn't track). A stub resolver
/// reuses one socket across A + AAAA queries to the same server, so a port-53
/// destination latches until the fd is closed or aimed elsewhere.
fn note_dns_dest(hfd: i32, sa: &std::net::SocketAddr) {
    if host_v6_route() {
        return;
    }
    let mut set = dns_dest_fds().lock().unwrap();
    if sa.port() == 53 {
        set.insert(hfd);
    } else {
        set.remove(&hfd);
    }
}
/// True if host fd `hfd` was last pointed at port 53 (DNS) on a v6-less host.
fn is_dns_dest_fd(hfd: i32) -> bool {
    if host_v6_route() {
        return false;
    }
    dns_dest_fds().lock().unwrap().contains(&hfd)
}
/// On a v6-less host, rewrite a received DNS reply in `buf[..n]` so AAAA answers
/// become NODATA. `n` is the byte count the host `recv*` returned; returns the new
/// length (unchanged unless an AAAA response was actually rewritten). Mirrors the
/// muxer's UDP-relay gate ([`crate::devices::virtio::vsock::dns_filter`]): the
/// rewrite only ever shrinks the buffer, and non-AAAA / non-DNS / malformed
/// payloads pass through verbatim — it never turns working traffic into broken
/// traffic.
fn maybe_strip_aaaa(hfd: i32, buf: &mut [u8], n: i64) -> i64 {
    if n <= 0 || !is_dns_dest_fd(hfd) {
        return n;
    }
    let got = n as usize;
    if got > buf.len() {
        return n;
    }
    match crate::devices::virtio::vsock::dns_filter::rewrite_aaaa_response_to_nodata(&buf[..got]) {
        Some(rw) => {
            let m = rw.len().min(buf.len());
            buf[..m].copy_from_slice(&rw[..m]);
            m as i64
        }
        None => n,
    }
}
/// If `addr` is an `AF_INET6` sockaddr carrying an IPv4-mapped destination
/// (`::ffff:a.b.c.d`), rewrite it IN PLACE into a native `AF_INET` (`sockaddr_in`)
/// so v4-mapped egress works on a v4-only host — the same unwrap the muxer's
/// connect/sendto path does (`Ipv6Addr::to_ipv4_mapped`). Returns `Some(new_len)`
/// when it rewrote (the host syscall should be passed that shorter length); `None`
/// to send `addr` untouched. The 16-byte `sockaddr_in` result always fits — it's
/// written over the front of the (≥24-byte) `sockaddr_in6` buffer in place, and
/// the caller passes the shorter length so the kernel never reads the stale tail.
///
/// Layout (x86_64, little-endian `sa_family`): `sockaddr_in` = family(2) port(2,BE)
/// addr(4) zero(8); `sockaddr_in6` = family(2) port(2,BE) flowinfo(4) addr(16)
/// scope(4).
fn unwrap_v4mapped_sockaddr(addr: &mut [u8]) -> Option<usize> {
    if addr.len() < 24 {
        return None;
    }
    if u16::from_ne_bytes([addr[0], addr[1]]) != 10 {
        return None; // not AF_INET6
    }
    let mut o = [0u8; 16];
    o.copy_from_slice(&addr[8..24]);
    let v4 = std::net::Ipv6Addr::from(o).to_ipv4_mapped()?;
    let octets = v4.octets();
    // Rewrite as sockaddr_in: family=AF_INET(2), keep the BE port at [2..4].
    addr[0..2].copy_from_slice(&2u16.to_ne_bytes());
    addr[4..8].copy_from_slice(&octets);
    for b in &mut addr[8..16] {
        *b = 0;
    }
    Some(16)
}

fn log_connect_debug(pid: i32, hfd: i32, addr: &[u8], ret: i64) {
    let sa = match parse_connect_addr(addr) {
        Some(sa) if ret < 0 || sa.port() == 443 => sa,
        _ => return,
    };
    let mut line = Vec::with_capacity(160);
    line.extend_from_slice(b"CONNDBG pid=");
    let mut nb = [0u8; 24];
    let i = fmt_i64(pid as i64, &mut nb);
    line.extend_from_slice(&nb[i..]);
    line.extend_from_slice(b" hfd=");
    let i = fmt_i64(hfd as i64, &mut nb);
    line.extend_from_slice(&nb[i..]);
    line.extend_from_slice(b" ret=");
    let i = fmt_i64(ret, &mut nb);
    line.extend_from_slice(&nb[i..]);
    line.extend_from_slice(b" addr=");
    line.extend_from_slice(sa.to_string().as_bytes());
    ipc_logf_raw(&line);
}
/// Create `/sys/fs/cgroup/sentry-<pid>` and write the requested caps. The dir is
/// named by the supervisor pid so the controller can `rmdir` it on teardown.
/// Returns the path (for the cell to join), or `None` if cgroup v2 is
/// unavailable / not writable (best-effort — the sandbox still runs uncapped).
fn setup_cgroup(name: &str, limits: &Limits, clear_stale: bool) -> Option<std::path::PathBuf> {
    let base = std::path::Path::new(CGROUP_BASE);
    if !base.join("cgroup.controllers").is_file() {
        return None; // not cgroup v2
    }
    let dir = base.join(name);
    if clear_stale {
        // Per-call (sentry-<pid>): clear a stale dir from a recycled pid. A
        // SHARED per-Sandbox cgroup skips this — removing a sibling exec's
        // freshly-created empty dir would race; the Sandbox owns its lifetime.
        let _ = std::fs::remove_dir(&dir);
    }
    match std::fs::create_dir(&dir) {
        Ok(()) => {}
        // A SHARED per-Sandbox cgroup already created by a concurrent exec: join
        // it (limit writes below are idempotent). rmdir above fails EBUSY while
        // it's in use, so this is the normal concurrent path.
        Err(e) if e.kind() == std::io::ErrorKind::AlreadyExists => {}
        Err(_) => return None, // no delegation / not writable
    }
    if let Some(m) = limits.memory_max {
        let _ = std::fs::write(dir.join("memory.max"), m.to_string());
        // A HARD memory cap: deny swap so the guest can't escape `memory.max`
        // into swap (which would thrash, not bound). Without this the cap is
        // "RAM + swap" and an over-limit guest hangs instead of being OOM-killed.
        let _ = std::fs::write(dir.join("memory.swap.max"), "0");
    }
    if let Some(p) = limits.pids_max {
        let _ = std::fs::write(dir.join("pids.max"), p.to_string());
    }
    if let Some(c) = &limits.cpu_max {
        let _ = std::fs::write(dir.join("cpu.max"), c);
    }
    Some(dir)
}
/// Remove the sandbox's cgroup (named by its supervisor pid). Best-effort with a
/// few retries: under `killpg` teardown the guest procs may take a beat to die,
/// leaving the cgroup briefly non-empty (`rmdir` → EBUSY). A lingering empty
/// cgroup is harmless; the next sandbox with the same pid clears it first.
fn remove_cgroup(pid: i32) {
    remove_cgroup_named(&format!("sentry-{pid}"));
}
/// Remove a cgroup dir by name (best-effort, retries for the killpg race).
fn remove_cgroup_named(name: &str) {
    let dir = std::path::Path::new(CGROUP_BASE).join(name);
    for _ in 0..5 {
        if std::fs::remove_dir(&dir).is_ok() || !dir.exists() {
            return;
        }
        std::thread::sleep(std::time::Duration::from_millis(2));
    }
}

#[repr(C)]
struct OpenHow {
    flags: u64,
    mode: u64,
    resolve: u64,
}

fn pull_open_how(pid: i32, remote: u64, size: u64) -> Result<OpenHow, i64> {
    if remote == 0 || size < std::mem::size_of::<OpenHow>() as u64 {
        return Err(-22); // -EINVAL
    }
    let mut buf = [0u8; std::mem::size_of::<OpenHow>()];
    if vm_read(pid, remote, &mut buf) != buf.len() as i64 {
        return Err(-14); // -EFAULT
    }
    Ok(OpenHow {
        flags: u64::from_le_bytes(buf[0..8].try_into().unwrap()),
        mode: u64::from_le_bytes(buf[8..16].try_into().unwrap()),
        resolve: u64::from_le_bytes(buf[16..24].try_into().unwrap()),
    })
}

fn mode_for_open_flags(pid: i32, flags: u64, mode: u64) -> u64 {
    if (flags & O_CREAT) != 0 {
        apply_umask(pid, mode & 0o7777)
    } else {
        0
    }
}
/// The standard container device set: a guest device path → the host device to
/// open for it. OCI rootfs images ship no device nodes (a runtime provides
/// them), so the supervisor exposes these directly. All are universally safe to
/// expose (random sources / sinks) — opening the host node reuses the normal
/// fd/read/write mediation, so no per-device read/write emulation is needed.
/// Returns a NUL-terminated host path. (`/dev/tty` is deliberately NOT here —
/// host terminal access from a sandbox is a leak.)
fn host_device_for(p: &[u8]) -> Option<&'static [u8]> {
    Some(match p {
        b"/dev/null" => b"/dev/null\0",
        b"/dev/zero" => b"/dev/zero\0",
        b"/dev/full" => b"/dev/full\0",
        b"/dev/random" => b"/dev/random\0",
        b"/dev/urandom" => b"/dev/urandom\0",
        _ => return None,
    })
}

/// `/dev/fd` is the standard symlink to `/proc/self/fd`. Map a guest `/dev/fd`
/// path to the `/proc/self/fd` RELATIVE form that [`open_proc_self`] consumes:
/// `/dev/fd` → `fd`, `/dev/fd/<n>` → `fd/<n>`. `None` for anything else (so a
/// plain `/dev` listing or other `/dev/*` path falls through to the rootfs).
fn dev_fd_rel(bare: &[u8]) -> Option<Vec<u8>> {
    if bare == b"/dev/fd" || bare == b"/dev/fd/" {
        return Some(b"fd".to_vec());
    }
    // /dev/stdin|stdout|stderr are the standard symlinks to /proc/self/fd/{0,1,2};
    // serving them through the same /proc/self/fd machinery makes `test -e`,
    // `command > /dev/stdout`, `bash <(cmd)`, and build scripts work as on a real
    // guest (init-oci / the KVM init create these symlinks; the sentry has no init).
    match bare {
        b"/dev/stdin" => return Some(b"fd/0".to_vec()),
        b"/dev/stdout" => return Some(b"fd/1".to_vec()),
        b"/dev/stderr" => return Some(b"fd/2".to_vec()),
        _ => {}
    }
    let n = bare.strip_prefix(b"/dev/fd/")?;
    if n.is_empty() || !n.iter().all(|c| c.is_ascii_digit()) {
        return None;
    }
    let mut rel = b"fd/".to_vec();
    rel.extend_from_slice(n);
    Some(rel)
}

// ─── cgroup-aware /proc synthesis (lxcfs-equivalent fidelity) ────────────────
//
// A container runtime sizes its worker/thread pools against what `/proc` reports
// (redis `io-threads`, python `os.cpu_count()`, node libuv, nginx `worker_processes
// auto`, the JVM). Plain `/proc/cpuinfo`/`/proc/meminfo` show the HOST totals, so a
// 64-core / 512 GiB host makes a cell that's capped at 0.5 core / 256 MiB spawn 64
// workers and pre-allocate against 512 GiB → thrash / OOM. We synthesize a view
// that reflects the cell's `cpu.max` / `memory.max` (the same fix lxcfs provides),
// served via a memfd of the synthesized text (`open_path`'s `proc_synth` arm).

/// The cell's effective online-CPU count derived from its `cpu.max` quota
/// (`"QUOTA PERIOD"`, both µs): `ceil(QUOTA / PERIOD)`, clamped to ≥1. `None` when
/// uncapped (`cpu.max` unset or literal `max`) — the caller then keeps the host's
/// real CPU count. Mirrors the kernel/runtime convention (the JVM, Go, .NET all
/// round the quota up to whole CPUs).
fn cgroup_cpu_count() -> Option<u64> {
    let l = cell_limits().lock().unwrap();
    let c = l.cpu_max.as_ref()?;
    let mut it = c.split_ascii_whitespace();
    let quota = it.next()?;
    if quota == "max" {
        return None; // explicitly uncapped
    }
    let quota: u64 = quota.parse().ok()?;
    let period: u64 = it.next().and_then(|p| p.parse().ok()).unwrap_or(100_000);
    if period == 0 {
        return None;
    }
    Some(((quota + period - 1) / period).max(1))
}

/// The cell's memory cap in bytes (`memory.max`), or `None` when uncapped. A
/// `memory.max` of `u64::MAX`-ish (the "no limit" sentinel some callers pass) is
/// treated as uncapped so we don't claim an absurd MemTotal.
fn cgroup_mem_bytes() -> Option<u64> {
    let l = cell_limits().lock().unwrap();
    match l.memory_max {
        Some(m) if m > 0 && m < (1u64 << 62) => Some(m),
        _ => None,
    }
}

/// Synthesize cgroup-aware `/proc/cpuinfo`: the host's first `processor` block
/// (model name, flags, …) repeated `n` times with renumbered `processor:` /
/// `core id:` lines, so a parser that counts `processor` entries sees the cap.
/// Falls back to the host file verbatim if its shape is unexpected.
fn synth_cpuinfo(n: u64) -> Vec<u8> {
    let host = std::fs::read("/proc/cpuinfo").unwrap_or_default();
    // Split into per-CPU blocks (separated by a blank line); use the first as the
    // template (all cores are homogeneous on the hosts we run on).
    let text = String::from_utf8_lossy(&host);
    let template = text.split("\n\n").next().unwrap_or("").trim_end();
    if template.is_empty() {
        return host;
    }
    let mut out = String::new();
    for i in 0..n {
        for line in template.lines() {
            if let Some(rest) = line.split_once(':') {
                let key = rest.0.trim_end();
                match key {
                    "processor" => {
                        out.push_str(&format!("processor\t: {i}\n"));
                        continue;
                    }
                    "core id" => {
                        out.push_str(&format!("core id\t\t: {i}\n"));
                        continue;
                    }
                    "apicid" | "initial apicid" => {
                        out.push_str(&format!("{key}\t\t: {i}\n"));
                        continue;
                    }
                    _ => {}
                }
            }
            out.push_str(line);
            out.push('\n');
        }
        out.push('\n');
    }
    out.into_bytes()
}

/// Synthesize cgroup-aware `/proc/stat`: keep the host's aggregate `cpu` line and
/// non-CPU counters, but expose only `cpu0..cpuN-1`. glibc's
/// `_SC_NPROCESSORS_ONLN` fallback counts these lines when sysfs CPU topology is
/// unavailable, so leaking the host's `/proc/stat` makes `getconf` report the host
/// CPU count even when `/proc/cpuinfo` and `sched_getaffinity` are capped.
fn synth_stat(n: u64) -> Vec<u8> {
    let host = std::fs::read("/proc/stat").unwrap_or_default();
    let text = String::from_utf8_lossy(&host);
    let mut out = String::new();
    let mut saw_cpu = false;
    let mut emitted_cpus = 0u64;

    for line in text.lines() {
        if line.starts_with("cpu ") {
            saw_cpu = true;
            out.push_str(line);
            out.push('\n');
            continue;
        }
        if let Some(rest) = line.strip_prefix("cpu") {
            let is_cpu_line = rest
                .as_bytes()
                .first()
                .map(|b| b.is_ascii_digit())
                .unwrap_or(false);
            if is_cpu_line {
                if emitted_cpus < n {
                    out.push_str(&format!("cpu{emitted_cpus}"));
                    if let Some(fields) = rest
                        .trim_start_matches(|c: char| c.is_ascii_digit())
                        .strip_prefix(' ')
                    {
                        out.push(' ');
                        out.push_str(fields);
                    }
                    out.push('\n');
                    emitted_cpus += 1;
                }
                continue;
            }
        }
        out.push_str(line);
        out.push('\n');
    }

    if saw_cpu && emitted_cpus == n {
        out.into_bytes()
    } else {
        host
    }
}

/// Synthesize cgroup-aware `/proc/meminfo`: start from the host's file and rewrite
/// `MemTotal`/`MemFree`/`MemAvailable`/`SwapTotal`/`SwapFree` to reflect the cell's
/// `memory.max` (kept fields like `MemAvailable` follow the cap so a runtime that
/// reads "free" memory doesn't over-allocate). `cap` is bytes; meminfo is in kiB.
fn synth_meminfo(cap: u64) -> Vec<u8> {
    let host = std::fs::read("/proc/meminfo").unwrap_or_default();
    let text = String::from_utf8_lossy(&host);
    let total_kb = cap / 1024;
    // Approximate "free"/"available" as ~90% of the cap — a fresh cell hasn't
    // touched most of it. (lxcfs reads the live cgroup `memory.current`; we don't
    // have a per-read counter on this path, so a static plausible figure is used —
    // runtimes size against MemTotal, not MemFree.)
    let free_kb = (total_kb / 10) * 9;
    let mut out = String::new();
    for line in text.lines() {
        let key = line.split(':').next().unwrap_or("");
        match key {
            "MemTotal" => out.push_str(&format!("MemTotal:       {total_kb:>8} kB\n")),
            "MemFree" => out.push_str(&format!("MemFree:        {free_kb:>8} kB\n")),
            "MemAvailable" => out.push_str(&format!("MemAvailable:   {free_kb:>8} kB\n")),
            // The cell's swap is denied (`memory.swap.max` = 0), so report none.
            "SwapTotal" => out.push_str("SwapTotal:             0 kB\n"),
            "SwapFree" => out.push_str("SwapFree:              0 kB\n"),
            _ => {
                out.push_str(line);
                out.push('\n');
            }
        }
    }
    if out.is_empty() {
        // Host file unreadable: emit a minimal-but-valid meminfo.
        out = format!(
            "MemTotal:       {total_kb:>8} kB\nMemFree:        {free_kb:>8} kB\nMemAvailable:   {free_kb:>8} kB\nSwapTotal:             0 kB\nSwapFree:              0 kB\n"
        );
    }
    out.into_bytes()
}

/// A read-only `/proc/sys/*` value the cell can safely read (tuning knobs runtimes
/// probe). Each is a plausible default for a container; writes are rejected by the
/// open path (these are served read-only). `boot_id`/`uuid` are freshly random per
/// open (the kernel regenerates `uuid` every read; `boot_id` is stable per boot —
/// we use a per-cell-stable value good enough for the cache-key uses runtimes make).
fn proc_sys_synth(p: &[u8]) -> Option<Vec<u8>> {
    Some(match p {
        b"/proc/sys/kernel/random/uuid" => {
            let mut b = random_uuid_v4();
            b.push(b'\n');
            b
        }
        b"/proc/sys/kernel/random/boot_id" => {
            let mut b = boot_id().clone();
            b.push(b'\n');
            b
        }
        b"/proc/sys/net/core/somaxconn" => b"4096\n".to_vec(),
        // Containers run with `net.ipv4.ip_unprivileged_port_start=0` so non-root
        // workloads bind :80/:443 (nginx, node). init-oci / the KVM init set this;
        // the sentry has no init, so synthesize the same value here.
        b"/proc/sys/net/ipv4/ip_unprivileged_port_start" => b"0\n".to_vec(),
        b"/proc/sys/vm/overcommit_memory" => b"1\n".to_vec(),
        b"/proc/sys/vm/max_map_count" => b"262144\n".to_vec(),
        b"/proc/sys/kernel/pid_max" => b"4194304\n".to_vec(),
        b"/proc/sys/kernel/threads-max" => b"63988\n".to_vec(),
        b"/proc/sys/fs/inotify/max_user_watches" => b"1048576\n".to_vec(),
        b"/proc/sys/fs/inotify/max_user_instances" => b"1024\n".to_vec(),
        b"/proc/sys/fs/inotify/max_queued_events" => b"16384\n".to_vec(),
        _ => return None,
    })
}

/// A random RFC-4122 v4 UUID as lowercase-hex bytes (`8-4-4-4-12`). Uses the raw
/// host `getrandom` (the same call the delegated `SYS_GETRANDOM` arm makes); runs
/// in the supervisor servicer, so a direct host syscall is fine.
fn random_uuid_v4() -> Vec<u8> {
    let mut r = [0u8; 16];
    unsafe {
        host(
            SYS_GETRANDOM,
            r.as_mut_ptr() as u64,
            r.len() as u64,
            0,
            0,
            0,
            0,
        );
    }
    r[6] = (r[6] & 0x0f) | 0x40; // version 4
    r[8] = (r[8] & 0x3f) | 0x80; // variant 1
    let hex = b"0123456789abcdef";
    let mut out = Vec::with_capacity(36);
    for (i, byte) in r.iter().enumerate() {
        if i == 4 || i == 6 || i == 8 || i == 10 {
            out.push(b'-');
        }
        out.push(hex[(byte >> 4) as usize]);
        out.push(hex[(byte & 0x0f) as usize]);
    }
    out
}

/// The cell's `boot_id`: a per-process-stable random UUID (the supervisor mints it
/// once; every cell read returns the same value, as a real `boot_id` is stable for
/// the life of the boot). Distinct from the host's so it isn't an infoleak.
fn boot_id() -> &'static Vec<u8> {
    static B: OnceLock<Vec<u8>> = OnceLock::new();
    B.get_or_init(random_uuid_v4)
}

/// Synthesize `/proc/self/cgroup` (cgroup-v2 single line: `0::<path>`). `<path>`
/// is the cell's cgroup dir RELATIVE to the v2 root (`/sentry-<pid>` or the shared
/// `/<name>`) when one was created, else `/` — always non-empty and well-formed so
/// a runtime that parses it (to then read `memory.max`/`cpu.max` under
/// `/sys/fs/cgroup/<path>`) sees a coherent path.
fn synth_self_cgroup() -> Vec<u8> {
    let rel = cgroup_path()
        .lock()
        .unwrap()
        .as_ref()
        .and_then(|d| d.strip_prefix(CGROUP_BASE).ok().map(|r| r.to_path_buf()))
        .map(|r| {
            let mut s = b"/".to_vec();
            s.extend_from_slice(r.to_string_lossy().as_bytes());
            s
        })
        .unwrap_or_else(|| b"/".to_vec());
    let mut out = b"0::".to_vec();
    out.extend_from_slice(&rel);
    out.push(b'\n');
    out
}

/// Synthesize `/proc/self/mountinfo`: the cell's confined view — the rootfs as `/`,
/// then the synthetic `/proc`, `/dev/shm`, `/dev/fd` we expose. The numeric ids
/// (mount id / parent id / major:minor) are plausible placeholders; the columns
/// match the kernel format so a parser (systemd, `findmnt`, container detection)
/// doesn't choke. Never the host's real mount table (that would leak host paths).
fn synth_self_mountinfo() -> Vec<u8> {
    // `mount_id parent_id major:minor root mount_point opts - fstype source super_opts`
    let mut out = String::new();
    out.push_str("1 0 0:1 / / rw,relatime - overlay rootfs rw\n");
    out.push_str("2 1 0:2 / /proc rw,nosuid,nodev,noexec,relatime - proc proc rw\n");
    out.push_str("3 1 0:3 / /dev rw,nosuid - tmpfs tmpfs rw,mode=755\n");
    out.push_str("4 3 0:4 / /dev/shm rw,nosuid,nodev - tmpfs shm rw\n");
    out.push_str("5 3 0:5 / /dev/fd rw,relatime - proc proc rw\n");
    out.into_bytes()
}

/// Synthesize a cgroup-aware GLOBAL `/proc` file (`/proc/cpuinfo`, `/proc/meminfo`)
/// or a read-only `/proc/sys/*` value, as raw bytes to back a memfd. Returns `None`
/// when nothing is synthesized for `p` (the caller falls through to the host
/// pass-through in `proc_redirect`). `cpuinfo`/`meminfo` are synthesized ONLY when
/// the corresponding cap is set — uncapped, the host's real file is served (the
/// pre-existing behavior), so a no-limits sandbox is byte-identical to before.
fn proc_synth(p: &[u8]) -> Option<Vec<u8>> {
    match p {
        b"/proc/cpuinfo" => cgroup_cpu_count().map(synth_cpuinfo),
        b"/proc/stat" => cgroup_cpu_count().map(synth_stat),
        b"/proc/meminfo" => cgroup_mem_bytes().map(synth_meminfo),
        // `/proc/mounts` (and the `/proc/self/mounts` it conventionally links to)
        // + `/proc/net/dev`: the cell has no real mount table / netdev list (no
        // /proc is mounted in the rootfs). Synthesize a coherent confined view so
        // tools that probe them (`df`, container-detection, `grep lo: /proc/net/dev`,
        // anything checking `/dev/shm tmpfs`) see the rootfs + the loopback iface.
        b"/proc/mounts" => Some(synth_mounts()),
        b"/proc/uptime" => Some(synth_uptime()),
        b"/proc/net/dev" => Some(synth_net_dev()),
        _ => proc_sys_synth(p),
    }
}

/// Synthesize `/proc/mounts` (the `fstab`-style view, distinct from the richer
/// `/proc/self/mountinfo`): the rootfs as `/` plus the synthetic `/proc`, `/dev`,
/// and the writable `/dev/shm` tmpfs the sentry exposes. Columns are
/// `spec mountpoint fstype opts dump pass` — enough for `df`/`grep`-style probes.
fn synth_mounts() -> Vec<u8> {
    let mut out = String::new();
    out.push_str("overlay / overlay rw,relatime 0 0\n");
    out.push_str("proc /proc proc rw,nosuid,nodev,noexec,relatime 0 0\n");
    out.push_str("tmpfs /dev tmpfs rw,nosuid,mode=755 0 0\n");
    out.push_str("shm /dev/shm tmpfs rw,nosuid,nodev 0 0\n");
    out.into_bytes()
}

/// Synthesize `/proc/net/dev`: the two header lines plus a single `lo:` interface
/// row (zeroed counters). The owned loopback is the only guest-visible iface; this
/// makes `ip`/`ifconfig`/`grep lo: /proc/net/dev` and loopback-presence probes see
/// it (the real host netdev list would leak host interfaces).
fn synth_net_dev() -> Vec<u8> {
    let mut out = String::new();
    out.push_str("Inter-|   Receive                                                |  Transmit\n");
    out.push_str(
        " face |bytes    packets errs drop fifo frame compressed multicast|bytes    packets errs drop fifo colls carrier compressed\n",
    );
    out.push_str(
        "    lo:       0       0    0    0    0     0          0         0        0       0    0    0    0     0       0          0\n",
    );
    out.into_bytes()
}

static GUEST_MONOTONIC_BASE_NS: OnceLock<u128> = OnceLock::new();

fn host_monotonic_ns() -> u128 {
    let mut ts = [0u64; 2];
    unsafe {
        host(
            SYS_CLOCK_GETTIME,
            libc::CLOCK_MONOTONIC as u64,
            ts.as_mut_ptr() as u64,
            0,
            0,
            0,
            0,
        );
    }
    (ts[0] as u128) * 1_000_000_000u128 + ts[1] as u128
}

fn seed_guest_monotonic_clock() {
    let _ = GUEST_MONOTONIC_BASE_NS.get_or_init(host_monotonic_ns);
}

fn guest_monotonic_timespec() -> [u8; 16] {
    let now = host_monotonic_ns();
    let base = *GUEST_MONOTONIC_BASE_NS.get_or_init(|| now);
    let elapsed = now.saturating_sub(base);
    let sec = (elapsed / 1_000_000_000u128) as u64;
    let nsec = (elapsed % 1_000_000_000u128) as u64;
    let mut out = [0u8; 16];
    out[..8].copy_from_slice(&sec.to_le_bytes());
    out[8..].copy_from_slice(&nsec.to_le_bytes());
    out
}

fn synth_uptime() -> Vec<u8> {
    let ts = guest_monotonic_timespec();
    let sec = u64::from_le_bytes(ts[..8].try_into().unwrap_or([0; 8]));
    let nsec = u64::from_le_bytes(ts[8..].try_into().unwrap_or([0; 8]));
    format!("{}.{:02} 0.00\n", sec, nsec / 10_000_000).into_bytes()
}

/// GLOBAL `/proc` files served from the host's `/proc` (no rootfs `/proc` is
/// mounted). These are host-global, accurate, and the standard container
/// exposure (CPU/memory sizing — `nproc`, `/proc/meminfo`, runtimes). PER-PROCESS
/// entries (`/proc/self/*`, `/proc/<pid>/*`) are NOT served here — they're
/// synthesized in `open_proc_self`. `/proc/cpuinfo` + `/proc/meminfo` are served
/// verbatim ONLY when uncapped; with a `cpu.max`/`memory.max` set, `proc_synth`
/// rewrites them to the cap BEFORE this pass-through is consulted.
fn proc_redirect(p: &[u8]) -> Option<&'static [u8]> {
    Some(match p {
        b"/proc/cpuinfo" => b"/proc/cpuinfo\0",
        b"/proc/meminfo" => b"/proc/meminfo\0",
        b"/proc/stat" => b"/proc/stat\0",
        b"/proc/loadavg" => b"/proc/loadavg\0",
        b"/proc/version" => b"/proc/version\0",
        b"/proc/filesystems" => b"/proc/filesystems\0",
        _ => return None,
    })
}

// ─── per-process /proc (the CALLER's own process only) ───────────────────────
const SYS_MEMFD_CREATE: i64 = 319;
/// The guest's argv joined by NUL (with a trailing NUL) — `/proc/self/cmdline`.
/// Set by `sandbox_main` (the supervisor knows the initial argv) before the cell
/// fork. Stale after an in-cell `execve` (documented); correct for the common
/// case. A fork child shares it (fork keeps argv), as the kernel would.
fn guest_cmdline() -> &'static Mutex<Vec<u8>> {
    static C: OnceLock<Mutex<Vec<u8>>> = OnceLock::new();
    C.get_or_init(|| Mutex::new(Vec::new()))
}
/// The guest's executable path (guest-visible, e.g. `/bin/sh`) — `/proc/self/exe`.
/// Set by `sandbox_main` before the cell fork. Stale after an in-cell `execve`.
fn guest_exe() -> &'static Mutex<Vec<u8>> {
    static E: OnceLock<Mutex<Vec<u8>>> = OnceLock::new();
    E.get_or_init(|| Mutex::new(Vec::new()))
}
/// Parse an unsigned decimal byte string (a `/proc/self/fd/<n>` index).
fn parse_u32(b: &[u8]) -> Option<i32> {
    if b.is_empty() {
        return None;
    }
    let mut v: i64 = 0;
    for &c in b {
        if !c.is_ascii_digit() {
            return None;
        }
        v = v * 10 + (c - b'0') as i64;
        if v > i32::MAX as i64 {
            return None;
        }
    }
    Some(v as i32)
}
/// If `bare` addresses the CALLER's own process under `/proc` (`/proc/self/<x>`
/// or `/proc/<caller_pid>/<x>`), return `<x>`. Other pids → `None` (→ ENOENT;
/// no cross-process/host-process leak). The bare `/proc/self` dir → `None` too
/// (no synthetic dir listing).
fn proc_self_rel<'a>(pid: i32, bare: &'a [u8]) -> Option<&'a [u8]> {
    if let Some(rest) = bare.strip_prefix(b"/proc/self/") {
        return Some(rest);
    }
    if let Some(rest) = bare.strip_prefix(b"/proc/thread-self/") {
        return Some(rest);
    }
    let mut nb = [0u8; 24];
    let i = fmt_i64(pid as i64, &mut nb);
    let mut pfx = b"/proc/".to_vec();
    pfx.extend_from_slice(&nb[i..]);
    pfx.push(b'/');
    bare.strip_prefix(pfx.as_slice())
}
/// Parse `/proc/<n>/<rel>` into `(<n>, <rel>)` for a numeric pid (NOT `self`).
/// Returns `None` for non-numeric or non-`/proc/<n>/…` paths. Used to serve a
/// cross-process `/proc/<pid>/…` entry for any pid in the OWNED process tree
/// (C2b): pids aren't virtualized, so the guest pid IS the host pid.
fn proc_pid_rel(bare: &[u8]) -> Option<(i32, &[u8])> {
    let rest = bare.strip_prefix(b"/proc/")?;
    let slash = rest.iter().position(|&b| b == b'/')?;
    let (num, tail) = rest.split_at(slash);
    let pid = parse_u32(num)? as i32;
    Some((pid, &tail[1..]))
}
/// Parse a BARE `/proc/<n>` (numeric pid, NO trailing component) → the pid. `None`
/// for `/proc/self`, `/proc/<n>/<rel>`, or non-numeric. Used to stat/opendir the
/// per-process directory itself (`ls /proc`, `test -d /proc/<pid>`).
fn proc_bare_pid(bare: &[u8]) -> Option<i32> {
    let rest = bare.strip_prefix(b"/proc/")?;
    if rest.is_empty() || rest.contains(&b'/') {
        return None;
    }
    parse_u32(rest)
}
/// Back an open with synthetic bytes via an anonymous in-memory file (memfd),
/// seeked to 0 — the guest reads it like any file. Negative errno on failure.
fn memfd_with(content: &[u8]) -> i64 {
    let name = b"sentry-proc\0";
    let fd = unsafe { host(SYS_MEMFD_CREATE, name.as_ptr() as u64, 0, 0, 0, 0, 0) };
    if fd < 0 {
        return fd;
    }
    let mut off = 0usize;
    while off < content.len() {
        let n = unsafe {
            host(
                SYS_WRITE,
                fd as u64,
                content[off..].as_ptr() as u64,
                (content.len() - off) as u64,
                0,
                0,
                0,
            )
        };
        if n <= 0 {
            break;
        }
        off += n as usize;
    }
    unsafe {
        host(SYS_LSEEK, fd as u64, 0, 0 /*SEEK_SET*/, 0, 0, 0)
    };
    fd
}
/// Serve a per-process `/proc` file for the CALLER. `cmdline` is synthesized;
/// `status`/`stat`/`statm`/`comm` are the caller's REAL metrics (its host procfs
/// entry — accurate, since the cell process IS the guest, modulo the loader's
/// `Name`); `exe`/`fd`/`maps` are synthesized to the guest's view (see each arm).
/// Unknown entries → ENOENT (fail cleanly rather than leaking the loader's view).
fn open_proc_self(pid: i32, rel: &[u8], flags: u64) -> i64 {
    let f = flags & !(O_CREAT | O_TRUNC);
    match rel {
        // /proc/<pid>/cmdline → the per-pid recorded guest argv (set on launch +
        // each in-cell execve via CTL_SET_CMDLINE). Falls back to the supervisor's
        // spawn-time `guest_cmdline()` only when nothing was recorded (the very
        // first cell before any record). This is what in-guest `pgrep -f`/`ps`
        // read — for the CALLER and for any other proctree pid (cross-process).
        b"cmdline" => {
            let c = cmdline_for(pid);
            if c.is_empty() {
                memfd_with(&guest_cmdline().lock().unwrap())
            } else {
                memfd_with(&c)
            }
        }
        // /proc/self/cgroup → the cgroup-v2 unified-hierarchy line for the cell.
        // A container runtime reads this to discover its cgroup path (and from it
        // the cgroupfs limits at `/sys/fs/cgroup/<path>/memory.max` etc.). We emit
        // the real per-cell cgroup dir (relative to the v2 root) when one was set
        // up, else `/` (the synthesized fallback — non-empty, the conformance bar).
        b"cgroup" => memfd_with(&synth_self_cgroup()),
        // /proc/self/mountinfo → a plausible mount table for the cell: the rootfs
        // as `/`, plus the synthetic /proc, /dev/shm, /dev/fd we expose. Tools that
        // walk mountinfo (systemd, container detection, df) get a coherent view
        // instead of the host's mount table (an infoleak) or ENOENT.
        b"mountinfo" => memfd_with(&synth_self_mountinfo()),
        b"mounts" => memfd_with(&synth_mounts()),
        // /proc/self/comm → the caller's REAL host comm (read-through, same as
        // status/stat/statm). The cell process IS the guest process and PR_SET_NAME
        // is forwarded straight to the host (dispatch_simple → host(SYS_PRCTL, …)),
        // so the host's `/proc/<pid>/comm` already reflects the set name (the kernel
        // truncates to 15 chars). A static synth would NOT track PR_SET_NAME, so the
        // read-through is the correct mechanism here.
        // …and /proc/self/task (the thread directory) + /proc/self/task/<tid>/…:
        // read-through to the cell's REAL host /proc/<pid>/task tree. The cell process
        // IS the guest, so its host threads ARE the guest threads and host TIDs are the
        // guest's own gettid() values (pids aren't virtualized). Chromium's
        // sandbox/linux/services/thread_helpers opendir's /proc/self/task and FATALs on
        // ENOENT; runtimes (Go, the JVM, tokio) walk it to enumerate/count threads.
        b"status" | b"stat" | b"statm" | b"comm" | b"task" | b"oom_score_adj" => {
            let mut nb = [0u8; 24];
            let i = fmt_i64(pid as i64, &mut nb);
            let mut path = b"/proc/".to_vec();
            path.extend_from_slice(&nb[i..]);
            path.push(b'/');
            path.extend_from_slice(rel);
            path.push(0);
            unsafe { host(SYS_OPEN, path.as_ptr() as u64, f, 0, 0, 0, 0) }
        }
        _ if rel.starts_with(b"task/") => {
            let mut nb = [0u8; 24];
            let i = fmt_i64(pid as i64, &mut nb);
            let mut path = b"/proc/".to_vec();
            path.extend_from_slice(&nb[i..]);
            path.push(b'/');
            path.extend_from_slice(rel);
            path.push(0);
            unsafe { host(SYS_OPEN, path.as_ptr() as u64, f, 0, 0, 0, 0) }
        }
        // /proc/self/exe → open the guest exe within the rootfs (the symlink
        // TARGET); readlink is handled separately in the readlink arm.
        b"exe" => {
            let mut exe = exe_for(pid);
            if exe.is_empty() {
                return -2;
            }
            exe.push(0);
            let root = ROOT_FD.load(Ordering::Relaxed);
            let how = OpenHow {
                flags: f,
                mode: 0,
                resolve: RESOLVE_IN_ROOT,
            };
            let r = unsafe {
                host(
                    SYS_OPENAT2,
                    root as u64,
                    exe.as_ptr() as u64,
                    &how as *const _ as u64,
                    std::mem::size_of::<OpenHow>() as u64,
                    0,
                    0,
                )
            };
            if r < 0 {
                let bare = exe
                    .iter()
                    .position(|&b| b == 0)
                    .map(|n| &exe[..n])
                    .unwrap_or(exe.as_slice());
                ipc_logf_raw(
                    &[
                        b"PROCEXE_OPEN_FAIL pid=".as_slice(),
                        pid.to_string().as_bytes(),
                        b" errno=".as_slice(),
                        r.to_string().as_bytes(),
                        b" exe=".as_slice(),
                        bare,
                    ]
                    .concat(),
                );
            }
            r
        }
        // /proc/self/cwd → open the caller's guest cwd directory.
        b"cwd" => {
            let mut cwd = cwd_of(pid);
            cwd.push(0);
            open_path(pid, &cwd, f | O_DIRECTORY, 0)
        }
        // /proc/self/fd/<n> → REOPEN the underlying file (a fresh open file
        // description, like the real /proc/self/fd): translate the guest fd to
        // the supervisor's host fd H and open the supervisor's own
        // /proc/self/fd/<H>. A `fd/` with NO numeric component (just trailing
        // slashes) is the DIRECTORY, handled below — Chromium's sandbox opens
        // `self/fd/` WITH a trailing slash (proc_util.cc HasOpenDirectory), so
        // the bare-directory form MUST resolve to the dir, not a 0-length fd
        // number (which used to parse-fail → ENOENT → a sandbox FATAL).
        _ if rel.starts_with(b"fd/") && !rel[3..].iter().all(|&c| c == b'/') => {
            // Stop the number at any trailing slash (`fd/5/` → 5).
            let num = &rel[3..];
            let num = &num[..num.iter().position(|&c| c == b'/').unwrap_or(num.len())];
            let n = match parse_u32(num) {
                Some(n) => n,
                None => return -2,
            };
            let h = match fd_host(pid, n) {
                Some(h) => h,
                None => return -9, // -EBADF
            };
            let mut nb = [0u8; 24];
            let i = fmt_i64(h as i64, &mut nb);
            let mut path = b"/proc/self/fd/".to_vec();
            path.extend_from_slice(&nb[i..]);
            path.push(0);
            unsafe { host(SYS_OPEN, path.as_ptr() as u64, f, 0, 0, 0, 0) }
        }
        // /proc/self/fd (the directory): open a real placeholder host dir fd and
        // TAG it so getdents64 synthesizes the guest's fd numbers (not the
        // placeholder's). The placeholder is the supervisor's own /proc/self/fd
        // (always a valid dir); we never read its real contents. `fd//` (extra
        // trailing slashes) lands here too — it's still the directory.
        _ if rel == b"fd" || (rel.starts_with(b"fd/") && rel[3..].iter().all(|&c| c == b'/')) => {
            let ph = b"/proc/self/fd\0";
            let fd = unsafe {
                host(
                    SYS_OPEN,
                    ph.as_ptr() as u64,
                    (libc::O_RDONLY | libc::O_DIRECTORY) as u64,
                    0,
                    0,
                    0,
                    0,
                )
            };
            if fd >= 0 {
                synth_fd_dirs().lock().unwrap().insert(fd as i32, (pid, 0));
            }
            fd
        }
        // /proc/self/maps → the caller's REAL host maps FILTERED to the guest's
        // mappings only: keep lines whose start address is below the SUD floor
        // (the guest arena lives there; `load_elf` forbids guest segments at/above
        // it), drop everything at/above it — that's the sentry loader/supervisor's
        // own code/heap/stack/vdso, hidden to avoid an ASLR infoleak. Guest
        // segments are all `MAP_ANONYMOUS` (copied in, never file-backed), so kept
        // lines carry no host pathname. Served via a memfd of the filtered text so
        // introspecting runtimes (Go, ASan, JITs, profilers) get a coherent view.
        b"maps" => {
            let raw = std::fs::read(format!("/proc/{pid}/maps")).unwrap_or_default();
            let mut filtered = Vec::with_capacity(raw.len());
            for line in raw.split_inclusive(|&b| b == b'\n') {
                let dash = line.iter().position(|&b| b == b'-').unwrap_or(0);
                let start = std::str::from_utf8(&line[..dash])
                    .ok()
                    .and_then(|s| u64::from_str_radix(s, 16).ok())
                    .unwrap_or(u64::MAX);
                if start < WINDOW_FLOOR {
                    filtered.extend_from_slice(line);
                }
            }
            memfd_with(&filtered)
        }
        _ => -2,
    }
}

// Synthetic `/proc/self/fd` directory fds: a guest `opendir("/proc/self/fd")`
// gets a real placeholder host dir fd that we TAG here (host fd → (owner pid,
// emit cursor)); `getdents64` on it is synthesized from the pid's virtual fd
// table instead of the placeholder's real contents. Untagged on close / death.
fn synth_fd_dirs() -> &'static Mutex<HashMap<i32, (i32, usize)>> {
    static T: OnceLock<Mutex<HashMap<i32, (i32, usize)>>> = OnceLock::new();
    T.get_or_init(|| Mutex::new(HashMap::new()))
}
/// Build `linux_dirent64` records for `/proc/self/fd`: `.`, `..`, then each guest
/// fd number (as a symlink). Emits from `cursor` onward, up to `cap` bytes
/// (never splitting a record); returns `(bytes, new_cursor)`. `new_cursor` past
/// the end ⇒ the next call returns 0 (EOF).
fn synth_fd_dirents(pid: i32, cursor: usize, cap: usize) -> (Vec<u8>, usize) {
    // Entry list: 0=".", 1="..", then the sorted guest fd numbers.
    let fds: Vec<i32> = {
        let t = fdt().lock().unwrap();
        let mut v: Vec<i32> = t
            .get(&pid)
            .map(|m| m.keys().copied().collect())
            .unwrap_or_default();
        v.sort_unstable();
        v
    };
    let mut names: Vec<(Vec<u8>, u8)> = Vec::with_capacity(fds.len() + 2);
    names.push((b".".to_vec(), 4 /*DT_DIR*/));
    names.push((b"..".to_vec(), 4));
    for fd in fds {
        let mut nb = [0u8; 24];
        let i = fmt_i64(fd as i64, &mut nb);
        names.push((nb[i..].to_vec(), 10 /*DT_LNK*/));
    }
    let mut out = Vec::new();
    let mut idx = cursor;
    while idx < names.len() {
        let (name, dtype) = &names[idx];
        let reclen = ((19 + name.len() + 1 + 7) & !7) as u16; // align d_reclen to 8
        if out.len() + reclen as usize > cap {
            break; // don't split a record
        }
        let start = out.len();
        out.extend_from_slice(&((idx as u64) + 1).to_le_bytes()); // d_ino (nonzero)
        out.extend_from_slice(&((idx as i64) + 1).to_le_bytes()); // d_off
        out.extend_from_slice(&reclen.to_le_bytes());
        out.push(*dtype);
        out.extend_from_slice(name);
        out.push(0);
        out.resize(start + reclen as usize, 0); // pad to reclen
        idx += 1;
    }
    (out, idx)
}

/// Synthetic `/proc` directory fds (C2b): a guest `opendir("/proc")` gets a real
/// placeholder host dir fd that we TAG here (host fd → emit cursor); `getdents64`
/// on it is synthesized from the OWNED process tree's live pids + the global
/// synthetic `/proc` files instead of the placeholder's real contents. This is
/// what `pgrep -f`/`ps` enumerate. Untagged on close / death.
fn synth_proc_dirs() -> &'static Mutex<HashMap<i32, usize>> {
    static T: OnceLock<Mutex<HashMap<i32, usize>>> = OnceLock::new();
    T.get_or_init(|| Mutex::new(HashMap::new()))
}
/// Build `linux_dirent64` records for `/proc`: `.`, `..`, `self` (symlink), a
/// curated set of global synthetic files we serve, then each live owned-tree pid
/// (as a directory). Emits from `cursor` onward up to `cap` bytes (never splitting
/// a record); returns `(bytes, new_cursor)`. Cursor past the end ⇒ next call EOF.
fn synth_proc_dirents(cursor: usize, cap: usize) -> (Vec<u8>, usize) {
    let mut names: Vec<(Vec<u8>, u8)> = Vec::new();
    names.push((b".".to_vec(), 4 /*DT_DIR*/));
    names.push((b"..".to_vec(), 4));
    names.push((b"self".to_vec(), 10 /*DT_LNK*/));
    // Global synthetic files/dirs we actually serve (so `ls /proc` matches what
    // can be opened): cpuinfo/meminfo/mounts/uptime (files), net/sys (dirs).
    for (n, d) in [
        (b"cpuinfo".as_slice(), 8u8),
        (b"meminfo".as_slice(), 8),
        (b"mounts".as_slice(), 8),
        (b"uptime".as_slice(), 8),
        (b"stat".as_slice(), 8),
        (b"net".as_slice(), 4),
        (b"sys".as_slice(), 4),
    ] {
        names.push((n.to_vec(), d));
    }
    // Live owned-tree pids — the guest-visible process set (pids == host pids).
    for h in proctree::live_host_pids() {
        let mut nb = [0u8; 24];
        let i = fmt_i64(h as i64, &mut nb);
        names.push((nb[i..].to_vec(), 4 /*DT_DIR*/));
    }
    let mut out = Vec::new();
    let mut idx = cursor;
    while idx < names.len() {
        let (name, dtype) = &names[idx];
        let reclen = ((19 + name.len() + 1 + 7) & !7) as u16; // align d_reclen to 8
        if out.len() + reclen as usize > cap {
            break; // don't split a record
        }
        let start = out.len();
        out.extend_from_slice(&((idx as u64) + 1).to_le_bytes()); // d_ino (nonzero)
        out.extend_from_slice(&((idx as i64) + 1).to_le_bytes()); // d_off
        out.extend_from_slice(&reclen.to_le_bytes());
        out.push(*dtype);
        out.extend_from_slice(name);
        out.push(0);
        out.resize(start + reclen as usize, 0); // pad to reclen
        idx += 1;
    }
    (out, idx)
}

/// `readlink` of a per-process `/proc` SYMLINK for the caller: `exe` → the guest
/// exe path; `fd/<n>` → the open file's GUEST-visible path (or `pipe:[…]` /
/// `socket:[…]` verbatim). `None` → not a symlink we synthesize (the caller
/// falls through to the normal readlink path, which yields EINVAL/ENOENT).
/// Supervisor-side per-pid exe path (the cell's CURRENT `/proc/self/exe`, set by
/// the cell on each in-cell execve via CTL_SET_EXE). The supervisor's own
/// `guest_exe()` is only the spawn-time value (e.g. `/bin/sh`); this map carries
/// the actual exec'd binary per cell process so `/proc/self/exe` is correct after
/// any exec — load-bearing for Chromium (icudtl.dat resource dir + re-exec'd
/// renderer/gpu children). Dropped with the pid in `fd_drop`.
fn proc_exe() -> &'static Mutex<HashMap<i32, Vec<u8>>> {
    static S: OnceLock<Mutex<HashMap<i32, Vec<u8>>>> = OnceLock::new();
    S.get_or_init(|| Mutex::new(HashMap::new()))
}
/// Supervisor-side per-pid guest cmdline (NUL-separated argv), set on each in-cell
/// execve via CTL_SET_CMDLINE. Serves `/proc/<pid>/cmdline` and `/proc/self/cmdline`
/// so in-guest `pgrep -f`/`ps` see the real workload command, not the sentry
/// binary's argv. Dropped with the pid in `fd_drop`.
fn proc_cmdline() -> &'static Mutex<HashMap<i32, Vec<u8>>> {
    static S: OnceLock<Mutex<HashMap<i32, Vec<u8>>>> = OnceLock::new();
    S.get_or_init(|| Mutex::new(HashMap::new()))
}
/// The guest-visible `/proc/<pid>/cmdline` for `pid` (NUL-separated argv, empty
/// if unknown — caller decides whether to fall back to comm). Cloned snapshot.
fn cmdline_for(pid: i32) -> Vec<u8> {
    proc_cmdline()
        .lock()
        .unwrap()
        .get(&pid)
        .cloned()
        .unwrap_or_default()
}

fn fdtrace_cmd_interesting(cmd: &[u8]) -> bool {
    cmd.windows(b"pseudonymization-salt-handle".len())
        .any(|w| w == b"pseudonymization-salt-handle")
        || cmd.windows(b"zygote".len()).any(|w| w == b"zygote")
}
fn host_fd_size(hfd: i32) -> Option<i64> {
    let mut st: libc::stat = unsafe { std::mem::zeroed() };
    let r = unsafe {
        host(
            SYS_FSTAT,
            hfd as u64,
            &mut st as *mut libc::stat as u64,
            0,
            0,
            0,
            0,
        )
    };
    (r == 0).then_some(st.st_size)
}
fn repair_chromium_zygote_salt_fd(pid: i32) {
    let cmd = cmdline_for(pid);
    if !cmd
        .windows(b"--type=zygote".len())
        .any(|w| w == b"--type=zygote")
    {
        return;
    }
    if fd_host(pid, 10).and_then(host_fd_size) == Some(4) {
        return;
    }
    let entries: Vec<(i32, FdVal)> = {
        let t = fdt().lock().unwrap();
        t.get(&pid)
            .map(|m| m.iter().map(|(&g, &v)| (g, v)).collect())
            .unwrap_or_default()
    };
    let mut candidate: Option<(i32, i32)> = None;
    for (g, v) in entries {
        if g == 10 {
            continue;
        }
        let FdVal::Host(h) = v else {
            continue;
        };
        if host_fd_size(h) == Some(4) {
            candidate = Some((g, h));
        }
    }
    let Some((src_g, src_h)) = candidate else {
        return;
    };
    let dup = unsafe { host(SYS_DUP, src_h as u64, 0, 0, 0, 0, 0) };
    if dup < 0 {
        return;
    }
    fd_install_val_at_with_flags(pid, 10, FdVal::Host(dup as i32), 0);
    ipc_logf(
        &[
            (b"FDSALTFIX pid=", pid as i64),
            (b" src=", src_g as i64),
            (b" h=", src_h as i64),
            (b" dup=", dup),
        ],
        b"",
    );
}
fn fdtrace_dump_table(pid: i32, label: &[u8]) {
    if !unsafe { FDTRACE } {
        return;
    }
    let cmd = cmdline_for(pid);
    let entries: Vec<(i32, FdVal)> = {
        let t = fdt().lock().unwrap();
        t.get(&pid)
            .map(|m| m.iter().map(|(&g, &v)| (g, v)).collect())
            .unwrap_or_default()
    };
    if !fdtrace_cmd_interesting(&cmd) && !entries.iter().any(|(g, _)| *g == 10 || *g == 11) {
        return;
    }
    let mut line = Vec::new();
    line.extend_from_slice(b"FDTRACE ");
    line.extend_from_slice(label);
    line.extend_from_slice(b" pid=");
    line.extend_from_slice(pid.to_string().as_bytes());
    line.extend_from_slice(b" cmd=");
    for &b in cmd.iter().take(220) {
        line.push(if b == 0 { b' ' } else { b });
    }
    ipc_logf_raw(&line);
    for (g, v) in entries {
        if g > 64 && g != 10 && g != 11 {
            continue;
        }
        let mut row = Vec::new();
        row.extend_from_slice(b"FDTRACE_FD pid=");
        row.extend_from_slice(pid.to_string().as_bytes());
        row.extend_from_slice(b" g=");
        row.extend_from_slice(g.to_string().as_bytes());
        row.extend_from_slice(b" flags=");
        row.extend_from_slice(fd_get_desc_flags(pid, g, v).to_string().as_bytes());
        match v {
            FdVal::Host(h) => {
                row.extend_from_slice(b" h=");
                row.extend_from_slice(h.to_string().as_bytes());
                if let Some((dev, ino)) = stat_dev_ino(h) {
                    row.extend_from_slice(b" dev=");
                    row.extend_from_slice(dev.to_string().as_bytes());
                    row.extend_from_slice(b" ino=");
                    row.extend_from_slice(ino.to_string().as_bytes());
                }
            }
            FdVal::Loop(s) => {
                row.extend_from_slice(b" loop=");
                row.extend_from_slice((s as i64).to_string().as_bytes());
            }
        }
        ipc_logf_raw(&row);
    }
}
/// The guest-visible `/proc/self/exe` for `pid`: the per-pid exe recorded on its
/// last in-cell execve, else the supervisor's spawn-time `guest_exe()` (the main
/// cell before it has exec'd anything).
fn exe_for(pid: i32) -> Vec<u8> {
    if let Some(e) = proc_exe().lock().unwrap().get(&pid) {
        if !e.is_empty() {
            return e.clone();
        }
    }
    guest_exe().lock().unwrap().clone()
}
fn proc_self_readlink(pid: i32, bare: &[u8]) -> Option<Vec<u8>> {
    let rel = proc_self_rel(pid, bare)?;
    if rel == b"exe" {
        let e = exe_for(pid);
        return if e.is_empty() { None } else { Some(e) };
    }
    if rel == b"cwd" {
        return Some(cwd_of(pid));
    }
    if let Some(num) = rel.strip_prefix(b"fd/") {
        let n = parse_u32(num)?;
        let h = fd_host(pid, n)?;
        let mut nb = [0u8; 24];
        let i = fmt_i64(h as i64, &mut nb);
        let mut p = b"/proc/self/fd/".to_vec();
        p.extend_from_slice(&nb[i..]);
        p.push(0);
        let mut buf = [0u8; 4096];
        let r = unsafe {
            host(
                SYS_READLINK,
                p.as_ptr() as u64,
                buf.as_mut_ptr() as u64,
                buf.len() as u64,
                0,
                0,
                0,
            )
        };
        if r <= 0 {
            return None;
        }
        let target = &buf[..r as usize];
        if target.first() != Some(&b'/') {
            // pipe:[inode] / socket:[inode] / anon_inode:[…] — no path to map.
            return Some(target.to_vec());
        }
        // A real file: map the host path back to the guest path (never leak the
        // host rootfs location); if it's somehow outside, fail rather than leak.
        return host_to_guest_path(target);
    }
    None
}

/// Open `path` (NUL-terminated, guest-absolute) for the cell. A whitelisted
/// `/dev/*` device opens the real host node (the standard container device set);
/// otherwise confined to the rootfs, with a matching bind-mount redirecting to
/// the mount's host dir (read-only mounts reject write opens). With no rootfs
/// set, a plain host open (back-compat for the non-rootfs spike runs).
fn open_path(pid: i32, path: &[u8], flags: u64, mode: u64) -> i64 {
    let root = ROOT_FD.load(Ordering::Relaxed);
    if root < 0 {
        return unsafe { host(SYS_OPEN, path.as_ptr() as u64, flags, mode, 0, 0, 0) };
    }
    // Strip the trailing NUL for prefix matching; resolve_mount re-adds it.
    let bare = match path.split_last() {
        Some((0, head)) => head,
        _ => path,
    };
    // Per-process /proc for the CALLER's own process (cmdline synth, status/stat
    // redirect); other pids / unknown entries ENOENT (no leak).
    if let Some(rel) = proc_self_rel(pid, bare) {
        return open_proc_self(pid, rel, flags);
    }
    // Cross-process /proc/<pid>/… for ANOTHER process in the OWNED tree (C2b):
    // `pgrep -f`/`ps` readdir(/proc) then open each sibling's cmdline/stat/status.
    // Pids aren't virtualized, so the target host pid == guest pid; serve it via the
    // same per-pid machinery (cmdline from `proc_cmdline`, stat/status/comm read
    // through the target's host procfs). Only tree members — never host/foreign pids.
    if let Some((tpid, rel)) = proc_pid_rel(bare) {
        if proctree::vpid_for(tpid).is_some() {
            return open_proc_self(tpid, rel, flags);
        }
    }
    // Bare /proc/self or /proc/<proctree-pid> (the per-process DIRECTORY, no
    // trailing entry): open the REAL host /proc/<pid> dir so stat reports a
    // directory and `ls /proc` / `test -d /proc/<pid>` work — `ls` stats each pid
    // entry, and proc_pid_rel above only matches /proc/<pid>/<rel>. Pids aren't
    // virtualized (guest pid == host pid). Self always; another pid only if it is
    // in the owned tree — never a foreign host pid (falls through → ENOENT).
    {
        let tpid = if bare == b"/proc/self" || bare == b"/proc/thread-self" {
            Some(pid)
        } else {
            proc_bare_pid(bare).filter(|&p| p == pid || proctree::vpid_for(p).is_some())
        };
        if let Some(tpid) = tpid {
            let mut nb = [0u8; 24];
            let i = fmt_i64(tpid as i64, &mut nb);
            let mut p = b"/proc/".to_vec();
            p.extend_from_slice(&nb[i..]);
            p.push(0);
            let f = flags & !(O_CREAT | O_TRUNC);
            return unsafe { host(SYS_OPEN, p.as_ptr() as u64, f, 0, 0, 0, 0) };
        }
    }
    // /proc (the directory itself): open a real placeholder host dir fd and TAG it
    // so getdents64 synthesizes the owned process tree's pids + global synthetic
    // files (C2b). Without this, opendir("/proc") would list the rootfs's empty
    // /proc mountpoint and `pgrep -f`/`ps` would see NO processes. Trailing slashes
    // (`/proc/`, `/proc//`) land here too — still the directory.
    if bare == b"/proc" || (bare.starts_with(b"/proc/") && bare[6..].iter().all(|&c| c == b'/')) {
        let ph = b"/proc\0";
        let fd = unsafe {
            host(
                SYS_OPEN,
                ph.as_ptr() as u64,
                (libc::O_RDONLY | libc::O_DIRECTORY) as u64,
                0,
                0,
                0,
                0,
            )
        };
        if fd >= 0 {
            synth_proc_dirs().lock().unwrap().insert(fd as i32, 0);
        }
        return fd;
    }
    // /dev/fd → /proc/self/fd (the standard symlink): an `open("/dev/fd")` or
    // `open("/dev/fd/<n>")` behaves exactly like the `/proc/self/fd` equivalent
    // (the guest fd table is the source of truth). readlink("/dev/fd") yields the
    // `/proc/self/fd` target, handled in the readlink path.
    if let Some(rel) = dev_fd_rel(bare) {
        return open_proc_self(pid, &rel, flags);
    }
    // Whitelisted device → open the real host node (never a write-create on it).
    if let Some(hostdev) = host_device_for(bare) {
        let f = flags & !(O_CREAT | O_TRUNC);
        let r = unsafe { host(SYS_OPEN, hostdev.as_ptr() as u64, f, 0, 0, 0, 0) };
        if r >= 0 || !ends_with(bare, b"random") {
            return r;
        }
        // The host lacks /dev/[u]random (an incomplete/quirky/corrupted host /dev — e.g.
        // a container, or a devtmpfs with the nodes removed). Chrome CHILD processes do
        // CHECK(open("/dev/urandom") >= 0) at startup (base/rand_util_posix.cc) and ABORT
        // on failure — so every renderer/utility/zygote dies while the browser (getrandom/
        // BoringSSL) survives. Back it with a getrandom-filled memfd so sentry doesn't
        // depend on the host node: getrandom is the same entropy source /dev/urandom
        // drains, and the size covers the seeding-scale reads chrome makes of this fd
        // (BoringSSL handles the bulk of randomness, not this fd).
        let mut buf = vec![0u8; 1 << 20];
        let mut off = 0usize;
        while off < buf.len() {
            let n = unsafe {
                host(
                    SYS_GETRANDOM,
                    buf[off..].as_mut_ptr() as u64,
                    (buf.len() - off) as u64,
                    0,
                    0,
                    0,
                    0,
                )
            };
            if n <= 0 {
                break;
            }
            off += n as usize;
        }
        if off == 0 {
            return r;
        }
        return memfd_with(&buf[..off]);
    }
    // Cgroup-aware synthesized /proc (cpuinfo/meminfo when capped) or read-only
    // /proc/sys/* value → back it with a memfd of the synthesized bytes.
    if let Some(content) = proc_synth(bare) {
        let f = flags & !(O_CREAT | O_TRUNC);
        if is_write_open(f) {
            return -30; // -EROFS: these are read-only synthetic files
        }
        return memfd_with(&content);
    }
    // Global /proc file → open the host's (read-only; never create/truncate).
    if let Some(procfile) = proc_redirect(bare) {
        let f = flags & !(O_CREAT | O_TRUNC);
        return unsafe { host(SYS_OPEN, procfile.as_ptr() as u64, f, 0, 0, 0, 0) };
    }
    let (dirfd, resolved, readonly) = resolve_mount(bare);
    if readonly && is_write_open(flags) {
        return -30; // -EROFS
    }
    let created_candidate = (flags & O_CREAT) != 0;
    let existed_before = if created_candidate {
        let fd = opath(pid, path, false);
        if fd >= 0 {
            unsafe {
                host(SYS_CLOSE, fd as u64, 0, 0, 0, 0, 0);
            }
            true
        } else {
            false
        }
    } else {
        true
    };
    let how = OpenHow {
        flags,
        // mode only with O_CREAT, and MASKED to 07777: `openat2` is stricter than
        // `open` — it returns EINVAL for any bit outside the permission/setid/
        // sticky set, whereas `open` silently ignores them. Callers like busybox
        // `cp` pass the source's full `st_mode` (incl. the `S_IFREG` type bit), so
        // an unmasked mode would fail every `cp`/install of a file. (#A3a)
        mode: mode_for_open_flags(pid, flags, mode),
        resolve: RESOLVE_IN_ROOT,
    };
    let r = unsafe {
        host(
            SYS_OPENAT2,
            dirfd as u64,
            resolved.as_ptr() as u64,
            &how as *const _ as u64,
            std::mem::size_of::<OpenHow>() as u64,
            0,
            0,
        )
    };
    if r >= 0 && created_candidate && !existed_before {
        let exact_mode = mode_for_open_flags(pid, flags, mode);
        unsafe { host(SYS_FCHMOD, r as u64, exact_mode, 0, 0, 0, 0) };
        chown_created_fd_to_virtual(pid, r as i32);
    }
    // IPCTRACE diagnostic: surface FAILED file opens (path + errno) — pinpoints a
    // missing resource (e.g. Chromium's icudtl.dat) under sentry path resolution.
    if r < 0 && unsafe { IPCTRACE } {
        let mut line: Vec<u8> = b"OPENFAIL errno=".to_vec();
        let mut nb = [0u8; 24];
        let i = fmt_i64(r, &mut nb);
        line.extend_from_slice(&nb[i..]);
        line.extend_from_slice(b" flags=0x");
        let mut hb = [0u8; 16];
        let mut fv = flags;
        let mut hi = 16;
        if fv == 0 {
            hi -= 1;
            hb[hi] = b'0';
        }
        while fv > 0 {
            hi -= 1;
            hb[hi] = b"0123456789abcdef"[(fv & 0xf) as usize];
            fv >>= 4;
        }
        line.extend_from_slice(&hb[hi..]);
        line.extend_from_slice(b" path=");
        line.extend_from_slice(bare);
        ipc_logf_raw(&line);
    }
    r
}
/// An O_PATH handle to `path` (confined), for stat/access/readlink-by-path.
fn opath(pid: i32, path: &[u8], nofollow: bool) -> i64 {
    let flags = O_PATH | if nofollow { O_NOFOLLOW } else { 0 };
    open_path(pid, path, flags, 0)
}

/// Split a (NUL-terminated) path into `(parent_nul, basename_nul)` — the final
/// component and everything before it. Trailing slashes are stripped. For a
/// top-level path (`/foo`) the parent is `/`; for a bare leaf the parent is `.`.
fn split_parent_base(path_nul: &[u8]) -> (Vec<u8>, Vec<u8>) {
    let bare = match path_nul.split_last() {
        Some((0, head)) => head,
        _ => path_nul,
    };
    let mut end = bare.len();
    while end > 1 && bare[end - 1] == b'/' {
        end -= 1;
    }
    let b = &bare[..end];
    let (parent, base): (&[u8], &[u8]) = match b.iter().rposition(|&c| c == b'/') {
        Some(0) => (b"/", &b[1..]),
        Some(i) => (&b[..i], &b[i + 1..]),
        None => (b".", b),
    };
    let mut pn = parent.to_vec();
    pn.push(0);
    let mut bn = base.to_vec();
    bn.push(0);
    (pn, bn)
}

/// Resolve a guest path (for a `*at`-style path-mutating op) to a CONFINED parent
/// directory fd + the final basename, so the caller can run `mkdirat`/`unlinkat`/
/// `renameat`/… against `(pfd, base)` without any `..`/symlink escape. Returns
/// `Err(-errno)` on failure. The caller MUST `close(pfd)` on success.
///
/// - `AT_FDCWD` or an absolute path → the parent is opened rootfs-confined via
///   [`open_path`] (`RESOLVE_IN_ROOT` + bind-mount aware), and a write into a
///   read-only mount is rejected with `-EROFS`.
/// - a real dirfd + relative path → the parent is opened `RESOLVE_BENEATH` the
///   (already-in-root) dirfd, so resolution can't climb out of that subtree.
fn confined_parent(pid: i32, dirfd: i32, path_ptr: u64) -> Result<(i32, Vec<u8>), i64> {
    let raw = pull_path(pid, path_ptr); // NUL-terminated, as the guest gave it
    let raw_bare = match raw.split_last() {
        Some((0, head)) => head,
        _ => raw.as_slice(),
    };
    let absolute = raw_bare.first() == Some(&b'/');
    if dirfd == AT_FDCWD || absolute {
        let abs = if absolute {
            raw.clone()
        } else {
            pull_cwd_path(pid, path_ptr)
        };
        let abs_bare = match abs.split_last() {
            Some((0, head)) => head,
            _ => abs.as_slice(),
        };
        if abs_bare.is_empty() {
            return Err(-2); // -ENOENT
        }
        if abs_bare == b"/" {
            // The rootfs root always exists; report EEXIST so `mkdir -p` (which
            // walks ancestors from `/`) tolerates it instead of aborting.
            return Err(-17); // -EEXIST
        }
        let (_, _, readonly) = resolve_mount(abs_bare);
        if readonly {
            return Err(-30); // -EROFS
        }
        let (parent, base) = split_parent_base(&abs);
        let pfd = open_path(pid, &parent, (O_PATH | O_DIRECTORY) as u64, 0);
        if pfd < 0 {
            return Err(pfd);
        }
        Ok((pfd as i32, base))
    } else {
        let hostdir = match fd_host(pid, dirfd) {
            Some(h) => h,
            None => return Err(-9), // -EBADF
        };
        if raw_bare.is_empty() {
            return Err(-2); // -ENOENT
        }
        let (parent_rel, base) = split_parent_base(&raw);
        let how = OpenHow {
            flags: O_PATH | O_DIRECTORY,
            mode: 0,
            resolve: RESOLVE_BENEATH,
        };
        let pfd = unsafe {
            host(
                SYS_OPENAT2,
                hostdir as u64,
                parent_rel.as_ptr() as u64,
                &how as *const _ as u64,
                std::mem::size_of::<OpenHow>() as u64,
                0,
                0,
            )
        };
        if pfd < 0 {
            return Err(pfd);
        }
        Ok((pfd as i32, base))
    }
}

/// Parse a guest `connect()` sockaddr into a `SocketAddr` for egress-policy
/// checks. Returns `None` for non-IP families (AF_UNIX, …) or truncated input.
/// Bring the loopback interface UP in the CURRENT network namespace (via
/// `SIOCSIFFLAGS`). A freshly-`unshare`d netns has `lo` present but DOWN, so
/// loopback traffic (the guest's own `127.0.0.1` services) wouldn't flow until
/// this runs. Best-effort.
fn netns_bring_up_lo() {
    // `struct ifreq` is 40 bytes: char name[16] then a 24-byte union whose first
    // member (for the flags ioctls) is a `short ifr_flags`.
    #[repr(C)]
    struct IfReq {
        name: [u8; 16],
        flags: i16,
        _pad: [u8; 22],
    }
    const SIOCGIFFLAGS: u64 = 0x8913;
    const SIOCSIFFLAGS: u64 = 0x8914;
    const IFF_UP: i16 = 0x1;
    unsafe {
        let s = host(
            SYS_SOCKET,
            libc::AF_INET as u64,
            libc::SOCK_DGRAM as u64,
            0,
            0,
            0,
            0,
        );
        if s < 0 {
            return;
        }
        let mut req = IfReq {
            name: [0; 16],
            flags: 0,
            _pad: [0; 22],
        };
        req.name[0] = b'l';
        req.name[1] = b'o';
        host(
            SYS_IOCTL,
            s as u64,
            SIOCGIFFLAGS,
            &mut req as *mut _ as u64,
            0,
            0,
            0,
        );
        req.flags |= IFF_UP;
        host(
            SYS_IOCTL,
            s as u64,
            SIOCSIFFLAGS,
            &mut req as *mut _ as u64,
            0,
            0,
            0,
        );
        host(SYS_CLOSE, s as u64, 0, 0, 0, 0, 0);
    }
}

/// fds to the HOST and the SANDBOX network namespaces, so a servicer can hop into
/// the host netns to open an EGRESS socket and hop back (see
/// [`rehome_to_host_netns`]). `-1` until [`setup_netns`] runs (or if it isn't a
/// netns sandbox). An fd is process-wide, usable by any servicer thread.
static HOST_NETNS_FD: std::sync::atomic::AtomicI32 = std::sync::atomic::AtomicI32::new(-1);
static SANDBOX_NETNS_FD: std::sync::atomic::AtomicI32 = std::sync::atomic::AtomicI32::new(-1);

/// Route guest TCP loopback through the supervisor-OWNED in-process `LoopNet`
/// instead of the real kernel `lo`. OFF by default: a guest's `127.0.0.1` is the
/// real kernel loopback of its network namespace (its own netns by default, or the
/// host's under `without_netns`) — full mac/linux/VM parity (all protocols,
/// cross-process, host-reachable via `expose_tcp`/published ports, isolated
/// per-netns). The owned `LoopNet` is the NEV warm-restore mechanism: an opt-in
/// (`SandboxCfg::own_loopback`) for pools that must warm-restore a LIVE loopback
/// connection across a fork-from-checkpoint, where the connection state must live
/// in the supervisor (serializable) rather than the kernel. Set once in
/// `setup_sandbox_env`; read by the BIND/CONNECT loopback switch.
static OWN_LOOPBACK: AtomicBool = AtomicBool::new(false);
fn own_loopback() -> bool {
    OWN_LOOPBACK.load(Ordering::Relaxed)
}

/// Move this process into a fresh, ISOLATED network namespace and bring loopback
/// up. Called once in the supervisor (before it spawns servicers / forks cells),
/// so the whole sandbox tree — supervisor sockets, servicers, cells — lives in
/// its own netns: the tenant gets its own port space (no cross-tenant bind
/// collisions) and its own `127.0.0.1` (it can't reach another tenant's or the
/// host's loopback services). A socket's netns is fixed at creation, so the
/// servicers (which `socket()` on the guest's behalf) need no per-call swap for
/// the ISOLATED (bind/listen/loopback) case.
///
/// EGRESS to non-loopback destinations is handled by [`rehome_to_host_netns`]
/// (the servicer opens that one socket back in the host netns), so a netns
/// sandbox reaches the outside world with the host's source IP (masquerade
/// semantics) WITHOUT veth/NAT — fitting the supervisor-mediates-every-socket
/// design. We save fds to both namespaces here for that hop.
///
/// Requires root (CAP_SYS_ADMIN); best-effort — a failed unshare leaves the
/// sandbox in the shared host netns (and `*_NETNS_FD` at `-1`, disabling the
/// proxy, which is correct since there's then nothing to isolate from).
fn setup_netns() {
    let nspath = b"/proc/self/ns/net\0";
    let host_fd = unsafe {
        host(
            SYS_OPEN,
            nspath.as_ptr() as u64,
            libc::O_RDONLY as u64,
            0,
            0,
            0,
            0,
        )
    };
    let r = unsafe { host(SYS_UNSHARE, libc::CLONE_NEWNET as u64, 0, 0, 0, 0, 0) };
    if r < 0 {
        if host_fd >= 0 {
            unsafe { host(SYS_CLOSE, host_fd as u64, 0, 0, 0, 0, 0) };
        }
        if unsafe { TRACE } {
            log(b"[supervisor] netns unshare failed; running in the shared host netns\n");
        }
        return;
    }
    netns_bring_up_lo();
    let sb_fd = unsafe {
        host(
            SYS_OPEN,
            nspath.as_ptr() as u64,
            libc::O_RDONLY as u64,
            0,
            0,
            0,
            0,
        )
    };
    if host_fd >= 0 && sb_fd >= 0 {
        HOST_NETNS_FD.store(host_fd as i32, Ordering::Relaxed);
        SANDBOX_NETNS_FD.store(sb_fd as i32, Ordering::Relaxed);
    } else {
        // Couldn't capture both ns fds: keep isolation, but no egress proxy.
        if host_fd >= 0 {
            unsafe { host(SYS_CLOSE, host_fd as u64, 0, 0, 0, 0, 0) };
        }
        if sb_fd >= 0 {
            unsafe { host(SYS_CLOSE, sb_fd as u64, 0, 0, 0, 0, 0) };
        }
    }
}

/// Re-home a socket from the (isolated, route-less) sandbox netns into the HOST
/// netns by recreating it there and `dup2`ing it onto the same host fd — the
/// guest's virtual fd is unchanged, so this is transparent. Two uses, both in a
/// netns sandbox:
///   - EGRESS: a connect/sendto to a non-loopback destination, so it leaves with
///     the HOST's source IP (masquerade semantics) instead of dying route-less.
///   - INGRESS: a bind to a PUBLISHED port, so the listener is host-reachable
///     instead of buried in the sandbox netns.
/// Loopback / non-published binds are NOT re-homed (they stay isolated). No-op
/// outside a netns sandbox (`*_NETNS_FD == -1`). The servicer always returns to
/// the sandbox netns so its other (isolated) sockets are unaffected.
fn rehome_to_host_netns(old: i32) {
    let host_ns = HOST_NETNS_FD.load(Ordering::Relaxed);
    let sb_ns = SANDBOX_NETNS_FD.load(Ordering::Relaxed);
    if host_ns < 0 || sb_ns < 0 {
        return;
    }
    // Idempotent: if the socket is ALREADY in the host netns (an earlier connect /
    // sendto re-homed it), don't recreate it — doing so would drop its source port
    // and any in-flight reply. Critical for UDP, where a resolver sends repeatedly.
    if socket_in_host_netns(old) {
        return;
    }
    // Domain/type/protocol of the existing socket (so the host-netns one matches).
    let dom = getsockopt_int(old, 39); // SO_DOMAIN
    let typ = getsockopt_int(old, 3); //  SO_TYPE
    if dom < 0 || typ < 0 {
        return;
    }
    let proto = getsockopt_int(old, 38).max(0); // SO_PROTOCOL
    let fl = unsafe { host(SYS_FCNTL, old as u64, libc::F_GETFL as u64, 0, 0, 0, 0) };
    unsafe {
        if host(
            SYS_SETNS,
            host_ns as u64,
            libc::CLONE_NEWNET as u64,
            0,
            0,
            0,
            0,
        ) < 0
        {
            return;
        }
        let new = host(SYS_SOCKET, dom as u64, typ as u64, proto as u64, 0, 0, 0);
        // Always hop back so this servicer's other (isolated) sockets stay in-netns.
        host(
            SYS_SETNS,
            sb_ns as u64,
            libc::CLONE_NEWNET as u64,
            0,
            0,
            0,
            0,
        );
        if new < 0 {
            return;
        }
        if fl >= 0 {
            host(
                SYS_FCNTL,
                new as u64,
                libc::F_SETFL as u64,
                fl as u64,
                0,
                0,
                0,
            ); // keep O_NONBLOCK
        }
        host(SYS_DUP2, new as u64, old as u64, 0, 0, 0, 0); // guest fd now → host-netns socket
        host(SYS_CLOSE, new as u64, 0, 0, 0, 0, 0);
    }
}
/// Is socket `fd` already in the host network namespace? Uses `SIOCGSKNS` to get
/// an fd to the socket's netns and compares its (dev, ino) to the host netns —
/// stateless, so re-homing stays idempotent without tracking per-fd flags.
fn socket_in_host_netns(fd: i32) -> bool {
    let host_ns = HOST_NETNS_FD.load(Ordering::Relaxed);
    if host_ns < 0 {
        return false;
    }
    const SIOCGSKNS: u64 = 0x894C;
    unsafe {
        let nsfd = host(SYS_IOCTL, fd as u64, SIOCGSKNS, 0, 0, 0, 0);
        if nsfd < 0 {
            return false;
        }
        let sock_ns = stat_dev_ino(nsfd as i32);
        host(SYS_CLOSE, nsfd as u64, 0, 0, 0, 0, 0);
        let host_id = stat_dev_ino(host_ns);
        sock_ns.is_some() && sock_ns == host_id
    }
}
/// `(st_dev, st_ino)` of an fd via `fstat` (x86_64 `struct stat`: dev@0, ino@8).
fn stat_dev_ino(fd: i32) -> Option<(u64, u64)> {
    let mut st = [0u8; 144];
    let r = unsafe { host(SYS_FSTAT, fd as u64, st.as_mut_ptr() as u64, 0, 0, 0, 0) };
    if r < 0 {
        return None;
    }
    let dev = u64::from_le_bytes(st[0..8].try_into().unwrap());
    let ino = u64::from_le_bytes(st[8..16].try_into().unwrap());
    Some((dev, ino))
}
/// `getsockopt(fd, SOL_SOCKET, optname)` returning a single `int` (or `-1`).
fn getsockopt_int(fd: i32, optname: i32) -> i32 {
    let mut val: i32 = -1;
    let mut len: u32 = 4;
    let r = unsafe {
        host(
            SYS_GETSOCKOPT,
            fd as u64,
            1, // SOL_SOCKET
            optname as u64,
            &mut val as *mut i32 as u64,
            &mut len as *mut u32 as u64,
            0,
        )
    };
    if r < 0 {
        -1
    } else {
        val
    }
}

/// `sin_port`/`sin6_port` are network byte order; `sa_family` is host order.
fn parse_connect_addr(addr: &[u8]) -> Option<std::net::SocketAddr> {
    if addr.len() < 2 {
        return None;
    }
    let family = u16::from_ne_bytes([addr[0], addr[1]]);
    match family {
        2 => {
            // AF_INET: family(2) port(2,BE) addr(4)
            if addr.len() < 8 {
                return None;
            }
            let port = u16::from_be_bytes([addr[2], addr[3]]);
            let ip = std::net::Ipv4Addr::new(addr[4], addr[5], addr[6], addr[7]);
            Some(std::net::SocketAddr::from((ip, port)))
        }
        10 => {
            // AF_INET6: family(2) port(2,BE) flowinfo(4) addr(16)
            if addr.len() < 24 {
                return None;
            }
            let port = u16::from_be_bytes([addr[2], addr[3]]);
            let mut o = [0u8; 16];
            o.copy_from_slice(&addr[8..24]);
            Some(std::net::SocketAddr::from((
                std::net::Ipv6Addr::from(o),
                port,
            )))
        }
        _ => None,
    }
}

/// Supervisor-local `struct iovec` for the single-segment bounce buffer passed to
/// the host `sendmsg`/`recvmsg` (the guest's multi-segment iov is gathered into /
/// scattered from one contiguous buffer).
#[repr(C)]
struct LocalIov {
    base: u64,
    len: u64,
}
/// Supervisor-local `struct msghdr` (x86_64 layout, 56 bytes) for the host
/// `sendmsg`/`recvmsg`.
#[repr(C)]
struct LocalMsghdr {
    name: u64,
    namelen: u32,
    _p: u32,
    iov: u64,
    iovlen: u64,
    control: u64,
    controllen: u64,
    flags: i32,
    _p2: i32,
}

// SCM_RIGHTS/SCM_CREDENTIALS ancillary data across delegated sendmsg/recvmsg. The
// supervisor owns every host fd, so a cell passing fds over an AF_UNIX socket
// (Chromium's Mojo channel hands its renderer/GPU children the IPC + shared-
// memory HANDLES exactly this way) must, on SEND, translate the guest fd numbers
// in its SCM_RIGHTS cmsg to the owning host fds; and on RECV, install the
// received host fds into the RECEIVING pid's fd table and rewrite the cmsg with
// the freshly-assigned guest fd numbers. For SCM_CREDENTIALS, delegated sendmsg
// must not let the kernel report the supervisor thread as the sender; AF_UNIX
// messages carry an explicit ucred with the sending cell's pid so SO_PASSCRED
// receivers (Chromium's zygote PID oracle) see the same pid they would on Linux.
const SCM_SOL_SOCKET: i32 = 1;
const SCM_RIGHTS_TYPE: i32 = 1;
const SCM_CREDENTIALS_TYPE: i32 = 2;
/// Cap on a translated control buffer (room for ~2000 fds — Mojo passes a few).
const SCM_CTL_CAP: usize = 8192;

#[inline]
fn cmsg_align(n: usize) -> usize {
    (n + 7) & !7
}

/// Count the SCM_RIGHTS fds encoded in a (host or guest) control buffer — sum of
/// `(cmsg_len - 16) / 4` over every SCM_RIGHTS cmsg. Used only for the end-to-end
/// fd-conservation accounting (SCM_FDS_SENT / SCM_FDS_RECVD); never affects control
/// flow. Mirrors the cmsg-walk in scm_send_control/scm_recv_control.
fn scm_count_fds(ctl: &[u8]) -> usize {
    let mut n = 0usize;
    let mut off = 0usize;
    while off + 16 <= ctl.len() {
        let clen = u64::from_le_bytes(ctl[off..off + 8].try_into().unwrap()) as usize;
        if clen < 16 || off + clen > ctl.len() {
            break;
        }
        let level = i32::from_le_bytes(ctl[off + 8..off + 12].try_into().unwrap());
        let ctype = i32::from_le_bytes(ctl[off + 12..off + 16].try_into().unwrap());
        if level == SCM_SOL_SOCKET && ctype == SCM_RIGHTS_TYPE {
            n += (clen - 16) / 4;
        }
        off += cmsg_align(clen);
    }
    n
}

/// Translate the SCM_RIGHTS fds in a GUEST control buffer into a host control
/// buffer (guest fd → owning host fd). `Err(-EBADF)` if a passed fd is unknown.
/// Non-SCM_RIGHTS cmsgs are dropped.
fn scm_send_control(pid: i32, gctl: &[u8]) -> Result<Vec<u8>, i64> {
    let mut out: Vec<u8> = Vec::new();
    let mut off = 0usize;
    while off + 16 <= gctl.len() {
        let clen = u64::from_le_bytes(gctl[off..off + 8].try_into().unwrap()) as usize;
        if clen < 16 || off + clen > gctl.len() {
            break;
        }
        let level = i32::from_le_bytes(gctl[off + 8..off + 12].try_into().unwrap());
        let ctype = i32::from_le_bytes(gctl[off + 12..off + 16].try_into().unwrap());
        if level == SCM_SOL_SOCKET && ctype == SCM_RIGHTS_TYPE {
            let data = &gctl[off + 16..off + clen];
            let nfds = data.len() / 4;
            let hdr = out.len();
            out.extend_from_slice(&0u64.to_le_bytes()); // cmsg_len (patched below)
            out.extend_from_slice(&SCM_SOL_SOCKET.to_le_bytes());
            out.extend_from_slice(&SCM_RIGHTS_TYPE.to_le_bytes());
            for i in 0..nfds {
                let gfd = i32::from_le_bytes(data[i * 4..i * 4 + 4].try_into().unwrap());
                match fd_host(pid, gfd) {
                    Some(h) => out.extend_from_slice(&h.to_le_bytes()),
                    None => return Err(-9), // -EBADF: the cell passed a bad fd
                }
            }
            let clen_h = (16 + nfds * 4) as u64;
            out[hdr..hdr + 8].copy_from_slice(&clen_h.to_le_bytes());
            while out.len() % 8 != 0 {
                out.push(0);
            }
        }
        off += cmsg_align(clen);
    }
    Ok(out)
}

fn append_scm_credentials(ctl: &mut Vec<u8>, pid: i32) {
    let hdr = ctl.len();
    ctl.extend_from_slice(&0u64.to_le_bytes());
    ctl.extend_from_slice(&SCM_SOL_SOCKET.to_le_bytes());
    ctl.extend_from_slice(&SCM_CREDENTIALS_TYPE.to_le_bytes());
    ctl.extend_from_slice(&pid.to_le_bytes());
    ctl.extend_from_slice(&0u32.to_le_bytes()); // uid
    ctl.extend_from_slice(&0u32.to_le_bytes()); // gid
    let clen = 28u64; // cmsghdr(16) + ucred { pid:i32, uid:u32, gid:u32 }
    ctl[hdr..hdr + 8].copy_from_slice(&clen.to_le_bytes());
    while ctl.len() % 8 != 0 {
        ctl.push(0);
    }
}

/// Result of delivering a received SCM_RIGHTS control buffer to a cell.
struct ScmRecv {
    /// Rewritten guest control buffer (cmsg headers + guest fd numbers).
    gctl: Vec<u8>,
    /// Number of fds actually installed in the cell's fd table and reflected in
    /// `gctl` (== fds the guest will see via CMSG_NXTHDR).
    installed: usize,
    /// True iff at least one host fd could NOT be delivered because the cell's
    /// ancillary buffer (`guest_cap`) was too small (kernel sets MSG_CTRUNC). Those
    /// undeliverable host fds are CLOSED here, never installed and never leaked.
    truncated: bool,
}

/// Install the SCM_RIGHTS fds from a RECEIVED host control buffer into `pid`'s fd
/// table and return a guest control buffer carrying the new guest fd numbers plus
/// any SCM_CREDENTIALS cmsgs the receiver requested via SO_PASSCRED.
///
/// `guest_cap` is the cell's `msg_controllen` — the exact byte budget the cell gave
/// for ancillary data. We honor it EXACTLY the way the kernel does on a too-small
/// receive buffer: deliver as many whole fds as fit, set the truncated flag (the
/// caller raises MSG_CTRUNC), and — critically — CLOSE every host fd we cannot
/// deliver instead of installing it. The naive prior version installed EVERY host fd
/// unconditionally and then let the caller truncate the rewritten cmsg with
/// `min(ctl_cap)`, so any fd past the cell's budget was installed-but-invisible: a
/// silent fd leak in the cell table AND, worse, a desync where the byte stream
/// advanced (the message was consumed) but an attached memfd never reached the
/// guest. For ipcz that drops an AddBlockBuffer/ProvideMemory buffer → the matching
/// parcel parks on `is_pending()` forever (the Chromium 15s "no connection" hang).
/// In practice `guest_cap` is never exceeded for the Mojo channel (Chromium sizes
/// its cmsg buffer for `kMaxSendmsgHandles`=128 fds), so `truncated` stays false and
/// behavior is byte-identical to before — this just removes the silent-drop footgun
/// and makes any real overflow loud (SCM_RECV_TRUNC) and leak-free.
fn scm_recv_control(pid: i32, hctl: &[u8], guest_cap: usize) -> ScmRecv {
    let mut out: Vec<u8> = Vec::new();
    let mut installed = 0usize;
    let mut truncated = false;
    let mut off = 0usize;
    while off + 16 <= hctl.len() {
        let clen = u64::from_le_bytes(hctl[off..off + 8].try_into().unwrap()) as usize;
        if clen < 16 || off + clen > hctl.len() {
            break;
        }
        let level = i32::from_le_bytes(hctl[off + 8..off + 12].try_into().unwrap());
        let ctype = i32::from_le_bytes(hctl[off + 12..off + 16].try_into().unwrap());
        if level == SCM_SOL_SOCKET && ctype == SCM_RIGHTS_TYPE {
            let data = &hctl[off + 16..off + clen];
            let nfds = data.len() / 4;
            let start = out.len();
            let hdr = out.len();
            out.extend_from_slice(&0u64.to_le_bytes());
            out.extend_from_slice(&SCM_SOL_SOCKET.to_le_bytes());
            out.extend_from_slice(&SCM_RIGHTS_TYPE.to_le_bytes());
            let mut here = 0usize;
            for i in 0..nfds {
                let hfd = i32::from_le_bytes(data[i * 4..i * 4 + 4].try_into().unwrap());
                // Would this fd's 4 cmsg-data bytes still leave a well-formed,
                // in-budget cmsg? Header is 16B; each fd adds 4B; the whole cmsg is
                // 8-byte aligned. Only install if it fits the cell's budget.
                let projected = start + cmsg_align(16 + (here + 1) * 4);
                if projected > guest_cap {
                    // Kernel-faithful: drop the remaining fds and CLOSE them (do not
                    // install — an installed-but-undelivered fd is a leak + a missing
                    // handle for the guest, i.e. a dropped ipcz buffer).
                    truncated = true;
                    unsafe {
                        host(SYS_CLOSE, hfd as u64, 0, 0, 0, 0, 0);
                    }
                    continue;
                }
                let g = fd_install(pid, hfd, 0) as i32;
                if unsafe { IPCTRACE } {
                    let mut st = [0u8; 144];
                    let ok =
                        unsafe { host(SYS_FSTAT, hfd as u64, st.as_mut_ptr() as u64, 0, 0, 0, 0) };
                    let (dev, ino, size) = if ok == 0 {
                        (
                            u64::from_le_bytes(st[0..8].try_into().unwrap()),
                            u64::from_le_bytes(st[8..16].try_into().unwrap()),
                            u64::from_le_bytes(st[48..56].try_into().unwrap()),
                        )
                    } else {
                        (0, 0, 0)
                    };
                    ipc_logf(
                        &[
                            (b"SCMRECV_FD pid=", pid as i64),
                            (b"hfd=", hfd as i64),
                            (b"gfd=", g as i64),
                            (b"dev=", dev as i64),
                            (b"ino=", ino as i64),
                            (b"sz=", size as i64),
                        ],
                        &[],
                    );
                }
                out.extend_from_slice(&g.to_le_bytes());
                here += 1;
                installed += 1;
            }
            if here == 0 {
                out.truncate(start);
            } else {
                let clen_h = (16 + here * 4) as u64;
                out[hdr..hdr + 8].copy_from_slice(&clen_h.to_le_bytes());
                while out.len() % 8 != 0 {
                    out.push(0);
                }
            }
        } else if level == SCM_SOL_SOCKET && ctype == SCM_CREDENTIALS_TYPE && clen >= 28 {
            let aligned = cmsg_align(28);
            if out.len() + aligned > guest_cap {
                truncated = true;
            } else {
                let hdr = out.len();
                out.extend_from_slice(&0u64.to_le_bytes());
                out.extend_from_slice(&SCM_SOL_SOCKET.to_le_bytes());
                out.extend_from_slice(&SCM_CREDENTIALS_TYPE.to_le_bytes());
                out.extend_from_slice(&hctl[off + 16..off + 28]);
                let clen_h = 28u64;
                out[hdr..hdr + 8].copy_from_slice(&clen_h.to_le_bytes());
                while out.len() % 8 != 0 {
                    out.push(0);
                }
            }
        }
        off += cmsg_align(clen);
    }
    ScmRecv {
        gctl: out,
        installed,
        truncated,
    }
}

/// Build an 8-byte poll(2) `struct pollfd { i32 fd; i16 events; i16 revents=0 }`.
#[inline]
fn pollfd_bytes(fd: i32, events: i16) -> [u8; 8] {
    let mut b = [0u8; 8];
    b[0..4].copy_from_slice(&fd.to_le_bytes());
    b[4..6].copy_from_slice(&events.to_le_bytes());
    b
}

/// Wait until `fd` (negative ⇒ omitted) is ready for input, the cancel eventfd
/// `efd` fires (⇒ our cell was reaped), the live-signal interrupt eventfd
/// `intr_efd` fires (⇒ return EINTR to the guest), `timeout_ms` elapses, or a host
/// signal EINTRs. Returns 1=ready, 0=timeout, -1=CANCEL, -2=host EINTR, -3=guest
/// signal interrupt. Eventfds are level-triggered, so a token written before we
/// enter poll() is seen immediately — there is no missed-wakeup window.
unsafe fn wait_or_cancel(fd: i32, efd: i32, intr_efd: i32, timeout_ms: i64) -> i32 {
    let mut pf = [0u8; 24];
    pf[0..8].copy_from_slice(&pollfd_bytes(fd, POLLIN_BIT | POLLPRI_BIT));
    pf[8..16].copy_from_slice(&pollfd_bytes(efd, POLLIN_BIT));
    pf[16..24].copy_from_slice(&pollfd_bytes(intr_efd, POLLIN_BIT));
    let r = host(
        SYS_POLL,
        pf.as_mut_ptr() as u64,
        3,
        timeout_ms as u64,
        0,
        0,
        0,
    );
    if r == EINTR {
        return -2;
    }
    if r == 0 {
        return 0;
    }
    // Cancel wins: entry 1 (the eventfd) has revents at bytes 14..16.
    if i16::from_le_bytes([pf[14], pf[15]]) != 0 {
        return -1;
    }
    if i16::from_le_bytes([pf[22], pf[23]]) != 0 {
        let mut drain = [0u8; 8];
        host(
            SYS_READ,
            intr_efd as u64,
            drain.as_mut_ptr() as u64,
            8,
            0,
            0,
            0,
        );
        let sr = SERVICER_RING.with(|cell| cell.get());
        if !sr.is_null() {
            std::ptr::write_volatile(std::ptr::addr_of_mut!((*sr).intr_pending), 0);
        }
        return -3;
    }
    1
}

/// Cancel-aware blocking host call. Used ONLY by the fd-wait family in `service()`
/// that can block INDEFINITELY on a dead peer (read/recv*/epoll_wait/poll/ppoll/
/// select/pselect6/accept/F_SETLKW/nanosleep). Each variant performs its wait via a
/// poll(2) that ALSO watches THIS servicer's cancel eventfd (`ring.cancel_efd`); if
/// free_slots_of writes that eventfd (our cell was reaped while wedged) the wait
/// returns CANCEL_SENTINEL, which servicer_loop turns into fd_drop + free-this-slot.
/// Race-free (level-triggered eventfd, no signals). `r` is THIS servicer's ring (set
/// in servicer_loop via SERVICER_RING); when there is no eventfd (a unit context) we
/// degrade to a plain retrying host() with no cancellation.
unsafe fn host_cancellable(nr: i64, a: u64, b: u64, c: u64, d: u64, e: u64, f: u64) -> i64 {
    let r = SERVICER_RING.with(|cell| cell.get());
    let (efd, intr_efd) = if r.is_null() {
        (-1, -1)
    } else {
        (
            std::ptr::read_volatile(std::ptr::addr_of!((*r).cancel_efd)),
            std::ptr::read_volatile(std::ptr::addr_of!((*r).intr_efd)),
        )
    };
    if efd < 0 {
        loop {
            let n = host(nr, a, b, c, d, e, f);
            if n != EINTR {
                return n;
            }
        }
    }
    match nr {
        // poll/ppoll/select all funnel here as poll(array@a, nfds@b, tmo_ms@c). Splice
        // the cancel eventfd onto the END of a local copy of the caller's pollfd
        // array, poll that, and (unless cancelled) copy the revents back.
        SYS_POLL => {
            let nfds = b as usize;
            let mut local = vec![0u8; (nfds + 2) * 8];
            if nfds > 0 {
                std::ptr::copy_nonoverlapping(a as *const u8, local.as_mut_ptr(), nfds * 8);
            }
            local[nfds * 8..nfds * 8 + 8].copy_from_slice(&pollfd_bytes(efd, POLLIN_BIT));
            local[nfds * 8 + 8..nfds * 8 + 16].copy_from_slice(&pollfd_bytes(intr_efd, POLLIN_BIT));
            loop {
                let rr = host(
                    SYS_POLL,
                    local.as_mut_ptr() as u64,
                    (nfds + 2) as u64,
                    c,
                    0,
                    0,
                    0,
                );
                if rr == EINTR {
                    continue;
                }
                let cancel = (nfds + 1) * 8;
                let intr = (nfds + 2) * 8;
                if i16::from_le_bytes([local[cancel - 2], local[cancel - 1]]) != 0 {
                    return CANCEL_SENTINEL;
                }
                if i16::from_le_bytes([local[intr - 2], local[intr - 1]]) != 0 {
                    let mut drain = [0u8; 8];
                    host(
                        SYS_READ,
                        intr_efd as u64,
                        drain.as_mut_ptr() as u64,
                        8,
                        0,
                        0,
                        0,
                    );
                    std::ptr::write_volatile(std::ptr::addr_of_mut!((*r).intr_pending), 0);
                    return EINTR;
                }
                if nfds > 0 {
                    std::ptr::copy_nonoverlapping(local.as_ptr(), a as *mut u8, nfds * 8);
                }
                return rr; // the cancel entry was idle ⇒ rr counts only the caller's fds
            }
        }
        // nanosleep(req@a, rem@b) / clock_nanosleep(clk@a, flags@b, req@c, rem@d).
        // No fd — wait on the cancel eventfd for the sleep duration. A delegated sleep
        // is never EINTR'd by a GUEST signal (the guest is parked in its own futex; the
        // signal hits the guest, not this servicer), so a rare supervisor EINTR just
        // retries. On timeout the full sleep elapsed (rem=0).
        SYS_NANOSLEEP | SYS_CLOCK_NANOSLEEP => {
            let (clk, flags, req_ptr, rem_ptr) = if nr == SYS_NANOSLEEP {
                (0u64, 0u64, a, b)
            } else {
                (a, b, c, d)
            };
            let secs = i64::from_le_bytes(*(req_ptr as *const [u8; 8]));
            let nsecs = i64::from_le_bytes(*((req_ptr + 8) as *const [u8; 8]));
            // clock_nanosleep with TIMER_ABSTIME (flag bit 0): req is an ABSOLUTE
            // deadline; convert to a duration via clock_gettime(clk).
            let (mut tsec, mut tnsec) = (secs, nsecs);
            if nr == SYS_CLOCK_NANOSLEEP && (flags & 1) != 0 {
                // TIMER_ABSTIME: req is an ABSOLUTE deadline; convert to a relative
                // duration by subtracting "now" IN THE SAME TIMEBASE the guest used to
                // compute the deadline. For CLOCK_MONOTONIC/CLOCK_BOOTTIME the guest
                // clock is VIRTUALIZED (guest = host − GUEST_MONOTONIC_BASE_NS, see
                // guest_monotonic_timespec); subtracting the RAW host clock here mixes
                // timebases → relative ≈ delay − base (clamped to 0) → an absolute
                // monotonic sleep returns INSTANTLY (same bug class as the futex
                // deadline fix). Use the virtualized clock for those; base is seeded in
                // setup_sandbox_env BEFORE the cell fork, so the servicer's value
                // matches the cell's. CLOCK_REALTIME is not virtualized → raw host.
                let (nsec0, nnsec0) =
                    if clk as i32 == libc::CLOCK_MONOTONIC || clk as i32 == libc::CLOCK_BOOTTIME {
                        let ts = guest_monotonic_timespec();
                        (
                            i64::from_le_bytes(ts[0..8].try_into().unwrap()),
                            i64::from_le_bytes(ts[8..16].try_into().unwrap()),
                        )
                    } else {
                        let mut now = [0u8; 16];
                        host(SYS_CLOCK_GETTIME, clk, now.as_mut_ptr() as u64, 0, 0, 0, 0);
                        (
                            i64::from_le_bytes(now[0..8].try_into().unwrap()),
                            i64::from_le_bytes(now[8..16].try_into().unwrap()),
                        )
                    };
                tsec = secs - nsec0;
                tnsec = nsecs - nnsec0;
            }
            let mut tmo_ms = tsec.saturating_mul(1000).saturating_add(tnsec / 1_000_000);
            if tmo_ms < 0 {
                tmo_ms = 0;
            } else if tmo_ms == 0 && (tsec > 0 || tnsec > 0) {
                tmo_ms = 1;
            }
            let tmo_ms = tmo_ms.clamp(0, i32::MAX as i64);
            loop {
                match wait_or_cancel(-1, efd, intr_efd, tmo_ms) {
                    -1 => return CANCEL_SENTINEL,
                    -3 => return EINTR,
                    -2 => continue,
                    _ => {
                        if rem_ptr != 0 {
                            std::ptr::write_bytes(rem_ptr as *mut u8, 0, 16);
                        }
                        return 0;
                    }
                }
            }
        }
        // epoll_wait(epfd@a, events@b, max@c, tmo_ms@d). The epoll fd is itself
        // pollable, so wait on [epfd, cancel_efd] then drain with a 0-timeout call.
        SYS_EPOLL_WAIT | SYS_EPOLL_PWAIT => {
            let tmo = d as i64;
            loop {
                match wait_or_cancel(a as i32, efd, intr_efd, tmo) {
                    -1 => return CANCEL_SENTINEL,
                    -3 => return EINTR,
                    0 => return 0,
                    -2 => continue,
                    _ => {
                        let n = host(SYS_EPOLL_WAIT, a, b, c, 0, 0, 0);
                        if n != 0 {
                            return n;
                        }
                    }
                }
            }
        }
        // Record-lock fcntl (fd@a, cmd@b, flock@c). The call site routes the WHOLE
        // lock family here, but only the BLOCKING cmds (F_SETLKW / F_OFD_SETLKW) can
        // strand: convert those to their non-blocking variant and poll the lock with a
        // short cancel-aware wait between tries (no fd-readiness exists for a lock; a
        // dead holder's lock is released by the kernel on its death, so this converges).
        // Every other lock cmd (GETLK / SETLK / OFD_GETLK / OFD_SETLK) is already
        // non-blocking — pass it straight through with its ORIGINAL cmd, unretried, so
        // F_GETLK still QUERIES and F_SETLK still returns EAGAIN immediately.
        SYS_FCNTL => {
            let nb_cmd = match b {
                F_SETLKW => F_SETLK,
                F_OFD_SETLKW => F_OFD_SETLK,
                _ => return host(SYS_FCNTL, a, b, c, d, e, f),
            };
            loop {
                let n = host(SYS_FCNTL, a, nb_cmd, c, 0, 0, 0);
                if n != EAGAIN && n != -13 {
                    return n; // acquired, or a real error (not EAGAIN/EACCES contention)
                }
                match wait_or_cancel(-1, efd, intr_efd, 20) {
                    -1 => return CANCEL_SENTINEL,
                    -3 => return EINTR,
                    _ => continue,
                }
            }
        }
        // read/pread/getdents/recv*/accept (fd@a). Make the fd non-blocking, try, and
        // on EAGAIN wait on [fd, cancel_efd]; retry on readiness, abandon on cancel.
        // Restore the fd's original flags before returning.
        _ => {
            let fd = a as i32;
            let saved = host(SYS_FCNTL, fd as u64, libc::F_GETFL as u64, 0, 0, 0, 0);
            // If the GUEST already marked this fd O_NONBLOCK it expects EAGAIN on an
            // empty fd and must NEVER be made to block. Drain loops rely on this:
            // ipcz/Chromium's SocketTransport::ClearIOThreadSignal does
            // `do { read(1) } while (ret == 1)` and terminates only on EAGAIN — if we
            // block there, the IO thread never observes shutdown, the join never
            // completes, and the whole multi-process connect (mojo bootstrap) hangs.
            // Only fds the guest left BLOCKING get the force-nonblock + readiness-wait
            // treatment (which is what gives them cancel-aware blocking semantics).
            let guest_nonblock = saved >= 0 && (saved as u64 & libc::O_NONBLOCK as u64) != 0;
            // Per-call MSG_DONTWAIT (0x40): recv-family calls can request a single
            // non-blocking read even on a BLOCKING fd (flags arg: recvmsg→c, recvfrom/
            // recv/recvmmsg→d). The guest explicitly asked not to block, so EAGAIN MUST
            // be returned straight through — NOT turned into an indefinite readiness
            // wait. This was honored for LoopNet fds (loop_recvmsg) but the host-fd arm
            // here ignored it, so ipcz/Chromium drain loops `do{recv(MSG_DONTWAIT)}
            // while(n==1)` (which terminate only on EAGAIN) blocked forever → the mojo
            // IO thread wedged → 15s "no connection". An AF_UNIX socketpair is a host fd,
            // so the mojo channel hits exactly this arm.
            const MSG_DONTWAIT: u64 = 0x40;
            let percall_nonblock = match nr {
                SYS_RECVMSG => (c & MSG_DONTWAIT) != 0,
                SYS_RECVFROM | SYS_RECVMMSG => (d & MSG_DONTWAIT) != 0,
                _ => false,
            };
            let nonblock = guest_nonblock || percall_nonblock;
            if saved >= 0 && !nonblock {
                host(
                    SYS_FCNTL,
                    fd as u64,
                    libc::F_SETFL as u64,
                    (saved as u64) | (libc::O_NONBLOCK as u64),
                    0,
                    0,
                    0,
                );
            }
            let res = loop {
                let n = host(nr, a, b, c, d, e, f);
                if n != EAGAIN && n != EINTR {
                    break n;
                }
                if n == EINTR {
                    continue;
                }
                // EAGAIN: a non-blocking call (fd O_NONBLOCK or per-call MSG_DONTWAIT)
                // returns it straight through; a genuinely BLOCKING call waits for
                // readiness (cancel-aware) and retries.
                if nonblock {
                    break n;
                }
                match wait_or_cancel(fd, efd, intr_efd, -1) {
                    -1 => break CANCEL_SENTINEL,
                    -3 => break EINTR,
                    _ => continue,
                }
            };
            if saved >= 0 && !nonblock {
                host(
                    SYS_FCNTL,
                    fd as u64,
                    libc::F_SETFL as u64,
                    saved as u64,
                    0,
                    0,
                    0,
                );
            }
            res
        }
    }
}

/// Perform one delegated syscall on the cell's behalf. Buffers that are cell
/// pointers are bounced through supervisor memory via process_vm_*.
// ─── #34 keystone: poll/select over a fd set that includes owned loop fds ────
//
// Real servers (node/libuv, Postgres, nginx) are event-loop driven; their sockets
// must be poll/epoll-able. A loop fd has no host fd, so the plain path maps it to a
// bogus host fd → POLLNVAL. `poll_merge` instead derives a loop fd's readiness from
// LoopNet (level-triggered) and has the kernel poll ALSO watch each loop socket's
// readiness eventfd, so the call blocks until a host fd OR a loop fd becomes ready.

const POLLIN_B: u16 = 0x001;
const POLLPRI_B: u16 = 0x002;
const POLLOUT_B: u16 = 0x004;
const POLLNVAL_B: u16 = 0x020;

enum PollKind {
    Loop(netstack::SockId),
    Host(i32),
    Nval,
    Ignore,
}

/// Level-triggered revents for a loop socket, masked by the requested `events`.
fn loop_revents(ls: &LoopState, sid: netstack::SockId, events: u16) -> u16 {
    let mut r = 0u16;
    if (events & POLLIN_B) != 0 && ls.net.readable(sid) {
        r |= POLLIN_B;
    }
    if (events & POLLOUT_B) != 0 && ls.net.writable(sid) {
        r |= POLLOUT_B;
    }
    r
}

/// Loop-aware poll CORE shared by `poll_merge` (SYS_POLL/PPOLL) and the
/// SYS_SELECT/PSELECT6 handler. Given guest `(fd, events)` entries, polls host fds
/// AND owned-loopback fds — a loop fd has no host fd, so its readiness is derived
/// from LoopNet (`loop_revents`: data buffered, EOF, OR a listener with a pending
/// connection in its accept backlog), NOT a bogus host poll. Returns `(ret, revents)`
/// where `ret` is the ready count, a negative errno, or `CANCEL_SENTINEL`; `revents`
/// is per-entry, same order. (select/pselect6 previously mapped loop fds to a fake
/// host fd ⇒ POLLNVAL ⇒ a loop *listen* socket read "ready" on EVERY call, so a guest
/// `select()`+`accept()` loop — e.g. Postgres' postmaster ServerLoop — wedged forever
/// in a blocking accept() on an empty backlog and never reached "ready to accept
/// connections".)
fn poll_merge_entries(pid: i32, gfds: &[(i32, u16)], timeout_ms: i64) -> (i64, Vec<u16>) {
    let n = gfds.len();
    let mut ents: Vec<(usize, PollKind, u16)> = Vec::with_capacity(n);
    for (i, &(g, ev)) in gfds.iter().enumerate() {
        let k = if g < 0 {
            PollKind::Ignore
        } else if let Some(sid) = fd_loop(pid, g) {
            PollKind::Loop(sid)
        } else if let Some(h) = fd_host(pid, g) {
            PollKind::Host(h)
        } else {
            PollKind::Nval
        };
        ents.push((i, k, ev));
    }
    let deadline = if timeout_ms < 0 {
        None
    } else {
        Some(now_ns().saturating_add((timeout_ms as u64).saturating_mul(1_000_000)))
    };
    loop {
        let mut rev = vec![0u16; n];
        let mut any_loop_ready = false;
        {
            let ls = loop_state().lock().unwrap();
            for (i, k, ev) in &ents {
                match k {
                    PollKind::Loop(sid) => {
                        let r = loop_revents(&ls, *sid, *ev);
                        if r != 0 {
                            any_loop_ready = true;
                        }
                        rev[*i] = r;
                    }
                    PollKind::Nval => rev[*i] = POLLNVAL_B,
                    _ => {}
                }
            }
        }
        let mut tp: Vec<u8> = Vec::new();
        // (tp_slot, original_index) for each HOST fd. The tp array interleaves host
        // fds AND loop readiness efds in `ents` order, so the fold MUST key off the
        // real tp slot — not the host-only ordinal — or a loop efd sitting before a
        // host fd mis-attributes its revents onto that host fd (e.g. a ready TCP-loop
        // listener spuriously marking a sibling AF_UNIX listener readable → a blocking
        // host accept on a socket with no pending connection).
        let mut host_map: Vec<(usize, usize)> = Vec::new();
        let mut efds: Vec<(usize, i32)> = Vec::new();
        {
            let ls = loop_state().lock().unwrap();
            for (i, k, ev) in &ents {
                match k {
                    PollKind::Host(h) => {
                        host_map.push((tp.len() / 8, *i));
                        tp.extend_from_slice(&h.to_le_bytes());
                        tp.extend_from_slice(&ev.to_le_bytes());
                        tp.extend_from_slice(&0u16.to_le_bytes());
                    }
                    PollKind::Loop(sid) => {
                        if let Some(&efd) = ls.efds.get(sid) {
                            let slot = tp.len() / 8;
                            tp.extend_from_slice(&efd.to_le_bytes());
                            tp.extend_from_slice(&POLLIN_B.to_le_bytes());
                            tp.extend_from_slice(&0u16.to_le_bytes());
                            efds.push((slot, efd));
                        }
                    }
                    _ => {}
                }
            }
        }
        let nt = (tp.len() / 8) as u64;
        let t: i64 = if any_loop_ready {
            0
        } else {
            match deadline {
                None => -1,
                Some(d) => {
                    let now = now_ns();
                    if now >= d {
                        0
                    } else {
                        ((d - now) / 1_000_000).min(i32::MAX as u64) as i64
                    }
                }
            }
        };
        let r =
            unsafe { host_cancellable(SYS_POLL, tp.as_mut_ptr() as u64, nt, t as u64, 0, 0, 0) };
        if r == CANCEL_SENTINEL {
            return (CANCEL_SENTINEL, rev);
        }
        for &(slot, efd) in &efds {
            let revents = u16::from_le_bytes(tp[slot * 8 + 6..slot * 8 + 8].try_into().unwrap());
            if (revents & POLLIN_B) != 0 {
                let mut sink = [0u8; 8];
                unsafe { host(SYS_READ, efd as u64, sink.as_mut_ptr() as u64, 8, 0, 0, 0) };
            }
        }
        for &(ts, oi) in &host_map {
            rev[oi] |= u16::from_le_bytes(tp[ts * 8 + 6..ts * 8 + 8].try_into().unwrap());
        }
        {
            let ls = loop_state().lock().unwrap();
            for (i, k, ev) in &ents {
                if let PollKind::Loop(sid) = k {
                    rev[*i] = loop_revents(&ls, *sid, *ev);
                }
            }
        }
        let mut ready = 0i64;
        for &v in &rev {
            if v != 0 {
                ready += 1;
            }
        }
        let elapsed = matches!(deadline, Some(d) if now_ns() >= d);
        if ready > 0 || elapsed || r < 0 {
            return (if r < 0 { r } else { ready }, rev);
        }
        // spurious wake (eventfd signaled but no requested event satisfiable) → retry
    }
}

/// `poll(2)` over a guest pollfd array (`pfds_ptr`, `nfds` entries) that may mix
/// host and owned-loopback fds. `timeout_ms` < 0 ⇒ block forever. Returns the
/// ready count (or `CANCEL_SENTINEL`). Used by SYS_POLL/PPOLL when a loop fd is in
/// the set; pure-host sets keep the simpler existing path.
fn poll_merge(pid: i32, pfds_ptr: u64, nfds: u64, timeout_ms: i64) -> i64 {
    let n = (nfds.min(1024)) as usize;
    let mut fds = vec![0u8; n * 8];
    vm_read(pid, pfds_ptr, &mut fds);
    let mut ents: Vec<(usize, PollKind, u16)> = Vec::with_capacity(n);
    for i in 0..n {
        let g = i32::from_le_bytes(fds[i * 8..i * 8 + 4].try_into().unwrap());
        let ev = u16::from_le_bytes(fds[i * 8 + 4..i * 8 + 6].try_into().unwrap());
        let k = if g < 0 {
            PollKind::Ignore
        } else if let Some(sid) = fd_loop(pid, g) {
            PollKind::Loop(sid)
        } else if let Some(h) = fd_host(pid, g) {
            PollKind::Host(h)
        } else {
            PollKind::Nval
        };
        ents.push((i, k, ev));
    }
    let deadline = if timeout_ms < 0 {
        None
    } else {
        Some(now_ns().saturating_add((timeout_ms as u64).saturating_mul(1_000_000)))
    };
    loop {
        let mut rev = vec![0u16; n];
        // Loop fds: level readiness from LoopNet. Unmapped fds: POLLNVAL.
        let mut any_loop_ready = false;
        {
            let ls = loop_state().lock().unwrap();
            for (i, k, ev) in &ents {
                match k {
                    PollKind::Loop(sid) => {
                        let r = loop_revents(&ls, *sid, *ev);
                        if r != 0 {
                            any_loop_ready = true;
                        }
                        rev[*i] = r;
                    }
                    PollKind::Nval => rev[*i] = POLLNVAL_B,
                    _ => {}
                }
            }
        }
        // Transient host pollfd set: host entries (their events) ++ each loop
        // socket's readiness eventfd (POLLIN) so the kernel wakes on a loop event.
        let mut tp: Vec<u8> = Vec::new();
        // (tp_slot, original_index) for each HOST fd. The tp array interleaves host
        // fds AND loop readiness efds in `ents` order, so the fold MUST key off the
        // real tp slot — not the host-only ordinal — or a loop efd sitting before a
        // host fd mis-attributes its revents onto that host fd (e.g. a ready TCP-loop
        // listener spuriously marking a sibling AF_UNIX listener readable → a blocking
        // host accept on a socket with no pending connection).
        let mut host_map: Vec<(usize, usize)> = Vec::new();
        let mut efds: Vec<(usize, i32)> = Vec::new();
        {
            let ls = loop_state().lock().unwrap();
            for (i, k, ev) in &ents {
                match k {
                    PollKind::Host(h) => {
                        host_map.push((tp.len() / 8, *i));
                        tp.extend_from_slice(&h.to_le_bytes());
                        tp.extend_from_slice(&ev.to_le_bytes());
                        tp.extend_from_slice(&0u16.to_le_bytes());
                    }
                    PollKind::Loop(sid) => {
                        if let Some(&efd) = ls.efds.get(sid) {
                            let slot = tp.len() / 8;
                            tp.extend_from_slice(&efd.to_le_bytes());
                            tp.extend_from_slice(&POLLIN_B.to_le_bytes());
                            tp.extend_from_slice(&0u16.to_le_bytes());
                            efds.push((slot, efd));
                        }
                    }
                    _ => {}
                }
            }
        }
        let nt = (tp.len() / 8) as u64;
        let t: i64 = if any_loop_ready {
            0
        } else {
            match deadline {
                None => -1,
                Some(d) => {
                    let now = now_ns();
                    if now >= d {
                        0
                    } else {
                        ((d - now) / 1_000_000).min(i32::MAX as u64) as i64
                    }
                }
            }
        };
        let r =
            unsafe { host_cancellable(SYS_POLL, tp.as_mut_ptr() as u64, nt, t as u64, 0, 0, 0) };
        if r == CANCEL_SENTINEL {
            return CANCEL_SENTINEL;
        }
        // Drain any signaled loop eventfds (level readiness is re-derived below).
        for &(slot, efd) in &efds {
            let revents = u16::from_le_bytes(tp[slot * 8 + 6..slot * 8 + 8].try_into().unwrap());
            if (revents & POLLIN_B) != 0 {
                let mut sink = [0u8; 8];
                unsafe { host(SYS_READ, efd as u64, sink.as_mut_ptr() as u64, 8, 0, 0, 0) };
            }
        }
        // Host revents back into the guest indices.
        for &(ts, oi) in &host_map {
            rev[oi] |= u16::from_le_bytes(tp[ts * 8 + 6..ts * 8 + 8].try_into().unwrap());
        }
        // Re-derive loop level readiness (a wake may have changed it).
        {
            let ls = loop_state().lock().unwrap();
            for (i, k, ev) in &ents {
                if let PollKind::Loop(sid) = k {
                    rev[*i] = loop_revents(&ls, *sid, *ev);
                }
            }
        }
        let mut ready = 0i64;
        for &v in &rev {
            if v != 0 {
                ready += 1;
            }
        }
        let elapsed = matches!(deadline, Some(d) if now_ns() >= d);
        if ready > 0 || elapsed || r < 0 {
            for i in 0..n {
                fds[i * 8 + 6..i * 8 + 8].copy_from_slice(&rev[i].to_le_bytes());
            }
            vm_write(pid, pfds_ptr, &fds);
            return if r < 0 { r } else { ready };
        }
        // spurious wake (eventfd signaled but no requested event satisfiable) → retry
    }
}

// ─── #34: epoll over owned loop fds (what node/libuv, Postgres, nginx use) ────
//
// A loop fd has no host fd, so it can't be added to a host epoll. Track loop-fd
// watches per host-epfd in a side table; add each loop socket's readiness eventfd
// to the host epoll (epoll_event.data tagged with the sid) so a host epoll_wait
// wakes on a loop-readable event; on wake, re-derive LEVEL readiness from LoopNet.
// CAVEAT: the tag is data bit 63 — real apps use userspace pointers (<2^47) or
// small-int fds as epoll data (bit 63 clear), so the tag is collision-free in
// practice (documented Stage-1 assumption).
fn epoll_loops() -> &'static Mutex<HashMap<i32, HashMap<netstack::SockId, (u32, u64, u32)>>> {
    static T: OnceLock<Mutex<HashMap<i32, HashMap<netstack::SockId, (u32, u64, u32)>>>> =
        OnceLock::new();
    T.get_or_init(|| Mutex::new(HashMap::new()))
}
const EPOLLIN_B: u32 = 0x001;
const EPOLLOUT_B: u32 = 0x004;
const EPOLLET_B: u32 = 0x8000_0000;

/// Apply EPOLLET edge semantics to a loop watch's current readiness.
/// `events` = the guest's watched mask (may carry EPOLLET); `ready` = the
/// currently-ready watched bits (IN/OUT) derived from LoopNet; `et` = persistent
/// per-(epfd,sid) delivered-edge state.
///
/// Level watches (no EPOLLET) report all currently-ready bits on every poll — the
/// kernel's level-triggered semantics. Edge watches (EPOLLET) report a direction
/// only on a not-ready→ready TRANSITION and suppress it while it stays continuously
/// ready; a direction that falls back to not-ready re-arms, so its next readiness is
/// reported as a fresh edge.
///
/// Without this, a LoopNet stream socket — which is essentially ALWAYS writable
/// (writable == Established && peer && !peer.rd_shut && peer.rx < CAP) — re-delivers
/// EPOLLOUT to an EPOLLET reactor (tokio/mio register the connection IN|OUT|ET) on
/// EVERY epoll_wait. The reactor's connection task is then perpetually woken, so the
/// worker's `io::driver::Driver::turn` returns immediately forever: a 100% busy-spin
/// livelock that starves the rest of the runtime. This is the residual Prisma P1001
/// under sentry — the connect succeeds, but the schema-engine's connection future is
/// never driven to completion because its tokio worker livelocks.
fn et_filter(events: u32, ready: u32, et: &mut u32) -> u32 {
    if events & EPOLLET_B == 0 {
        *et = 0; // level watch: no retained edge state
        return ready;
    }
    *et &= ready; // re-arm directions that fell back to not-ready
    let report = ready & !*et; // ready bits whose edge hasn't been delivered yet
    *et |= report; // mark them delivered
    report
}

/// DIAGNOSTIC (SENTRY_EPDBG=1): supervisor-side epoll tracing gate. Safe to use in
/// `service()` (the servicer runs in the supervisor, where openat is allowed) — unlike
/// SLOTDUMP, which fires ipc_logf in cells whose seccomp wall kills the openat.
fn epdbg_on() -> bool {
    static O: std::sync::OnceLock<bool> = std::sync::OnceLock::new();
    *O.get_or_init(|| std::env::var("SENTRY_EPDBG").is_ok())
}
const EPOLL_LOOP_TAG: u64 = 1 << 63;

/// Remove a loop socket from every epoll side table (called by `loop_close`; the
/// efd close itself removes it from the kernel epolls).
fn epoll_drop_sid(sid: netstack::SockId) {
    for (epfd, m) in epoll_loops().lock().unwrap().iter_mut() {
        if m.remove(&sid).is_some() && epdbg_on() {
            ipc_logf(
                &[(b"DROPSID epfd=", *epfd as i64), (b" sid=", sid as i64)],
                b"",
            );
        }
    }
}

fn restore_loop_state_from_snapshot(snap: &netstack::LoopSnapshot) {
    let mut ls = loop_state().lock().unwrap();
    for (_, efd) in ls.efds.drain() {
        unsafe { host(SYS_CLOSE, efd as u64, 0, 0, 0, 0, 0) };
    }
    ls.net = netstack::LoopNet::restore(snap);
    for sock in &snap.socks {
        ls.efds.insert(sock.id, loop_new_efd());
    }
}

fn restore_epoll_loop_fd(watches: &[state_snap::EpollLoopWatch]) -> std::io::Result<i32> {
    let epfd = unsafe { host(SYS_EPOLL_CREATE1, 0, 0, 0, 0, 0, 0) };
    if epfd < 0 {
        return Err(std::io::Error::from_raw_os_error((-epfd) as i32));
    }
    let epfd = epfd as i32;
    for watch in watches {
        let sid = watch.socket.0;
        let efd = match loop_state().lock().unwrap().efds.get(&sid).copied() {
            Some(efd) => efd,
            None => {
                unsafe { host(SYS_CLOSE, epfd as u64, 0, 0, 0, 0, 0) };
                return Err(std::io::Error::new(
                    std::io::ErrorKind::InvalidData,
                    format!("snapshot: epoll watch references missing loop socket {sid}"),
                ));
            }
        };
        let mut hev = [0u8; 12];
        hev[0..4].copy_from_slice(&EPOLLIN_B.to_le_bytes());
        hev[4..12].copy_from_slice(&(EPOLL_LOOP_TAG | sid as u64).to_le_bytes());
        let r = unsafe {
            host(
                SYS_EPOLL_CTL,
                epfd as u64,
                1,
                efd as u64,
                hev.as_ptr() as u64,
                0,
                0,
            )
        };
        if r < 0 && r != -17 {
            unsafe { host(SYS_CLOSE, epfd as u64, 0, 0, 0, 0, 0) };
            return Err(std::io::Error::from_raw_os_error((-r) as i32));
        }
        epoll_loops()
            .lock()
            .unwrap()
            .entry(epfd)
            .or_default()
            .insert(sid, (watch.events, watch.data, 0));
    }
    Ok(epfd)
}

/// epoll_ctl for an owned loop target fd. `host_epfd` = host epoll fd, `op`
/// (1=ADD,2=DEL,3=MOD), `sid` = the loop socket, `ev_ptr` = guest epoll_event*.
fn epoll_ctl_loop(pid: i32, host_epfd: i32, op: u64, sid: netstack::SockId, ev_ptr: u64) -> i64 {
    let (events, data) = if ev_ptr != 0 {
        let mut ev = [0u8; 12];
        vm_read(pid, ev_ptr, &mut ev);
        (
            u32::from_le_bytes(ev[0..4].try_into().unwrap()),
            u64::from_le_bytes(ev[4..12].try_into().unwrap()),
        )
    } else {
        (0, 0)
    };
    let efd = match loop_state().lock().unwrap().efds.get(&sid).copied() {
        Some(e) => e,
        None => return -9, // -EBADF
    };
    match op {
        1 | 3 => {
            // ADD/MOD: record the guest's watch; ensure the readiness efd is in the
            // host epoll (tagged) so epoll_wait wakes on a loop-readable transition.
            // ADD/MOD (re)registers the watch; reset the edge state to 0 so the next
            // readiness is reported as a fresh edge (matches the kernel: an ADD/MOD'd
            // fd's current readiness is re-armed as an initial edge).
            epoll_loops()
                .lock()
                .unwrap()
                .entry(host_epfd)
                .or_default()
                .insert(sid, (events, data, 0));
            if epdbg_on() {
                ipc_logf(
                    &[
                        (b"CTLADD epfd=", host_epfd as i64),
                        (b" sid=", sid as i64),
                        (b" op=", op as i64),
                    ],
                    b"",
                );
            }
            let mut hev = [0u8; 12];
            hev[0..4].copy_from_slice(&EPOLLIN_B.to_le_bytes());
            hev[4..12].copy_from_slice(&(EPOLL_LOOP_TAG | sid as u64).to_le_bytes());
            let r = unsafe {
                host(
                    SYS_EPOLL_CTL,
                    host_epfd as u64,
                    1,
                    efd as u64,
                    hev.as_ptr() as u64,
                    0,
                    0,
                )
            };
            if r == -17 {
                // -EEXIST: efd already watched → MOD it.
                unsafe {
                    host(
                        SYS_EPOLL_CTL,
                        host_epfd as u64,
                        3,
                        efd as u64,
                        hev.as_ptr() as u64,
                        0,
                        0,
                    )
                };
            }
            // Linux reports an ADD'd fd that is ALREADY ready for the watched events on
            // the next epoll_wait (the initial-ready edge). A reactor already BLOCKED in
            // epoll_wait (multi-threaded tokio: the reactor thread blocks while a WORKER
            // thread runs this epoll_ctl) wakes only when the readiness efd is signaled —
            // so signal it now if the loop socket is already readable/writable for the
            // watched events. Without this, a freshly-connected (immediately-writable)
            // client socket added to a blocked reactor never delivers EPOLLOUT, so
            // tokio's connect-completion future hangs forever (Prisma's P1001 on the
            // owned-loopback plane: connect succeeds, then the connect await times out).
            let ready_now = {
                let ls = loop_state().lock().unwrap();
                ((events & EPOLLIN_B) != 0 && ls.net.readable(sid))
                    || ((events & EPOLLOUT_B) != 0 && ls.net.writable(sid))
            };
            if ready_now {
                let one = 1u64.to_ne_bytes();
                unsafe { host(SYS_WRITE, efd as u64, one.as_ptr() as u64, 8, 0, 0, 0) };
            }
            0
        }
        2 => {
            if let Some(m) = epoll_loops().lock().unwrap().get_mut(&host_epfd) {
                if m.remove(&sid).is_some() && epdbg_on() {
                    ipc_logf(
                        &[(b"CTLDEL epfd=", host_epfd as i64), (b" sid=", sid as i64)],
                        b"",
                    );
                }
            }
            unsafe { host(SYS_EPOLL_CTL, host_epfd as u64, 2, efd as u64, 0, 0, 0) };
            0
        }
        _ => -22, // -EINVAL
    }
}

/// epoll_wait over a host epoll that has owned loop-fd watches: merge kernel host-fd
/// events with LoopNet readiness for the watched loop fds. Blocks on the host epoll
/// (which watches the host fds + the loop readiness eventfds) until ready/timeout.
fn epoll_wait_merge(pid: i32, host_epfd: i32, events_out: u64, max: usize, timeout_ms: i64) -> i64 {
    let deadline = if timeout_ms < 0 {
        None
    } else {
        Some(now_ns().saturating_add((timeout_ms as u64).saturating_mul(1_000_000)))
    };
    loop {
        // Pre-check loop watches (level readiness from LoopNet).
        let mut out: Vec<(u32, u64)> = Vec::new();
        let mut seen: std::collections::HashSet<u64> = std::collections::HashSet::new();
        {
            let mut lt = epoll_loops().lock().unwrap();
            let ls = loop_state().lock().unwrap();
            if let Some(m) = lt.get_mut(&host_epfd) {
                for (&sid, v) in m.iter_mut() {
                    let (events, data) = (v.0, v.1);
                    let mut ready = 0u32;
                    if (events & EPOLLIN_B) != 0 && ls.net.readable(sid) {
                        ready |= EPOLLIN_B;
                    }
                    if (events & EPOLLOUT_B) != 0 && ls.net.writable(sid) {
                        ready |= EPOLLOUT_B;
                    }
                    let re = et_filter(events, ready, &mut v.2);
                    if re != 0 && seen.insert(data) {
                        out.push((re, data));
                    }
                }
            }
        }
        let any_loop = !out.is_empty();
        let t: i64 = if any_loop {
            0
        } else {
            match deadline {
                None => -1,
                Some(d) => {
                    let now = now_ns();
                    if now >= d {
                        0
                    } else {
                        ((d - now) / 1_000_000).min(i32::MAX as u64) as i64
                    }
                }
            }
        };
        let mut hbuf = vec![0u8; max * 12];
        let hn = unsafe {
            host_cancellable(
                SYS_EPOLL_WAIT,
                host_epfd as u64,
                hbuf.as_mut_ptr() as u64,
                max as u64,
                t as u64,
                0,
                0,
            )
        };
        if hn == CANCEL_SENTINEL {
            return CANCEL_SENTINEL;
        }
        if hn > 0 {
            for i in 0..(hn as usize) {
                let ev = u32::from_le_bytes(hbuf[i * 12..i * 12 + 4].try_into().unwrap());
                let data = u64::from_le_bytes(hbuf[i * 12 + 4..i * 12 + 12].try_into().unwrap());
                if data & EPOLL_LOOP_TAG != 0 {
                    // A loop readiness efd woke → re-derive level readiness for its sid.
                    let sid = (data & !EPOLL_LOOP_TAG) as u32;
                    if let Some(&efd) = loop_state().lock().unwrap().efds.get(&sid) {
                        let mut sink = [0u8; 8];
                        unsafe { host(SYS_READ, efd as u64, sink.as_mut_ptr() as u64, 8, 0, 0, 0) };
                    }
                    let mut lt = epoll_loops().lock().unwrap();
                    if let Some(v) = lt.get_mut(&host_epfd).and_then(|m| m.get_mut(&sid)) {
                        let (wev, gdata) = (v.0, v.1);
                        let ls = loop_state().lock().unwrap();
                        let mut ready = 0u32;
                        if (wev & EPOLLIN_B) != 0 && ls.net.readable(sid) {
                            ready |= EPOLLIN_B;
                        }
                        if (wev & EPOLLOUT_B) != 0 && ls.net.writable(sid) {
                            ready |= EPOLLOUT_B;
                        }
                        // The efd waking is a genuine readiness EDGE — loop_wake signals it
                        // per send (loop_send_bytes), per connect-ready, and per close. Report
                        // the current readiness DIRECTLY rather than through et_filter: the
                        // guest draining the socket with recv() never clears et_filter's mark,
                        // so a socket that stays readable across the drain→re-arm window wedges
                        // a stuck mark and every later edge is suppressed (the Prisma seeding
                        // stall — reactor parks while the socket is readable). Re-arm the
                        // edge-state to the reported set so the LEVEL pre-check won't re-report
                        // it without a fresh edge.
                        v.2 = ready;
                        if ready != 0 && seen.insert(gdata) {
                            out.push((ready, gdata));
                        }
                    } else {
                        // dup-aliased epoll: this fd's table lacks (host_epfd, sid) because
                        // the watch was ADDed via a fork/dup SIBLING fd that shares this
                        // kernel epoll object. LAZILY ADOPT the sibling watch into THIS
                        // epfd's table with its OWN edge-state — reusing the sibling's
                        // shared et-state across aliases races (the edge never gets
                        // suppressed → perpetual re-delivery → reactor busy-spin under clean
                        // timing). A per-(epfd,sid) entry gives independent edge tracking
                        // and lets future polls + the pre-check find it directly. Prisma's
                        // dup-aliased connection epoll.
                        let sib_watch = lt.iter().find_map(|(ep2, m)| {
                            if *ep2 != host_epfd {
                                m.get(&sid).map(|w| (w.0, w.1))
                            } else {
                                None
                            }
                        });
                        if let Some((wev, gdata)) = sib_watch {
                            let v = lt
                                .entry(host_epfd)
                                .or_default()
                                .entry(sid)
                                .or_insert((wev, gdata, 0));
                            let ls = loop_state().lock().unwrap();
                            let mut ready = 0u32;
                            if (wev & EPOLLIN_B) != 0 && ls.net.readable(sid) {
                                ready |= EPOLLIN_B;
                            }
                            if (wev & EPOLLOUT_B) != 0 && ls.net.writable(sid) {
                                ready |= EPOLLOUT_B;
                            }
                            // Fresh edge (see the non-aliased branch above): report directly +
                            // re-arm et — never et_filter-suppress an efd wake.
                            v.2 = ready;
                            if ready != 0 && seen.insert(gdata) {
                                out.push((ready, gdata));
                            }
                        }
                    }
                } else {
                    out.push((ev, data)); // a real host fd event
                }
            }
        }
        let elapsed = matches!(deadline, Some(d) if now_ns() >= d);
        if !out.is_empty() || elapsed || hn < 0 {
            let cnt = out.len().min(max);
            if epdbg_on() {
                ipc_logf(
                    &[
                        (b"EPMERGE epfd=", host_epfd as i64),
                        (b" tmo=", timeout_ms),
                        (b" anyloop=", any_loop as i64),
                        (b" hn=", hn),
                        (b" out=", out.len() as i64),
                        (b" ev0=", out.first().map(|x| x.0 as i64).unwrap_or(-1)),
                    ],
                    b"",
                );
            }
            let mut wbuf = vec![0u8; cnt * 12];
            for (i, (ev, data)) in out.iter().take(cnt).enumerate() {
                wbuf[i * 12..i * 12 + 4].copy_from_slice(&ev.to_le_bytes());
                wbuf[i * 12 + 4..i * 12 + 12].copy_from_slice(&data.to_le_bytes());
            }
            if cnt > 0 {
                vm_write(pid, events_out, &wbuf);
            }
            return if hn < 0 { hn } else { cnt as i64 };
        }
        // spurious wake → retry
    }
}

// ─── C4 capture: gather the live owned state into the serializable snapshot ──
//
// The fd-table gather (host-pid-keyed FdVal table → vpid-keyed FdTarget snapshot).
// host fd → what it points at (RootRelFile / DevNode / best-effort), loop fd →
// LoopSocket. The other capture pieces are proctree::capture (C2a),
// LoopNet::capture (C1a/e8e244a), the C3 memory dump, and cwds; the orchestrator
// that assembles them + writes state.snap is the remaining C4 core.

fn ends_with(s: &[u8], suf: &[u8]) -> bool {
    s.len() >= suf.len() && &s[s.len() - suf.len()..] == suf
}

#[derive(Clone, Debug)]
struct PipeFdInfo {
    id: u64,
    end: state_snap::PipeEnd,
    flags: u32,
}

#[derive(Clone, Debug, Default)]
struct PipeCaptureGroup {
    has_read: bool,
    has_write: bool,
    buffered: Vec<u8>,
}

fn fd_status_flags(h: i32) -> u32 {
    let flags = unsafe { host(SYS_FCNTL, h as u64, libc::F_GETFL as u64, 0, 0, 0, 0) };
    if flags >= 0 {
        flags as u32
    } else {
        0
    }
}

fn host_fd_stat_kind(h: i32) -> Option<(u32, libc::ino_t)> {
    let mut st: libc::stat = unsafe { std::mem::zeroed() };
    let r = unsafe {
        host(
            SYS_FSTAT,
            h as u64,
            &mut st as *mut libc::stat as u64,
            0,
            0,
            0,
            0,
        )
    };
    (r == 0).then(|| (((st.st_mode as u32) & S_IFMT), st.st_ino))
}

fn pipe_fd_info(h: i32) -> Option<PipeFdInfo> {
    let (kind, ino) = host_fd_stat_kind(h)?;
    if kind != S_IFIFO {
        return None;
    }
    let flags = fd_status_flags(h);
    let end = match (flags as u64) & O_ACCMODE {
        O_RDONLY => state_snap::PipeEnd::Read,
        O_WRONLY => state_snap::PipeEnd::Write,
        _ => return None,
    };
    Some(PipeFdInfo {
        id: ino as u64,
        end,
        flags,
    })
}

fn pipe_buffered_bytes(read_h: i32) -> std::io::Result<Vec<u8>> {
    const FIONREAD: u64 = 0x541B;
    const SPLICE_F_NONBLOCK: u64 = 0x02;

    let mut avail: i32 = 0;
    let r = unsafe {
        host(
            SYS_IOCTL,
            read_h as u64,
            FIONREAD,
            &mut avail as *mut i32 as u64,
            0,
            0,
            0,
        )
    };
    if r < 0 {
        return Err(std::io::Error::from_raw_os_error((-r) as i32));
    }
    if avail <= 0 {
        return Ok(Vec::new());
    }

    let mut tmp = [0i32; 2];
    let r = unsafe {
        host(
            SYS_PIPE2,
            tmp.as_mut_ptr() as u64,
            libc::O_CLOEXEC as u64,
            0,
            0,
            0,
            0,
        )
    };
    if r < 0 {
        return Err(std::io::Error::from_raw_os_error((-r) as i32));
    }
    let (tmp_r, tmp_w) = (tmp[0], tmp[1]);
    let mut out = Vec::with_capacity(avail as usize);
    let mut remaining = avail as usize;
    let result = (|| {
        while remaining > 0 {
            let n = unsafe {
                host(
                    SYS_TEE,
                    read_h as u64,
                    tmp_w as u64,
                    remaining as u64,
                    SPLICE_F_NONBLOCK,
                    0,
                    0,
                )
            };
            if n < 0 {
                return Err(std::io::Error::from_raw_os_error((-n) as i32));
            }
            if n == 0 {
                return Err(std::io::Error::new(
                    std::io::ErrorKind::UnexpectedEof,
                    "snapshot: pipe buffer changed while copying",
                ));
            }
            let mut copied = 0usize;
            let want = n as usize;
            while copied < want {
                let mut buf = [0u8; 8192];
                let take = buf.len().min(want - copied);
                let got = unsafe {
                    host(
                        SYS_READ,
                        tmp_r as u64,
                        buf.as_mut_ptr() as u64,
                        take as u64,
                        0,
                        0,
                        0,
                    )
                };
                if got <= 0 {
                    return Err(if got < 0 {
                        std::io::Error::from_raw_os_error((-got) as i32)
                    } else {
                        std::io::Error::new(
                            std::io::ErrorKind::UnexpectedEof,
                            "snapshot: pipe tee drained short",
                        )
                    });
                }
                out.extend_from_slice(&buf[..got as usize]);
                copied += got as usize;
            }
            remaining -= want;
        }
        Ok(out)
    })();
    unsafe {
        host(SYS_CLOSE, tmp_r as u64, 0, 0, 0, 0, 0);
        host(SYS_CLOSE, tmp_w as u64, 0, 0, 0, 0, 0);
    }
    result
}

/// Derive the serializable [`state_snap::FdTarget`] for a live HOST fd (loop fds
/// are handled by the caller). Regular file → `RootRelFile` (guest path + current
/// seek offset + open flags); char device → `DevNode`; anonymous pipe read ends
/// → `Pipe` with their currently buffered bytes. A read end remains meaningful
/// after its writer has closed: restore recreates those bytes plus EOF. Pipe
/// write ends require their read peer to be in the captured graph. External
/// sockets/unknown fds are omitted so restore keeps its freshly seeded stdio fds
/// instead of installing a corrupt placeholder.
#[allow(dead_code)] // wired by the C4 capture orchestrator (next)
fn host_fd_to_target(
    h: i32,
    pipe_groups: &HashMap<u64, PipeCaptureGroup>,
) -> std::io::Result<Option<state_snap::FdTarget>> {
    if let Some(loop_watches) = epoll_loops().lock().unwrap().get(&h).map(|m| {
        let mut watches = m
            .iter()
            .map(|(&sid, &(events, data, _))| state_snap::EpollLoopWatch {
                socket: state_snap::SocketHandle(sid),
                events,
                data,
            })
            .collect::<Vec<_>>();
        watches.sort_by_key(|w| w.socket.0);
        watches
    }) {
        return Ok(Some(state_snap::FdTarget::Epoll { loop_watches }));
    }

    let Some((kind, _ino)) = host_fd_stat_kind(h) else {
        return Ok(None);
    };
    match kind {
        S_IFREG => {
            let offset = unsafe {
                host(SYS_LSEEK, h as u64, 0, 1 /*SEEK_CUR*/, 0, 0, 0)
            };
            let Some(path) = readlink_fd(h).and_then(|hp| host_to_guest_path(&hp)) else {
                return Ok(None);
            };
            Ok(Some(state_snap::FdTarget::RootRelFile {
                path,
                offset: if offset >= 0 { offset as u64 } else { 0 },
                flags: fd_status_flags(h),
            }))
        }
        S_IFCHR => {
            let p = readlink_fd(h).unwrap_or_default();
            let kind = if ends_with(&p, b"null") {
                state_snap::DevKind::Null
            } else if ends_with(&p, b"zero") {
                state_snap::DevKind::Zero
            } else if ends_with(&p, b"full") {
                state_snap::DevKind::Full
            } else if ends_with(&p, b"random") || ends_with(&p, b"urandom") {
                state_snap::DevKind::Random
            } else {
                state_snap::DevKind::Tty
            };
            if kind == state_snap::DevKind::Tty {
                return Ok(None);
            }
            Ok(Some(state_snap::FdTarget::DevNode {
                kind,
                flags: fd_status_flags(h),
            }))
        }
        S_IFIFO => {
            let Some(info) = pipe_fd_info(h) else {
                return Ok(None);
            };
            let Some(group) = pipe_groups.get(&info.id) else {
                return Ok(None);
            };
            match info.end {
                state_snap::PipeEnd::Read => {
                    if !group.has_read {
                        return Ok(None);
                    }
                }
                state_snap::PipeEnd::Write => {
                    if !group.has_read || !group.has_write {
                        return Ok(None);
                    }
                }
            }
            Ok(Some(state_snap::FdTarget::Pipe {
                id: info.id,
                end: info.end,
                buffered: group.buffered.clone(),
                flags: info.flags,
            }))
        }
        _ => Ok(None),
    }
}

/// Gather the live per-pid fd tables into a serializable, vpid-keyed
/// [`state_snap::FdTableSnapshot`] (C4 capture): host pid → vpid (proctree),
/// host fd → `FdTarget`, loop fd → `LoopSocket`. Untracked pids (no vpid) skipped.
#[allow(dead_code)] // wired by the C4 capture orchestrator (next)
fn capture_fdtable() -> std::io::Result<state_snap::FdTableSnapshot> {
    let entries: Vec<(state_snap::Vpid, i32, FdVal)> = {
        let table = fdt().lock().unwrap();
        let mut entries = Vec::new();
        for (&host_pid, fds) in table.iter() {
            let Some(vpid) = proctree::vpid_for(host_pid) else {
                continue;
            };
            for (&gfd, &val) in fds.iter() {
                entries.push((vpid, gfd, val));
            }
        }
        entries
    };

    let mut pipe_groups: HashMap<u64, PipeCaptureGroup> = HashMap::new();
    for (_, _, val) in &entries {
        let FdVal::Host(h) = *val else {
            continue;
        };
        let Some(info) = pipe_fd_info(h) else {
            continue;
        };
        let group = pipe_groups.entry(info.id).or_default();
        match info.end {
            state_snap::PipeEnd::Read => {
                group.has_read = true;
                if group.buffered.is_empty() {
                    group.buffered = pipe_buffered_bytes(h)?;
                }
            }
            state_snap::PipeEnd::Write => {
                group.has_write = true;
            }
        }
    }

    let mut by_vpid: BTreeMap<state_snap::Vpid, Vec<(i32, state_snap::FdTarget)>> = BTreeMap::new();
    for (vpid, gfd, val) in entries {
        let target = match val {
            FdVal::Loop(sid) => Some(state_snap::FdTarget::LoopSocket(state_snap::SocketHandle(
                sid,
            ))),
            FdVal::Host(h) => host_fd_to_target(h, &pipe_groups)?,
        };
        if let Some(target) = target {
            by_vpid.entry(vpid).or_default().push((gfd, target));
        }
    }
    let mut per_vpid = Vec::with_capacity(by_vpid.len());
    for (vpid, mut fds) in by_vpid {
        fds.sort_by_key(|(gfd, _)| *gfd);
        per_vpid.push((vpid, fds));
    }
    Ok(state_snap::FdTableSnapshot { per_vpid })
}

fn capture_cwds() -> Vec<(state_snap::Vpid, Vec<u8>)> {
    let table = cwds().lock().unwrap();
    let mut out = Vec::new();
    for (&host_pid, cwd) in table.iter() {
        if let Some(vpid) = proctree::vpid_for(host_pid) {
            out.push((vpid, cwd.clone()));
        }
    }
    out.sort_by_key(|(vpid, _)| *vpid);
    out
}

fn capture_cmdlines() -> Vec<(state_snap::Vpid, Vec<u8>)> {
    let table = proc_cmdline().lock().unwrap();
    let mut out = Vec::new();
    for (&host_pid, cmdline) in table.iter() {
        if let Some(vpid) = proctree::vpid_for(host_pid) {
            out.push((vpid, cmdline.clone()));
        }
    }
    out.sort_by_key(|(vpid, _)| *vpid);
    out
}

fn capture_exes() -> Vec<(state_snap::Vpid, Vec<u8>)> {
    let table = proc_exe().lock().unwrap();
    let mut out = Vec::new();
    for (&host_pid, exe) in table.iter() {
        if let Some(vpid) = proctree::vpid_for(host_pid) {
            out.push((vpid, exe.clone()));
        }
    }
    out.sort_by_key(|(vpid, _)| *vpid);
    out
}

fn rekey_fdtable(
    fdtable: state_snap::FdTableSnapshot,
    vpid_map: &BTreeMap<state_snap::Vpid, state_snap::Vpid>,
) -> state_snap::FdTableSnapshot {
    let mut per_vpid = Vec::new();
    for (old_vpid, fds) in fdtable.per_vpid {
        if let Some(new_vpid) = vpid_map.get(&old_vpid) {
            per_vpid.push((*new_vpid, fds));
        }
    }
    per_vpid.sort_by_key(|(vpid, _)| *vpid);
    state_snap::FdTableSnapshot { per_vpid }
}

fn rekey_cwds(
    cwds_in: Vec<(state_snap::Vpid, Vec<u8>)>,
    vpid_map: &BTreeMap<state_snap::Vpid, state_snap::Vpid>,
) -> Vec<(state_snap::Vpid, Vec<u8>)> {
    rekey_vpid_bytes(cwds_in, vpid_map)
}

fn rekey_vpid_bytes(
    entries: Vec<(state_snap::Vpid, Vec<u8>)>,
    vpid_map: &BTreeMap<state_snap::Vpid, state_snap::Vpid>,
) -> Vec<(state_snap::Vpid, Vec<u8>)> {
    let mut out = Vec::new();
    for (old_vpid, bytes) in entries {
        if let Some(new_vpid) = vpid_map.get(&old_vpid) {
            out.push((*new_vpid, bytes));
        }
    }
    out.sort_by_key(|(vpid, _)| *vpid);
    out
}

#[cfg(all(target_os = "linux", target_arch = "x86_64"))]
fn snapshot_tmp_suffix() -> u64 {
    static SEQ: AtomicU64 = AtomicU64::new(0);
    let nanos = std::time::SystemTime::now()
        .duration_since(std::time::UNIX_EPOCH)
        .map(|d| d.as_nanos() as u64)
        .unwrap_or(0);
    nanos ^ SEQ.fetch_add(1, Ordering::Relaxed)
}

#[cfg(all(target_os = "linux", target_arch = "x86_64"))]
fn read_exact_process_mem(mem: &std::fs::File, addr: u64, out: &mut [u8]) -> bool {
    use std::os::unix::fs::FileExt;

    let mut done = 0usize;
    while done < out.len() {
        match mem.read_at(&mut out[done..], addr + done as u64) {
            Ok(0) => return false,
            Ok(n) => done += n,
            Err(e) if e.kind() == std::io::ErrorKind::Interrupted => {}
            Err(_) => return false,
        }
    }
    true
}

#[cfg(all(target_os = "linux", target_arch = "x86_64"))]
fn read_process_u32(mem: &std::fs::File, addr: u64) -> Option<u32> {
    let mut buf = [0u8; 4];
    read_exact_process_mem(mem, addr, &mut buf).then(|| u32::from_le_bytes(buf))
}

#[cfg(all(target_os = "linux", target_arch = "x86_64"))]
fn read_process_u64(mem: &std::fs::File, addr: u64) -> Option<u64> {
    let mut buf = [0u8; 8];
    read_exact_process_mem(mem, addr, &mut buf).then(|| u64::from_le_bytes(buf))
}

#[cfg(all(target_os = "linux", target_arch = "x86_64"))]
fn capture_guest_vma_ranges(mem: &std::fs::File) -> Vec<(u64, u64)> {
    let n_addr = std::ptr::addr_of!(GUEST_VMA_NEXT) as u64;
    let n = read_process_u32(mem, n_addr)
        .unwrap_or(0)
        .min(GUEST_VMA_MAX as u32) as usize;
    let base = std::ptr::addr_of!(GUEST_VMAS) as u64;
    let stride = std::mem::size_of::<GuestVmaSlot>() as u64;
    let mut ranges = Vec::new();
    for i in 0..n {
        let slot = base + i as u64 * stride;
        let lo = read_process_u64(mem, slot).unwrap_or(0);
        let hi = read_process_u64(mem, slot + 8).unwrap_or(0);
        if lo >= WINDOW_FLOOR && hi > lo && hi <= USER_TOP {
            ranges.push((lo, hi));
        }
    }
    ranges.sort_unstable();
    ranges.dedup();
    ranges
}

#[cfg(all(target_os = "linux", target_arch = "x86_64"))]
fn select_guest_map_entries(maps: &[u8], guest_ranges: &[(u64, u64)]) -> Vec<memimage::MapEntry> {
    let mut out = Vec::new();
    for e in memimage::parse_maps(maps, USER_TOP) {
        let end = e.va.saturating_add(e.len);
        if e.va < WINDOW_FLOOR {
            let low_end = end.min(WINDOW_FLOOR);
            if low_end > e.va {
                out.push(memimage::MapEntry {
                    va: e.va,
                    len: low_end - e.va,
                    prot: e.prot,
                });
            }
            continue;
        }
        for &(lo, hi) in guest_ranges {
            let s = e.va.max(lo);
            let t = end.min(hi);
            if t > s {
                out.push(memimage::MapEntry {
                    va: s,
                    len: t - s,
                    prot: e.prot,
                });
            }
        }
    }
    out.sort_by_key(|e| e.va);
    out.dedup_by(|a, b| a.va == b.va && a.len == b.len && a.prot == b.prot);
    out
}

#[cfg(all(target_os = "linux", target_arch = "x86_64"))]
fn capture_warm_thread_regs(
    mem: &std::fs::File,
    vtid: state_snap::Vpid,
) -> std::io::Result<state_snap::ThreadRegs> {
    let mut gregs = [0i64; 18];
    let mut gbuf = [0u8; 18 * std::mem::size_of::<i64>()];
    let gaddr = std::ptr::addr_of!(CAPTURED) as u64;
    if !read_exact_process_mem(mem, gaddr, &mut gbuf) {
        return Err(std::io::Error::new(
            std::io::ErrorKind::Other,
            "snapshot: read warm-cell captured gregs failed",
        ));
    }
    for (i, chunk) in gbuf.chunks_exact(8).enumerate() {
        gregs[i] = i64::from_le_bytes(chunk.try_into().unwrap());
    }
    // A cooperative SENTINEL checkpoint resumes as if the synthetic syscall
    // returned 0. Live ptrace captures preserve RAX exactly; this normalization is
    // only for the sentinel-backed warm zygote path.
    gregs[REG_RAX] = 0;

    let mut fsbuf = [0u8; std::mem::size_of::<u64>()];
    let fsaddr = std::ptr::addr_of!(GUEST_FS) as u64;
    if !read_exact_process_mem(mem, fsaddr, &mut fsbuf) {
        return Err(std::io::Error::new(
            std::io::ErrorKind::Other,
            "snapshot: read warm-cell guest fs failed",
        ));
    }

    Ok(state_snap::ThreadRegs {
        vtid,
        gregs,
        fs: u64::from_le_bytes(fsbuf),
        xsave: Vec::new(),
    })
}

#[cfg(all(target_os = "linux", target_arch = "x86_64"))]
fn read_mem_meta(dest_dir: &std::path::Path) -> std::io::Result<state_snap::MemImage> {
    let mut f = std::fs::File::open(dest_dir.join("mem.meta"))?;
    state_snap::MemImage::read_from(&mut f)
}

#[cfg(all(target_os = "linux", target_arch = "x86_64"))]
fn launcher_capture_live_mem(
    launch_fd: c_int,
    dest_dir: &std::path::Path,
    targets: Vec<LauncherCaptureTarget>,
) -> std::io::Result<state_snap::MemImage> {
    let req = LaunchReq {
        path: Some("/.supermachine/capture".to_string()),
        argv: vec!["/.supermachine/capture".to_string()],
        warm: false,
        env: Vec::new(),
        cwd: None,
        restore_dir: None,
        restore_vpid: 1,
        restore_warm: true,
        detached: false,
        slot: None,
        stop_before_resume: false,
        stop_before_start: false,
        capture: Some(LauncherCaptureReq {
            dest_dir: dest_dir.to_string_lossy().into_owned(),
            targets,
        }),
    };
    let json = serde_json::to_vec(&req).map_err(|e| {
        std::io::Error::new(
            std::io::ErrorKind::InvalidData,
            format!("snapshot: launcher capture request serialize: {e}"),
        )
    })?;
    unsafe { libc::send(launch_fd, json.as_ptr() as *const c_void, json.len(), 0) };
    let mut code: i32 = -1;
    let n = unsafe { libc::recv(launch_fd, &mut code as *mut i32 as *mut c_void, 4, 0) };
    if n != 4 {
        return Err(std::io::Error::new(
            std::io::ErrorKind::UnexpectedEof,
            "snapshot: launcher capture response missing",
        ));
    }
    if code != 0 {
        return Err(std::io::Error::from_raw_os_error((-code).max(1)));
    }
    let mem = read_mem_meta(dest_dir)?;
    let _ = std::fs::remove_file(dest_dir.join("mem.meta"));
    Ok(mem)
}

#[cfg(all(target_os = "linux", target_arch = "x86_64"))]
fn capture_state_snapshot(
    launch_fd: c_int,
    warm_pid: i32,
    dest_dir: &std::path::Path,
) -> std::io::Result<()> {
    use std::io::Write as _;

    std::fs::create_dir_all(dest_dir)?;

    let live_instances = live_running_instances_snapshot();
    if !live_instances.is_empty() {
        let mut subtree_by_vpid: BTreeMap<state_snap::Vpid, (state_snap::Vpid, i32)> =
            BTreeMap::new();
        for root_host in &live_instances {
            for (old_vpid, old_parent, host_pid) in proctree::live_subtree(*root_host) {
                subtree_by_vpid
                    .entry(old_vpid)
                    .or_insert((old_parent, host_pid));
            }
        }
        if subtree_by_vpid.is_empty() {
            return Err(std::io::Error::new(
                std::io::ErrorKind::NotFound,
                "snapshot: preserved live instance is not in the owned process tree",
            ));
        }
        let mut vpid_map = BTreeMap::new();
        let mut next_vpid: state_snap::Vpid = 1;
        for old_vpid in subtree_by_vpid.keys() {
            vpid_map.insert(*old_vpid, next_vpid);
            next_vpid = next_vpid.saturating_add(1);
        }
        let targets: Vec<LauncherCaptureTarget> = subtree_by_vpid
            .iter()
            .filter_map(|(old_vpid, (_, host_pid))| {
                vpid_map
                    .get(old_vpid)
                    .map(|new_vpid| LauncherCaptureTarget {
                        vpid: *new_vpid,
                        host_pid: *host_pid,
                    })
            })
            .collect();
        let mem = launcher_capture_live_mem(launch_fd, dest_dir, targets)?;
        let mut procs = Vec::with_capacity(subtree_by_vpid.len());
        for (old_vpid, (old_parent, host_pid)) in &subtree_by_vpid {
            let new_vpid = vpid_map[old_vpid];
            let new_parent = vpid_map.get(old_parent).copied().unwrap_or(0);
            let pgid = unsafe { libc::getpgid(*host_pid) };
            let sid = unsafe { libc::getsid(*host_pid) };
            let threads = mem
                .proc_image(new_vpid)
                .map(|image| image.threads.to_vec())
                .unwrap_or_default();
            procs.push(state_snap::GuestProcRecord {
                vpid: new_vpid,
                parent_vpid: new_parent,
                pgid: if pgid > 0 { pgid as u32 } else { 0 },
                sid: if sid > 0 { sid as u32 } else { 0 },
                threads,
                state: state_snap::ProcState::Running,
            });
        }
        procs.sort_by_key(|p| p.vpid);
        let state = state_snap::StateSnapshot {
            version: state_snap::CURRENT_VERSION,
            cpu_feature_baseline: state_snap::CpuFeatures::current(),
            entry_kind: state_snap::SnapshotEntryKind::LiveTree,
            mem,
            proctree: state_snap::ProcTreeSnapshot { procs },
            netstack: loop_state().lock().unwrap().net.capture(),
            fdtable: rekey_fdtable(capture_fdtable()?, &vpid_map),
            cwds: rekey_cwds(capture_cwds(), &vpid_map),
            cmdlines: rekey_vpid_bytes(capture_cmdlines(), &vpid_map),
            exes: rekey_vpid_bytes(capture_exes(), &vpid_map),
        };
        let tmp_suffix = format!("{}-{}", std::process::id(), snapshot_tmp_suffix());
        let snap_tmp = dest_dir.join(format!(".restore.snap.tmp-{tmp_suffix}"));
        let mut snap_buf = Vec::new();
        state.write_to(&mut snap_buf)?;
        std::fs::write(&snap_tmp, snap_buf)?;
        std::fs::rename(&snap_tmp, dest_dir.join("restore.snap"))?;
        return Ok(());
    }

    if warm_pid <= 1 {
        return Err(std::io::Error::new(
            std::io::ErrorKind::InvalidInput,
            "snapshot: no warm cell or preserved live tree is parked",
        ));
    }

    let maps = std::fs::read(format!("/proc/{warm_pid}/maps"))?;
    let mem = std::fs::File::open(format!("/proc/{warm_pid}/mem"))?;
    let guest_ranges = capture_guest_vma_ranges(&mem);
    let entries = select_guest_map_entries(&maps, &guest_ranges);
    let tmp_suffix = format!("{}-{}", std::process::id(), snapshot_tmp_suffix());
    let mem_tmp = dest_dir.join(format!(".mem.blob.tmp-{tmp_suffix}"));
    let snap_tmp = dest_dir.join(format!(".restore.snap.tmp-{tmp_suffix}"));
    let mut ram = std::fs::File::create(&mem_tmp)?;
    let regions = memimage::capture_regions(
        &entries,
        |va, dst| read_exact_process_mem(&mem, va, dst),
        &mut ram,
    )?;
    ram.flush()?;

    let warm_vpid = proctree::vpid_for(warm_pid).ok_or_else(|| {
        std::io::Error::new(
            std::io::ErrorKind::Other,
            format!("snapshot: warm pid {warm_pid} has no vpid"),
        )
    })?;
    let thread = capture_warm_thread_regs(&mem, warm_vpid)?;
    let mut procs = proctree::capture(|host_pid| unsafe {
        let pgid = libc::getpgid(host_pid);
        let sid = libc::getsid(host_pid);
        (
            if pgid > 0 { pgid as u32 } else { 0 },
            if sid > 0 { sid as u32 } else { 0 },
        )
    });
    if let Some(proc_rec) = procs.procs.iter_mut().find(|p| p.vpid == warm_vpid) {
        proc_rec.threads.push(thread.clone());
    }

    let state = state_snap::StateSnapshot {
        version: state_snap::CURRENT_VERSION,
        cpu_feature_baseline: state_snap::CpuFeatures::current(),
        entry_kind: state_snap::SnapshotEntryKind::WarmZygote,
        mem: state_snap::MemImage {
            regions: regions.clone(),
            threads: vec![thread.clone()],
            procs: vec![state_snap::ProcMemImage {
                vpid: warm_vpid,
                blob_off: 0,
                regions,
                threads: vec![thread],
            }],
        },
        proctree: procs,
        netstack: loop_state().lock().unwrap().net.capture(),
        fdtable: capture_fdtable()?,
        cwds: capture_cwds(),
        cmdlines: capture_cmdlines(),
        exes: capture_exes(),
    };
    let mut snap_buf = Vec::new();
    state.write_to(&mut snap_buf)?;
    std::fs::write(&snap_tmp, snap_buf)?;
    std::fs::rename(&mem_tmp, dest_dir.join("mem.blob"))?;
    std::fs::rename(&snap_tmp, dest_dir.join("restore.snap"))?;
    Ok(())
}

#[cfg(all(target_os = "linux", target_arch = "x86_64"))]
fn read_state_snapshot_dir(dir: &std::path::Path) -> std::io::Result<state_snap::StateSnapshot> {
    let mut f = std::fs::File::open(dir.join("restore.snap"))?;
    state_snap::StateSnapshot::read_from(&mut f)
}

fn io_err_from_neg(n: i64) -> std::io::Error {
    std::io::Error::from_raw_os_error((-n) as i32)
}

fn snapshot_restore_open_flags(flags: u32) -> u64 {
    (flags as u64) & !(O_CREAT | O_TRUNC | O_EXCL)
}

fn nul_path(path: &[u8]) -> std::io::Result<Vec<u8>> {
    if path.is_empty() || path[0] != b'/' || path.iter().any(|&b| b == 0) {
        return Err(std::io::Error::new(
            std::io::ErrorKind::InvalidData,
            "snapshot: fd path is not a clean absolute guest path",
        ));
    }
    let mut out = path.to_vec();
    out.push(0);
    Ok(out)
}

fn restore_root_rel_file_fd(
    warm_pid: i32,
    path: &[u8],
    offset: u64,
    flags: u32,
) -> std::io::Result<i32> {
    let p = nul_path(path)?;
    let fd = open_path(warm_pid, &p, snapshot_restore_open_flags(flags), 0);
    if fd < 0 {
        return Err(io_err_from_neg(fd));
    }
    if offset > 0 {
        let r = unsafe {
            host(SYS_LSEEK, fd as u64, offset, 0 /*SEEK_SET*/, 0, 0, 0)
        };
        if r < 0 {
            unsafe { host(SYS_CLOSE, fd as u64, 0, 0, 0, 0, 0) };
            return Err(io_err_from_neg(r));
        }
    }
    Ok(fd as i32)
}

fn dev_kind_path(kind: state_snap::DevKind) -> Option<&'static [u8]> {
    match kind {
        state_snap::DevKind::Null => Some(b"/dev/null"),
        state_snap::DevKind::Zero => Some(b"/dev/zero"),
        state_snap::DevKind::Full => Some(b"/dev/full"),
        state_snap::DevKind::Random => Some(b"/dev/urandom"),
        state_snap::DevKind::Tty => None,
    }
}

fn restore_dev_node_fd(
    warm_pid: i32,
    kind: state_snap::DevKind,
    flags: u32,
) -> std::io::Result<i32> {
    let Some(path) = dev_kind_path(kind) else {
        return Err(std::io::Error::new(
            std::io::ErrorKind::InvalidData,
            "snapshot: /dev/tty cannot be restored without a terminal peer",
        ));
    };
    let p = nul_path(path)?;
    let fd = open_path(warm_pid, &p, snapshot_restore_open_flags(flags), 0);
    if fd < 0 {
        return Err(io_err_from_neg(fd));
    }
    Ok(fd as i32)
}

#[derive(Clone, Debug, Default)]
struct PipeRestoreGroup {
    buffered: Vec<u8>,
    entries: Vec<(i32, state_snap::PipeEnd, u32)>,
}

fn set_fd_status(fd: i32, flags: u32) -> std::io::Result<()> {
    let r = unsafe {
        host(
            SYS_FCNTL,
            fd as u64,
            libc::F_SETFL as u64,
            flags as u64,
            0,
            0,
            0,
        )
    };
    if r < 0 {
        Err(io_err_from_neg(r))
    } else {
        Ok(())
    }
}

fn write_all_to_fd(fd: i32, data: &[u8]) -> std::io::Result<()> {
    let mut off = 0usize;
    while off < data.len() {
        let n = unsafe {
            host(
                SYS_WRITE,
                fd as u64,
                data[off..].as_ptr() as u64,
                (data.len() - off) as u64,
                0,
                0,
                0,
            )
        };
        if n < 0 {
            return Err(io_err_from_neg(n));
        }
        if n == 0 {
            return Err(std::io::Error::new(
                std::io::ErrorKind::WriteZero,
                "snapshot: pipe restore wrote zero bytes",
            ));
        }
        off += n as usize;
    }
    Ok(())
}

fn restore_pipe_group(group: &PipeRestoreGroup) -> std::io::Result<Vec<(i32, FdVal)>> {
    const F_SETPIPE_SZ: u64 = 1031;
    let mut fds = [0i32; 2];
    let r = unsafe {
        host(
            SYS_PIPE2,
            fds.as_mut_ptr() as u64,
            libc::O_CLOEXEC as u64,
            0,
            0,
            0,
            0,
        )
    };
    if r < 0 {
        return Err(io_err_from_neg(r));
    }
    let (read_fd, write_fd) = (fds[0], fds[1]);
    let result = (|| {
        if !group.buffered.is_empty() {
            unsafe {
                host(
                    SYS_FCNTL,
                    write_fd as u64,
                    F_SETPIPE_SZ,
                    group.buffered.len() as u64,
                    0,
                    0,
                    0,
                );
            }
            let saved = fd_status_flags(write_fd);
            set_fd_status(write_fd, saved | libc::O_NONBLOCK as u32)?;
            let write_res = write_all_to_fd(write_fd, &group.buffered);
            let _ = set_fd_status(write_fd, saved);
            write_res?;
        }

        let mut restored = Vec::with_capacity(group.entries.len());
        for &(gfd, end, flags) in &group.entries {
            let base = match end {
                state_snap::PipeEnd::Read => read_fd,
                state_snap::PipeEnd::Write => write_fd,
            };
            let dup = unsafe { host(SYS_DUP, base as u64, 0, 0, 0, 0, 0) };
            if dup < 0 {
                for (_, val) in restored.drain(..) {
                    fd_close_val(val);
                }
                return Err(io_err_from_neg(dup));
            }
            let dup = dup as i32;
            if let Err(e) = set_fd_status(dup, flags) {
                unsafe { host(SYS_CLOSE, dup as u64, 0, 0, 0, 0, 0) };
                for (_, val) in restored.drain(..) {
                    fd_close_val(val);
                }
                return Err(e);
            }
            restored.push((gfd, FdVal::Host(dup)));
        }
        Ok(restored)
    })();
    unsafe {
        host(SYS_CLOSE, read_fd as u64, 0, 0, 0, 0, 0);
        host(SYS_CLOSE, write_fd as u64, 0, 0, 0, 0, 0);
    }
    result
}

#[cfg(all(target_os = "linux", target_arch = "x86_64"))]
fn restore_supervisor_state_for_vpid(
    host_pid: i32,
    vpid: state_snap::Vpid,
    state: &state_snap::StateSnapshot,
    restore_loop: bool,
) -> std::io::Result<()> {
    if !state
        .cpu_feature_baseline
        .satisfied_by(&state_snap::CpuFeatures::current())
    {
        return Err(std::io::Error::new(
            std::io::ErrorKind::InvalidData,
            "snapshot: CPU feature baseline is not satisfied by this host",
        ));
    }

    if restore_loop {
        restore_loop_state_from_snapshot(&state.netstack);
    }

    if let Some((_, cwd)) = state.cwds.iter().find(|(rec_vpid, _)| *rec_vpid == vpid) {
        cwds().lock().unwrap().insert(host_pid, cwd.clone());
    }
    if let Some((_, cmdline)) = state
        .cmdlines
        .iter()
        .find(|(rec_vpid, _)| *rec_vpid == vpid)
    {
        proc_cmdline()
            .lock()
            .unwrap()
            .insert(host_pid, cmdline.clone());
    }
    if let Some((_, exe)) = state.exes.iter().find(|(rec_vpid, _)| *rec_vpid == vpid) {
        proc_exe().lock().unwrap().insert(host_pid, exe.clone());
    }

    let mut restored = Vec::new();
    let mut pipe_groups: BTreeMap<u64, PipeRestoreGroup> = BTreeMap::new();
    if let Some((_, fds)) = state
        .fdtable
        .per_vpid
        .iter()
        .find(|(rec_vpid, _)| *rec_vpid == vpid)
    {
        for (gfd, target) in fds {
            match target {
                state_snap::FdTarget::LoopSocket(state_snap::SocketHandle(sid)) => {
                    restored.push((*gfd, FdVal::Loop(*sid)));
                }
                state_snap::FdTarget::Epoll { loop_watches } => {
                    restored.push((*gfd, FdVal::Host(restore_epoll_loop_fd(loop_watches)?)));
                }
                state_snap::FdTarget::RootRelFile {
                    path,
                    offset,
                    flags,
                } => restored.push((
                    *gfd,
                    FdVal::Host(restore_root_rel_file_fd(host_pid, path, *offset, *flags)?),
                )),
                state_snap::FdTarget::DevNode { kind, flags } => restored.push((
                    *gfd,
                    FdVal::Host(restore_dev_node_fd(host_pid, *kind, *flags)?),
                )),
                state_snap::FdTarget::Pipe {
                    id,
                    end,
                    buffered,
                    flags,
                } => {
                    let group = pipe_groups.entry(*id).or_default();
                    if group.buffered.is_empty() {
                        group.buffered = buffered.clone();
                    } else if !buffered.is_empty() && group.buffered != *buffered {
                        return Err(std::io::Error::new(
                            std::io::ErrorKind::InvalidData,
                            "snapshot: inconsistent pipe buffers for one pipe id",
                        ));
                    }
                    group.entries.push((*gfd, *end, *flags));
                }
                state_snap::FdTarget::HostEgressSocket { .. } => {
                    return Err(std::io::Error::new(
                        std::io::ErrorKind::Unsupported,
                        "snapshot: host-egress socket restore is not implemented",
                    ));
                }
            }
        }
    }
    for group in pipe_groups.values() {
        restored.extend(restore_pipe_group(group)?);
    }
    if !restored.is_empty() {
        let mut table = fdt().lock().unwrap();
        let fds = table.entry(host_pid).or_default();
        for (gfd, val) in restored {
            fds.insert(gfd, val);
        }
    }
    Ok(())
}

#[cfg(all(target_os = "linux", target_arch = "x86_64"))]
fn restore_supervisor_state_for_warm(
    warm_pid: i32,
    state: &state_snap::StateSnapshot,
) -> std::io::Result<()> {
    restore_supervisor_state_for_vpid(warm_pid, 1, state, true)
}

#[cfg(test)]
mod c4_capture_tests {
    use super::*;
    use std::io::{Seek, SeekFrom, Write as _};
    use std::os::fd::AsRawFd;

    #[test]
    fn host_fd_to_target_regular_file_carries_offset_and_flags() {
        let path = std::env::temp_dir().join(format!("nev-c4-{}.bin", std::process::id()));
        let mut f = std::fs::OpenOptions::new()
            .read(true)
            .write(true)
            .create(true)
            .truncate(true)
            .open(&path)
            .unwrap();
        f.write_all(b"0123456789").unwrap();
        f.seek(SeekFrom::Start(4)).unwrap();
        match host_fd_to_target(f.as_raw_fd(), &HashMap::new()).unwrap() {
            Some(state_snap::FdTarget::RootRelFile { offset, flags, .. }) => {
                assert_eq!(offset, 4, "current seek offset captured");
                // O_RDWR == 2; flags should at least carry the access mode.
                assert_eq!(flags & 0x3, 2, "O_RDWR access mode captured");
            }
            other => panic!("expected RootRelFile, got {other:?}"),
        }
        let _ = std::fs::remove_file(&path);
    }

    #[test]
    fn host_fd_to_target_external_pipe_is_omitted() {
        let mut fds = [0i32; 2];
        let r = unsafe {
            host(
                SYS_PIPE2,
                fds.as_mut_ptr() as u64,
                libc::O_CLOEXEC as u64,
                0,
                0,
                0,
                0,
            )
        };
        assert_eq!(r, 0);
        let target = host_fd_to_target(fds[1], &HashMap::new()).unwrap();
        unsafe {
            host(SYS_CLOSE, fds[0] as u64, 0, 0, 0, 0, 0);
            host(SYS_CLOSE, fds[1] as u64, 0, 0, 0, 0, 0);
        }
        assert!(
            target.is_none(),
            "a pipe without its peer inside the captured fd graph is external"
        );
    }

    #[test]
    fn host_fd_to_target_closed_writer_pipe_preserves_read_buffer() {
        let mut fds = [0i32; 2];
        let r = unsafe {
            host(
                SYS_PIPE2,
                fds.as_mut_ptr() as u64,
                libc::O_CLOEXEC as u64,
                0,
                0,
                0,
                0,
            )
        };
        assert_eq!(r, 0);
        write_all_to_fd(fds[1], b"reply").unwrap();
        unsafe { host(SYS_CLOSE, fds[1] as u64, 0, 0, 0, 0, 0) };
        let read_info = pipe_fd_info(fds[0]).unwrap();
        let mut groups = HashMap::new();
        groups.insert(
            read_info.id,
            PipeCaptureGroup {
                has_read: true,
                has_write: false,
                buffered: pipe_buffered_bytes(fds[0]).unwrap(),
            },
        );
        let target = host_fd_to_target(fds[0], &groups).unwrap();
        unsafe { host(SYS_CLOSE, fds[0] as u64, 0, 0, 0, 0, 0) };
        match target {
            Some(state_snap::FdTarget::Pipe { end, buffered, .. }) => {
                assert_eq!(end, state_snap::PipeEnd::Read);
                assert_eq!(buffered, b"reply");
            }
            other => panic!("expected read Pipe with buffered bytes, got {other:?}"),
        }
    }

    #[test]
    fn host_fd_to_target_internal_pipe_carries_id_and_buffer() {
        let mut fds = [0i32; 2];
        let r = unsafe {
            host(
                SYS_PIPE2,
                fds.as_mut_ptr() as u64,
                libc::O_CLOEXEC as u64,
                0,
                0,
                0,
                0,
            )
        };
        assert_eq!(r, 0);
        write_all_to_fd(fds[1], b"abc").unwrap();
        let read_info = pipe_fd_info(fds[0]).unwrap();
        let mut groups = HashMap::new();
        groups.insert(
            read_info.id,
            PipeCaptureGroup {
                has_read: true,
                has_write: true,
                buffered: pipe_buffered_bytes(fds[0]).unwrap(),
            },
        );
        let target = host_fd_to_target(fds[0], &groups).unwrap();
        unsafe {
            host(SYS_CLOSE, fds[0] as u64, 0, 0, 0, 0, 0);
            host(SYS_CLOSE, fds[1] as u64, 0, 0, 0, 0, 0);
        }
        match target {
            Some(state_snap::FdTarget::Pipe {
                id, end, buffered, ..
            }) => {
                assert_eq!(id, read_info.id);
                assert_eq!(end, state_snap::PipeEnd::Read);
                assert_eq!(buffered, b"abc");
            }
            other => panic!("expected Pipe, got {other:?}"),
        }
    }

    #[test]
    fn restore_pipe_group_preserves_buffered_bytes() {
        let group = PipeRestoreGroup {
            buffered: b"xyz".to_vec(),
            entries: vec![
                (3, state_snap::PipeEnd::Read, O_RDONLY as u32),
                (4, state_snap::PipeEnd::Write, O_WRONLY as u32),
            ],
        };
        let restored = restore_pipe_group(&group).unwrap();
        let read_fd = match restored.iter().find(|(gfd, _)| *gfd == 3).unwrap().1 {
            FdVal::Host(h) => h,
            FdVal::Loop(_) => panic!("pipe restore returned loop fd"),
        };
        let mut buf = [0u8; 3];
        let n = unsafe {
            host(
                SYS_READ,
                read_fd as u64,
                buf.as_mut_ptr() as u64,
                buf.len() as u64,
                0,
                0,
                0,
            )
        };
        assert_eq!(n, 3);
        assert_eq!(&buf, b"xyz");
        for (_, val) in restored {
            fd_close_val(val);
        }
    }
}

fn service(slot: u64, pid: i32, nr: i64, a: u64, b: u64, c: u64, d: u64, e: u64, f: u64) -> i64 {
    // C1b: a guest fd resolving to an owned loopback socket is serviced ENTIRELY by
    // the in-process netstack (no host fd, no host syscall). connect/bind are NOT in
    // this list — they are addr-driven and may SWITCH a host fd to loop (in-arm).
    if let Some(sid) = fd_loop(pid, a as i32) {
        match nr {
            SYS_READ | SYS_WRITE | SYS_WRITEV | SYS_READV | SYS_SENDMSG | SYS_RECVMSG
            | SYS_RECVFROM | SYS_SENDTO | SYS_LISTEN | SYS_ACCEPT | SYS_ACCEPT4 | SYS_SHUTDOWN
            | SYS_GETSOCKNAME | SYS_GETPEERNAME | SYS_SETSOCKOPT | SYS_GETSOCKOPT | SYS_FCNTL
            | SYS_DUP | SYS_DUP2 | SYS_DUP3 | SYS_SENDFILE => {
                return loop_service(pid, nr, a as i32, sid, b, c, d, e, f);
            }
            _ => {}
        }
    }
    // Guest fds are VIRTUAL (per-pid tables, see fd_* above): translate them to
    // host fds up front. close/dup2/dup3/poll handle their own translation.
    let a = match nr {
        SYS_READ | SYS_WRITE | SYS_PREAD64 | SYS_PWRITE64 | SYS_GETDENTS64 | SYS_WRITEV | SYS_READV
        | SYS_FSTAT | SYS_LSEEK | SYS_IOCTL | SYS_FTRUNCATE | SYS_FSYNC | SYS_FDATASYNC
        | SYS_FCHMOD | SYS_FCHOWN | SYS_FADVISE64 | SYS_SENDFILE | SYS_LISTEN
        | SYS_SENDTO | SYS_RECVFROM | SYS_SENDMSG | SYS_RECVMSG
        | SYS_SENDMMSG | SYS_RECVMMSG
        | SYS_SHUTDOWN | SYS_ACCEPT | SYS_ACCEPT4 | SYS_GETSOCKNAME | SYS_GETPEERNAME
        | SYS_SETSOCKOPT | SYS_GETSOCKOPT | SYS_EPOLL_CTL | SYS_EPOLL_WAIT
        | SYS_EPOLL_PWAIT | SYS_TIMERFD_SETTIME | SYS_TIMERFD_GETTIME | SYS_DUP
        | SYS_FALLOCATE | SYS_PREADV | SYS_PWRITEV | SYS_PREADV2 | SYS_PWRITEV2
        | SYS_FSTATFS | SYS_COPY_FILE_RANGE | SYS_SPLICE | SYS_TEE | SYS_VMSPLICE
        | SYS_FLOCK | SYS_FCNTL
        // inotify_add_watch(fd,…) / inotify_rm_watch(fd,…) take the inotify fd in a0.
        | SYS_INOTIFY_ADD_WATCH | SYS_INOTIFY_RM_WATCH => match fd_host(pid, a as i32) {
            Some(h) => h as u64,
            None => return -9, // -EBADF
        },
        // openat/newfstatat take a DIRFD at arg0 (unless AT_FDCWD).
        SYS_OPENAT | SYS_NEWFSTATAT if a as i32 != -100 => match fd_host(pid, a as i32) {
            Some(h) => h as u64,
            None => return -9,
        },
        _ => a,
    };
    // sendfile reads from fd `b`; tee's fd_out is `b`; epoll_ctl's TARGET fd is
    // `c`; copy_file_range/splice's fd_out is `c`.
    let b = match nr {
        SYS_SENDFILE | SYS_TEE => match fd_host(pid, b as i32) {
            Some(h) => h as u64,
            None => return -9,
        },
        _ => b,
    };
    let c = match nr {
        // NB: SYS_EPOLL_CTL is intentionally NOT here — its target fd (`c`) must stay
        // the GUEST fd so the arm can route an owned loop target to LoopNet (#34).
        SYS_COPY_FILE_RANGE | SYS_SPLICE => match fd_host(pid, c as i32) {
            Some(h) => h as u64,
            None => return -9,
        },
        _ => c,
    };
    unsafe {
        match nr {
            // ---- writes (pull buffer from the cell, then write locally) ----
            SYS_WRITE => {
                let len = (c.min(CAP)) as usize;
                with_scratch(len, |buf| {
                    vm_read(pid, b, buf);
                    unsafe { host(SYS_WRITE, a, buf.as_ptr() as u64, len as u64, 0, 0, 0) }
                })
            }
            // pwrite64(fd, buf, count, offset): WRITE at a POSITION (offset = `d`).
            // The write counterpart of the SYS_PREAD64 arm above; its absence
            // returned -ENOSYS and broke any positioned write — nginx's
            // pwrite("/run/nginx.pid"), sqlite/postgres journals, log rotation. Mirror
            // SYS_WRITE, passing the offset through (host pwrite64 ignores nothing else).
            SYS_PWRITE64 => {
                let len = (c.min(CAP)) as usize;
                with_scratch(len, |buf| {
                    vm_read(pid, b, buf);
                    unsafe { host(SYS_PWRITE64, a, buf.as_ptr() as u64, len as u64, d, 0, 0) }
                })
            }
            SYS_WRITEV => {
                // pull the iovec array, then each segment; write the concatenation.
                let cnt = (c.min(1024)) as usize;
                let mut iovs = vec![0u8; cnt * 16];
                vm_read(pid, b, &mut iovs);
                let mut data = Vec::new();
                for i in 0..cnt {
                    let base = u64::from_le_bytes(iovs[i * 16..i * 16 + 8].try_into().unwrap());
                    let len = u64::from_le_bytes(iovs[i * 16 + 8..i * 16 + 16].try_into().unwrap());
                    let len = len.min(CAP) as usize;
                    let mut seg = vec![0u8; len];
                    if len > 0 {
                        vm_read(pid, base, &mut seg);
                    }
                    data.extend_from_slice(&seg);
                }
                host(
                    SYS_WRITE,
                    a,
                    data.as_ptr() as u64,
                    data.len() as u64,
                    0,
                    0,
                    0,
                )
            }
            // getdents64 on a synthetic /proc/self/fd dir → emit the guest's fd
            // numbers from its virtual fd table (not the placeholder's contents).
            SYS_GETDENTS64 if synth_fd_dirs().lock().unwrap().contains_key(&(a as i32)) => {
                let (owner, cursor) = *synth_fd_dirs().lock().unwrap().get(&(a as i32)).unwrap();
                let (recs, next) = synth_fd_dirents(owner, cursor, (c.min(CAP)) as usize);
                if !recs.is_empty() {
                    vm_write(pid, b, &recs);
                    synth_fd_dirs()
                        .lock()
                        .unwrap()
                        .insert(a as i32, (owner, next));
                }
                recs.len() as i64
            }
            // getdents64 on a synthetic /proc dir → emit the owned process tree's
            // live pids + global synthetic files (C2b). What `pgrep -f`/`ps` read.
            SYS_GETDENTS64 if synth_proc_dirs().lock().unwrap().contains_key(&(a as i32)) => {
                let cursor = *synth_proc_dirs().lock().unwrap().get(&(a as i32)).unwrap();
                let (recs, next) = synth_proc_dirents(cursor, (c.min(CAP)) as usize);
                if !recs.is_empty() {
                    vm_write(pid, b, &recs);
                    synth_proc_dirs().lock().unwrap().insert(a as i32, next);
                }
                recs.len() as i64
            }
            // ---- reads: fd,buf,len → read locally, push buffer into the cell ----
            // A pipe/socket read can block INDEFINITELY on a peer that never writes
            // (and never closes) — so use the cancel-aware host so a dead-cell strand
            // can be reclaimed (Part C). pread64/getdents64 don't block indefinitely,
            // but sharing the wrapper is harmless (only the cancel path differs).
            SYS_READ | SYS_PREAD64 | SYS_GETDENTS64 => {
                let len = (c.min(CAP)) as usize;
                with_scratch(len, |buf| {
                    let n = unsafe {
                        host_cancellable(nr, a, buf.as_mut_ptr() as u64, len as u64, d, e, f)
                    };
                    if n == CANCEL_SENTINEL {
                        return CANCEL_SENTINEL;
                    }
                    if n > 0 {
                        let w = vm_write(pid, b, &buf[..n as usize]);
                        if w != n {
                            return -14; // -EFAULT: guest buffer was not fully writable
                        }
                    }
                    n
                })
            }
            // getrandom args are (buf, buflen, flags) — NOT (fd, buf, len).
            SYS_GETRANDOM => {
                let len = (b.min(CAP)) as usize;
                with_scratch(len, |buf| {
                    let n = unsafe {
                        host(
                            SYS_GETRANDOM,
                            buf.as_mut_ptr() as u64,
                            len as u64,
                            c,
                            0,
                            0,
                            0,
                        )
                    };
                    if n > 0 {
                        vm_write(pid, a, &buf[..n as usize]);
                    }
                    n
                })
            }
            // capget/capset: capabilities are virtual and empty inside sentry.
            // Chromium's zygote treats ENOSYS from DropAllCapabilitiesOnCurrentThread
            // as fatal, while an empty cap set is the behavior it wants after the
            // sandbox drop. Accept known Linux capability ABI versions, report all
            // effective/permitted/inheritable words as zero, and accept capset as a
            // no-op drop. Unknown versions get the kernel-style preferred-version
            // writeback plus EINVAL.
            SYS_CAPGET | SYS_CAPSET => {
                const LINUX_CAPABILITY_VERSION_1: u32 = 0x1998_0330;
                const LINUX_CAPABILITY_VERSION_2: u32 = 0x2007_1026;
                const LINUX_CAPABILITY_VERSION_3: u32 = 0x2008_0522;

                if a == 0 {
                    return -14; // -EFAULT
                }
                let mut hdr = [0u8; 8];
                if vm_read(pid, a, &mut hdr) != hdr.len() as i64 {
                    return -14; // -EFAULT
                }
                let version = u32::from_ne_bytes([hdr[0], hdr[1], hdr[2], hdr[3]]);
                let words = match version {
                    LINUX_CAPABILITY_VERSION_1 => 1usize,
                    LINUX_CAPABILITY_VERSION_2 | LINUX_CAPABILITY_VERSION_3 => 2usize,
                    _ => {
                        vm_write(pid, a, &LINUX_CAPABILITY_VERSION_3.to_ne_bytes());
                        return -22; // -EINVAL
                    }
                };
                if nr == SYS_CAPGET && b != 0 {
                    let zero = vec![0u8; words * 12];
                    if vm_write(pid, b, &zero) != zero.len() as i64 {
                        return -14; // -EFAULT
                    }
                }
                0
            }
            // tgkill(tgid, tid, sig): allow signals to threads owned by any process
            // in this sandbox. Chromium/Crashpad uses helper processes to signal
            // sibling browser/GPU processes; restricting this to `tgid == pid`
            // diverges from Linux process-permission semantics and surfaces as
            // Crashpad `tgkill: Operation not permitted`.
            SYS_TGKILL => {
                let tgid = a as i32;
                let tid = b as i32;
                let mine = tgid > 0
                    && tid > 0
                    && pid_in_sandbox(tgid)
                    && std::path::Path::new(&format!("/proc/{}/task/{}", tgid, tid)).exists();
                if mine {
                    let r = host(SYS_TGKILL, a, b, c, 0, 0, 0);
                    if r == 0 && c != 0 {
                        interrupt_slots_for_pid(tgid);
                    }
                    r
                } else {
                    -1 // -EPERM
                }
            }
            // tkill(tid, sig): a guest thread signaling a THREAD — musl abort()/raise()/
            // pthread_kill all self-signal via tkill (NOT tgkill). Without this arm, nr
            // 200 fell to the `other => -ENOSYS` default and made NO host call, so a
            // guest abort()'s self-SIGABRT was never delivered: abort() then fell through
            // to its privileged `hlt` trap-guard → #GP → SIGSEGV(139) instead of a clean
            // SIGABRT(134). Gate by THREAD-GROUP membership: arg0 is a TID, but ring
            // slots store TGIDs (a thread slot's pid IS its tgid), so pid_in_sandbox(tid)
            // would wrongly reject a worker's own tid. Allow only a tid that is a task of
            // THIS cell's process — /proc/<pid>/task/<tid> exists iff `tid` is one of the
            // caller's threads (covers self + sibling pthread_kill). The servicer runs
            // privileged, so the kernel won't confine the forwarded tkill; this userspace
            // gate does, exactly like the SYS_KILL arm.
            SYS_TKILL => {
                let tid = a as i32;
                let mine = tid > 0
                    && std::path::Path::new(&format!("/proc/{}/task/{}", pid, tid)).exists();
                if mine {
                    let r = host(SYS_TKILL, a, b, 0, 0, 0, 0);
                    if r == 0 && b != 0 {
                        interrupt_slots_for_pid(pid);
                    }
                    r
                } else {
                    -1 // -EPERM
                }
            }
            // kill(pid, sig): only to a process that belongs to THIS sandbox — the
            // main cell or any pid currently occupying a ring slot (a forked child,
            // e.g. `timeout`'s child). Never a host / supervisor / other-tenant pid,
            // and never a process-group / broadcast target (pid <= 0). This is how a
            // guest can signal its own children (so `timeout` can kill its child)
            // without being able to reach outside the sandbox.
            SYS_KILL => {
                let target = a as i32;
                if pid_in_sandbox(target) {
                    let r = host(SYS_KILL, a, b, 0, 0, 0, 0);
                    if r == 0 && b != 0 {
                        interrupt_slots_for_pid(target);
                    }
                    r
                } else {
                    -3 // -ESRCH: pids outside the sandbox are not visible here.
                }
            }
            // ---- path-in: resolved within the rootfs (confined) ----
            SYS_OPEN => {
                let p = pull_cwd_path(pid, a);
                let n = open_path(pid, &p, b, c);
                if n < 0 {
                    if n == -24 {
                        log_open_emfile(pid, nr, &p);
                    }
                    n
                } else {
                    fd_install(pid, n as i32, 0)
                }
            }
            SYS_CREAT => {
                let p = pull_cwd_path(pid, a);
                let flags = (libc::O_WRONLY | libc::O_CREAT | libc::O_TRUNC) as u64;
                let n = open_path(pid, &p, flags, b);
                if n < 0 {
                    if n == -24 {
                        log_open_emfile(pid, nr, &p);
                    }
                    n
                } else {
                    fd_install(pid, n as i32, 0)
                }
            }
            SYS_OPENAT => {
                // AT_FDCWD → resolve against the pid's cwd within root; a real
                // dirfd (already translated to an in-root host fd in the prelude)
                // is the resolution base, so the path stays raw there.
                let raw = pull_path(pid, b);
                let raw_bare = match raw.split_last() {
                    Some((0, head)) => head,
                    _ => raw.as_slice(),
                };
                let n = if a as i32 == AT_FDCWD || raw_bare.first() == Some(&b'/') {
                    let p = if raw_bare.first() == Some(&b'/') {
                        raw.clone()
                    } else {
                        pull_cwd_path(pid, b)
                    };
                    open_path(pid, &p, c, d)
                } else {
                    // A dirfd-RELATIVE open of a synthesized per-process path must route
                    // through open_path's /proc-self view, not a raw host openat against
                    // the rootfs's (empty) /proc. Divert only /proc + /dev/fd; every
                    // other dirfd-relative open stays a direct, confined host openat.
                    match openat_abs_guest_path(a as i32, &raw) {
                        Some(abs) if abs.starts_with(b"/proc/") || abs.starts_with(b"/dev/fd") => {
                            open_path(pid, &abs, c, d)
                        }
                        _ => {
                            let created_candidate = (c & O_CREAT) != 0;
                            let existed_before = if created_candidate {
                                let how = OpenHow {
                                    flags: O_PATH,
                                    mode: 0,
                                    resolve: RESOLVE_BENEATH,
                                };
                                let fd = host(
                                    SYS_OPENAT2,
                                    a,
                                    raw.as_ptr() as u64,
                                    &how as *const _ as u64,
                                    std::mem::size_of::<OpenHow>() as u64,
                                    0,
                                    0,
                                );
                                if fd >= 0 {
                                    host(SYS_CLOSE, fd as u64, 0, 0, 0, 0, 0);
                                    true
                                } else {
                                    false
                                }
                            } else {
                                true
                            };
                            let fd = host(SYS_OPENAT, a, raw.as_ptr() as u64, c, d, 0, 0);
                            if fd >= 0 && created_candidate && !existed_before {
                                chown_created_fd_to_virtual(pid, fd as i32);
                            }
                            fd
                        }
                    }
                };
                if n < 0 {
                    if n == -24 {
                        let p = if a as i32 == AT_FDCWD {
                            pull_cwd_path(pid, b)
                        } else {
                            raw.clone()
                        };
                        log_open_emfile(pid, nr, &p);
                    }
                    n
                } else {
                    fd_install(pid, n as i32, 0)
                }
            }
            SYS_OPENAT2 => {
                let how = match pull_open_how(pid, c, d) {
                    Ok(how) => how,
                    Err(errno) => return errno,
                };
                let raw = pull_path(pid, b);
                let raw_bare = match raw.split_last() {
                    Some((0, head)) => head,
                    _ => raw.as_slice(),
                };
                let n = if a as i32 == AT_FDCWD || raw_bare.first() == Some(&b'/') {
                    let p = if raw_bare.first() == Some(&b'/') {
                        raw.clone()
                    } else {
                        pull_cwd_path(pid, b)
                    };
                    open_path(pid, &p, how.flags, how.mode)
                } else {
                    let hostdir = match fd_host(pid, a as i32) {
                        Some(h) => h,
                        None => return -9, // -EBADF
                    };
                    match openat_abs_guest_path(hostdir, &raw) {
                        Some(abs) if abs.starts_with(b"/proc/") || abs.starts_with(b"/dev/fd") => {
                            open_path(pid, &abs, how.flags, how.mode)
                        }
                        _ => {
                            let resolve = if (how.resolve & RESOLVE_IN_ROOT) != 0 {
                                how.resolve
                            } else {
                                how.resolve | RESOLVE_BENEATH
                            };
                            let host_how = OpenHow {
                                flags: how.flags,
                                mode: mode_for_open_flags(pid, how.flags, how.mode),
                                resolve,
                            };
                            let created_candidate = (how.flags & O_CREAT) != 0;
                            let existed_before = if created_candidate {
                                let exists_how = OpenHow {
                                    flags: O_PATH,
                                    mode: 0,
                                    resolve,
                                };
                                let fd = host(
                                    SYS_OPENAT2,
                                    hostdir as u64,
                                    raw.as_ptr() as u64,
                                    &exists_how as *const _ as u64,
                                    std::mem::size_of::<OpenHow>() as u64,
                                    0,
                                    0,
                                );
                                if fd >= 0 {
                                    host(SYS_CLOSE, fd as u64, 0, 0, 0, 0, 0);
                                    true
                                } else {
                                    false
                                }
                            } else {
                                true
                            };
                            let fd = host(
                                SYS_OPENAT2,
                                hostdir as u64,
                                raw.as_ptr() as u64,
                                &host_how as *const _ as u64,
                                std::mem::size_of::<OpenHow>() as u64,
                                0,
                                0,
                            );
                            if fd >= 0 && created_candidate && !existed_before {
                                let exact_mode = mode_for_open_flags(pid, how.flags, how.mode);
                                host(SYS_FCHMOD, fd as u64, exact_mode, 0, 0, 0, 0);
                                chown_created_fd_to_virtual(pid, fd as i32);
                            }
                            fd
                        }
                    }
                };
                if n < 0 {
                    if n == -24 {
                        log_open_emfile(pid, nr, &raw);
                    }
                    n
                } else {
                    fd_install(pid, n as i32, 0)
                }
            }
            SYS_ACCESS | SYS_FACCESSAT | SYS_FACCESSAT2 => {
                // F_OK/R_OK/W_OK/X_OK via a confined O_PATH open + guest-credential
                // DAC check. Existence alone is not enough: daemon setup scripts rely
                // on `test -w` failing after `su` when the directory is owned by root.
                // faccessat2 (glibc 2.34+ `test -x`, `which`) MUST be confined here too:
                // unhandled it fell through to the raw host delegate, so `[ -x /path ]`
                // probed the HOST fs (a confinement leak) and returned true for a guest-
                // missing file — e.g. x11-utils.postinst then ran a nonexistent
                // update-menus → exit 127 → dpkg configure failure during the bake.
                // pathname is arg1 (b) for both faccessat and faccessat2.
                let (dirfd, addr, mode, flags) = if nr == SYS_ACCESS {
                    (AT_FDCWD, a, b, 0)
                } else {
                    (a as i32, b, c, d)
                };
                if (mode & !(R_OK | W_OK | X_OK)) != 0 {
                    return -22; // -EINVAL
                }
                let raw = pull_path(pid, addr);
                let raw_bare = match raw.split_last() {
                    Some((0, head)) => head,
                    _ => raw.as_slice(),
                };
                // Empty pathname → ENOENT (Linux: only AT_EMPTY_PATH makes it the dirfd).
                // dash collapses `[ -x "$(command -v X)" ]` to `[ -x "" ]` when X is
                // absent; pull_cwd_path("") would otherwise resolve to the CWD (which
                // exists) and the guard would run a missing binary — the same
                // x11-utils.postinst → update-menus → 127 → dpkg failure chain.
                let mut close_after = true;
                let fd = if raw_bare.is_empty() {
                    if nr == SYS_ACCESS || (flags & AT_EMPTY_PATH) == 0 {
                        return -2; // -ENOENT
                    }
                    if dirfd == AT_FDCWD {
                        let cwd = cwd_of(pid);
                        opath(pid, &cwd, false)
                    } else {
                        close_after = false;
                        match fd_host(pid, dirfd) {
                            Some(h) => h as i64,
                            None => return -9, // -EBADF
                        }
                    }
                } else if nr == SYS_ACCESS || dirfd == AT_FDCWD || raw_bare.first() == Some(&b'/') {
                    let p = if raw_bare.first() == Some(&b'/') {
                        raw.clone()
                    } else {
                        pull_cwd_path(pid, addr)
                    };
                    opath(pid, &p, false)
                } else {
                    let hostdir = match fd_host(pid, dirfd) {
                        Some(h) => h,
                        None => return -9, // -EBADF
                    };
                    if let Some(abs) = openat_abs_guest_path(hostdir, &raw) {
                        opath(pid, &abs, false)
                    } else {
                        let how = OpenHow {
                            flags: O_PATH,
                            mode: 0,
                            resolve: RESOLVE_BENEATH,
                        };
                        host(
                            SYS_OPENAT2,
                            hostdir as u64,
                            raw.as_ptr() as u64,
                            &how as *const _ as u64,
                            std::mem::size_of::<OpenHow>() as u64,
                            0,
                            0,
                        )
                    }
                };
                if fd < 0 {
                    return fd;
                }
                let use_effective = nr != SYS_ACCESS && (flags & AT_EACCESS) != 0;
                let r = faccess_fd(pid, fd as i32, mode, use_effective);
                if close_after {
                    host(SYS_CLOSE, fd as u64, 0, 0, 0, 0, 0);
                }
                r
            }
            SYS_STATFS => {
                let p = pull_cwd_path(pid, a);
                let fd = opath(pid, &p, false);
                if fd < 0 {
                    return fd;
                }
                let mut sb = [0u8; 120];
                let n = host(SYS_FSTATFS, fd as u64, sb.as_mut_ptr() as u64, 0, 0, 0, 0);
                host(SYS_CLOSE, fd as u64, 0, 0, 0, 0, 0);
                if n == 0 {
                    vm_write(pid, b, &sb);
                }
                n
            }
            // ---- stat family: confined O_PATH handle, then fstat on it ----
            SYS_STAT | SYS_LSTAT => {
                let p = pull_cwd_path(pid, a);
                let fd = opath(pid, &p, nr == SYS_LSTAT);
                if fd < 0 {
                    return fd;
                }
                let mut sb = [0u8; 144];
                let n = host(SYS_FSTAT, fd as u64, sb.as_mut_ptr() as u64, 0, 0, 0, 0);
                host(SYS_CLOSE, fd as u64, 0, 0, 0, 0, 0);
                if n == 0 {
                    vm_write(pid, b, &sb);
                }
                n
            }
            SYS_FSTAT => {
                let mut sb = [0u8; 144];
                let n = host(SYS_FSTAT, a, sb.as_mut_ptr() as u64, 0, 0, 0, 0);
                if n == 0 {
                    vm_write(pid, b, &sb);
                }
                n
            }
            SYS_NEWFSTATAT => {
                if a as i32 == -100 {
                    let p = pull_cwd_path(pid, b);
                    let fd = opath(pid, &p, (d & 0x100) != 0); // AT_SYMLINK_NOFOLLOW
                    if fd < 0 {
                        return fd;
                    }
                    let mut sb = [0u8; 144];
                    let n = host(SYS_FSTAT, fd as u64, sb.as_mut_ptr() as u64, 0, 0, 0, 0);
                    host(SYS_CLOSE, fd as u64, 0, 0, 0, 0, 0);
                    if n == 0 {
                        vm_write(pid, c, &sb);
                    }
                    n
                } else {
                    let p = pull_path(pid, b);
                    // Chromium's sandbox HasOpenDirectory (sandbox/linux/services/proc_util.cc)
                    // opens "self/fd/" relative to its own /proc dirfd, then for each entry does
                    // `fstatat(proc_self_fd, "<N>", &s, 0)` and aborts if any is a directory
                    // (sandbox_linux.cc:427). That dirfd is OUR synthetic /proc/self/fd
                    // placeholder; "<N>" is a GUEST virtual fd. The generic path below resolves
                    // it via openat_abs_guest_path → "/proc/self/fd/<N>" → opath, which opens the
                    // CELL's REAL fd N — but sentry's internal rootfs/shm dir fds sit at low real
                    // fd numbers, so a guest whose virtual fd N collides with one is misreported
                    // as having an open directory → sandbox FATAL. Resolve "<N>" as the guest's
                    // virtual fd and fstat ITS host fd's target instead (chrome passes flags=0,
                    // i.e. follow — /proc/self/fd/<N> is a magic symlink, so fstat(H) is right).
                    if synth_fd_dirs().lock().unwrap().contains_key(&(a as i32)) {
                        let stop = p.iter().position(|&c| c == b'/' || c == 0).unwrap_or(p.len());
                        return match parse_u32(&p[..stop]) {
                            Some(n) => match fd_host(pid, n as i32) {
                                Some(h) => {
                                    let mut sb = [0u8; 144];
                                    let n2 =
                                        host(SYS_FSTAT, h as u64, sb.as_mut_ptr() as u64, 0, 0, 0, 0);
                                    if n2 == 0 {
                                        vm_write(pid, c, &sb);
                                    }
                                    n2
                                }
                                None => -9, // -EBADF: no such guest fd
                            },
                            None => {
                                // ".", ".." → the /proc/self/fd directory itself.
                                let mut sb = [0u8; 144];
                                let n2 = host(SYS_FSTAT, a, sb.as_mut_ptr() as u64, 0, 0, 0, 0);
                                if n2 == 0 {
                                    vm_write(pid, c, &sb);
                                }
                                n2
                            }
                        };
                    }
                    // A dirfd-RELATIVE stat of a synthesized per-process path must stat the
                    // /proc-self view, not raw rootfs/proc. Chromium's sandbox/linux/
                    // services/thread_helpers does fstatat(proc_fd,"self/task") to check its
                    // thread set; the raw host fstatat stats the rootfs's empty /proc and
                    // ENOENTs → a FATAL. Mirror the AT_FDCWD branch (opath + fstat) for
                    // /proc + /dev/fd; other dirfd-relative stats stay direct + confined.
                    match openat_abs_guest_path(a as i32, &p) {
                        Some(abs) if abs.starts_with(b"/proc/") || abs.starts_with(b"/dev/fd") => {
                            let fd = opath(pid, &abs, (d & 0x100) != 0);
                            if fd < 0 {
                                fd
                            } else {
                                let mut sb = [0u8; 144];
                                let n =
                                    host(SYS_FSTAT, fd as u64, sb.as_mut_ptr() as u64, 0, 0, 0, 0);
                                host(SYS_CLOSE, fd as u64, 0, 0, 0, 0, 0);
                                if n == 0 {
                                    vm_write(pid, c, &sb);
                                }
                                n
                            }
                        }
                        _ => {
                            let mut sb = [0u8; 144];
                            let n = host(
                                SYS_NEWFSTATAT,
                                a,
                                p.as_ptr() as u64,
                                sb.as_mut_ptr() as u64,
                                d,
                                0,
                                0,
                            );
                            if n == 0 {
                                vm_write(pid, c, &sb);
                            }
                            n
                        }
                    }
                }
            }
            SYS_STATX => {
                let mut sb = [0u8; 256];
                let empty = [0u8; 1];
                let raw = pull_path(pid, b);
                let raw_bare = match raw.split_last() {
                    Some((0, head)) => head,
                    _ => raw.as_slice(),
                };
                let n = if a as i32 != AT_FDCWD && raw_bare.is_empty() && (c & AT_EMPTY_PATH) != 0 {
                    let hostfd = match fd_host(pid, a as i32) {
                        Some(h) => h,
                        None => return -9,
                    };
                    host(
                        SYS_STATX,
                        hostfd as u64,
                        empty.as_ptr() as u64,
                        c,
                        d,
                        sb.as_mut_ptr() as u64,
                        0,
                    )
                } else if a as i32 == AT_FDCWD || raw_bare.first() == Some(&b'/') {
                    let p = if raw_bare.first() == Some(&b'/') {
                        raw
                    } else {
                        pull_cwd_path(pid, b)
                    };
                    let fd = opath(pid, &p, (c & 0x100) != 0);
                    if fd < 0 {
                        return fd;
                    }
                    // statx(fd, "", AT_EMPTY_PATH, mask, buf)
                    let n = host(
                        SYS_STATX,
                        fd as u64,
                        empty.as_ptr() as u64,
                        AT_EMPTY_PATH,
                        d,
                        sb.as_mut_ptr() as u64,
                        0,
                    );
                    host(SYS_CLOSE, fd as u64, 0, 0, 0, 0, 0);
                    n
                } else {
                    let hostdir = match fd_host(pid, a as i32) {
                        Some(h) => h,
                        None => return -9,
                    };
                    match openat_abs_guest_path(hostdir, &raw) {
                        Some(abs) if abs.starts_with(b"/proc/") || abs.starts_with(b"/dev/fd") => {
                            let fd = opath(pid, &abs, (c & 0x100) != 0);
                            if fd < 0 {
                                fd
                            } else {
                                let n = host(
                                    SYS_STATX,
                                    fd as u64,
                                    empty.as_ptr() as u64,
                                    AT_EMPTY_PATH,
                                    d,
                                    sb.as_mut_ptr() as u64,
                                    0,
                                );
                                host(SYS_CLOSE, fd as u64, 0, 0, 0, 0, 0);
                                n
                            }
                        }
                        _ => host(
                            SYS_STATX,
                            hostdir as u64,
                            raw.as_ptr() as u64,
                            c,
                            d,
                            sb.as_mut_ptr() as u64,
                            0,
                        ),
                    }
                };
                if n == 0 {
                    vm_write(pid, e, &sb);
                }
                n
            }
            // ---- readlink (path-in, buf-out): confined O_PATH then readlinkat ----
            SYS_READLINK | SYS_READLINKAT => {
                let (path_ptr, out_ptr, sz) = if nr == SYS_READLINK {
                    (a, b, c)
                } else {
                    (b, c, d)
                };
                // readlinkat with a REAL dirfd must resolve relative to that dirfd, not
                // the cwd (pull_cwd_path ignores it) — so a dirfd-relative /proc symlink
                // routes through proc_self_readlink. Chromium's proc_util walks
                // /proc/self/fd via readlinkat(proc_self_fd_dir, "<n>"). Reconstruct the
                // absolute guest path from the dirfd; fall back to cwd if unrecoverable.
                let p = if nr == SYS_READLINKAT && a as i32 != -100 {
                    fd_host(pid, a as i32)
                        .and_then(|h| openat_abs_guest_path(h, &pull_path(pid, path_ptr)))
                        .unwrap_or_else(|| pull_cwd_path(pid, path_ptr))
                } else {
                    pull_cwd_path(pid, path_ptr)
                };
                // Per-process /proc symlinks (/proc/self/exe, /proc/self/fd/<n>)
                // are synthesized — there's no real symlink to readlinkat.
                let bare = match p.split_last() {
                    Some((0, head)) => head,
                    _ => p.as_slice(),
                };
                if let Some(target) = proc_self_readlink(pid, bare) {
                    let w = target.len().min(sz as usize);
                    if w > 0 {
                        vm_write(pid, out_ptr, &target[..w]);
                    }
                    return w as i64;
                }
                // /dev/fd is the standard symlink to /proc/self/fd.
                if bare == b"/dev/fd" {
                    let target = b"/proc/self/fd";
                    let w = target.len().min(sz as usize);
                    if w > 0 {
                        vm_write(pid, out_ptr, &target[..w]);
                    }
                    return w as i64;
                }
                let fd = opath(pid, &p, true); // don't follow the final symlink
                if fd < 0 {
                    return fd; // path doesn't exist (or unreachable) → ENOENT, correct
                }
                let len = (sz.min(CAP)) as usize;
                let mut buf = vec![0u8; len];
                let empty = [0u8; 1];
                let n = host(
                    SYS_READLINKAT,
                    fd as u64,
                    empty.as_ptr() as u64,
                    buf.as_mut_ptr() as u64,
                    len as u64,
                    0,
                    0,
                );
                host(SYS_CLOSE, fd as u64, 0, 0, 0, 0, 0);
                if n > 0 {
                    vm_write(pid, out_ptr, &buf[..n as usize]);
                    n
                } else {
                    // opath SUCCEEDED (the path exists) but the empty-path readlinkat
                    // failed ⇒ the target is NOT a symlink. POSIX readlink(2) returns
                    // EINVAL for a non-symlink; the host's empty-path readlinkat can
                    // surface ENOENT here, which breaks glibc realpath()/canonicalize
                    // (it reads ENOENT as "path missing" and fails — apt's flAbsPath,
                    // Chromium's data-dir canonicalization, etc.). Normalize to EINVAL.
                    -22 // -EINVAL
                }
            }
            // ---- directory / path mutations: rootfs-confined via a parent-dir
            // resolve + the `*at` form (see `confined_parent`). Each closes the
            // O_PATH parent fd it opened. ----
            SYS_MKDIR | SYS_MKDIRAT => {
                let (dirfd, path_ptr, mode) = if nr == SYS_MKDIR {
                    (AT_FDCWD, a, b)
                } else {
                    (a as i32, b, c)
                };
                match confined_parent(pid, dirfd, path_ptr) {
                    Ok((pfd, base)) => {
                        let mode = apply_umask(pid, mode & 0o7777);
                        let r = host(SYS_MKDIRAT, pfd as u64, base.as_ptr() as u64, mode, 0, 0, 0);
                        if r == 0 {
                            host(SYS_FCHMODAT, pfd as u64, base.as_ptr() as u64, mode, 0, 0, 0);
                            chown_created_at_to_virtual(pid, pfd, &base, 0);
                        }
                        host(SYS_CLOSE, pfd as u64, 0, 0, 0, 0, 0);
                        r
                    }
                    Err(e) => e,
                }
            }
            SYS_RMDIR | SYS_UNLINK | SYS_UNLINKAT => {
                let (dirfd, path_ptr, flags) = match nr {
                    SYS_RMDIR => (AT_FDCWD, a, AT_REMOVEDIR),
                    SYS_UNLINK => (AT_FDCWD, a, 0),
                    _ => (a as i32, b, c),
                };
                if flags & AT_REMOVEDIR == 0 {
                    let raw = pull_path(pid, path_ptr);
                    let raw_bare = match raw.split_last() {
                        Some((0, head)) => head,
                        _ => raw.as_slice(),
                    };
                    let abs = if raw_bare.first() == Some(&b'/') {
                        raw.clone()
                    } else if dirfd == AT_FDCWD {
                        pull_cwd_path(pid, path_ptr)
                    } else {
                        fd_host(pid, dirfd)
                            .and_then(|h| openat_abs_guest_path(h, &raw))
                            .unwrap_or_else(|| raw.clone())
                    };
                    if let Some(mut alias) = unix_socket_alias_for_guest_path(&abs) {
                        alias.push(0);
                        return host(
                            SYS_UNLINKAT,
                            AT_FDCWD as u64,
                            alias.as_ptr() as u64,
                            0,
                            0,
                            0,
                            0,
                        );
                    }
                }
                match confined_parent(pid, dirfd, path_ptr) {
                    Ok((pfd, base)) => {
                        let r = host(
                            SYS_UNLINKAT,
                            pfd as u64,
                            base.as_ptr() as u64,
                            flags,
                            0,
                            0,
                            0,
                        );
                        host(SYS_CLOSE, pfd as u64, 0, 0, 0, 0, 0);
                        r
                    }
                    Err(e) => e,
                }
            }
            SYS_SYMLINK | SYS_SYMLINKAT => {
                // target is an arbitrary string (the symlink CONTENTS — not
                // resolved); only the link's parent is confined.
                let (target_ptr, dirfd, link_ptr) = if nr == SYS_SYMLINK {
                    (a, AT_FDCWD, b)
                } else {
                    (a, b as i32, c)
                };
                let target = pull_path(pid, target_ptr);
                match confined_parent(pid, dirfd, link_ptr) {
                    Ok((pfd, base)) => {
                        let r = host(
                            SYS_SYMLINKAT,
                            target.as_ptr() as u64,
                            pfd as u64,
                            base.as_ptr() as u64,
                            0,
                            0,
                            0,
                        );
                        if r == 0 {
                            chown_created_at_to_virtual(pid, pfd, &base, AT_SYMLINK_NOFOLLOW);
                        }
                        host(SYS_CLOSE, pfd as u64, 0, 0, 0, 0, 0);
                        r
                    }
                    Err(e) => e,
                }
            }
            SYS_CHMOD | SYS_FCHMODAT => {
                let (dirfd, path_ptr, mode, flags) = if nr == SYS_CHMOD {
                    (AT_FDCWD, a, b, 0)
                } else {
                    (a as i32, b, c, d)
                };
                let raw = pull_path(pid, path_ptr);
                let raw_bare = match raw.split_last() {
                    Some((0, head)) => head,
                    _ => raw.as_slice(),
                };
                let abs = if raw_bare.first() == Some(&b'/') {
                    raw.clone()
                } else if dirfd == AT_FDCWD {
                    pull_cwd_path(pid, path_ptr)
                } else {
                    fd_host(pid, dirfd)
                        .and_then(|h| openat_abs_guest_path(h, &raw))
                        .unwrap_or_else(|| raw.clone())
                };
                if let Some(mut alias) = unix_socket_alias_for_guest_path(&abs) {
                    alias.push(0);
                    return host(
                        SYS_FCHMODAT,
                        AT_FDCWD as u64,
                        alias.as_ptr() as u64,
                        mode,
                        flags,
                        0,
                        0,
                    );
                }
                match confined_parent(pid, dirfd, path_ptr) {
                    Ok((pfd, base)) => {
                        let r = host(
                            SYS_FCHMODAT,
                            pfd as u64,
                            base.as_ptr() as u64,
                            mode,
                            flags,
                            0,
                            0,
                        );
                        host(SYS_CLOSE, pfd as u64, 0, 0, 0, 0, 0);
                        r
                    }
                    Err(e) => e,
                }
            }
            SYS_CHOWN | SYS_LCHOWN | SYS_FCHOWNAT => {
                // AT_SYMLINK_NOFOLLOW = 0x100.
                let (dirfd, path_ptr, owner, group, flags) = match nr {
                    SYS_CHOWN => (AT_FDCWD, a, b, c, 0),
                    SYS_LCHOWN => (AT_FDCWD, a, b, c, 0x100),
                    _ => (a as i32, b, c, d, e),
                };
                match confined_parent(pid, dirfd, path_ptr) {
                    Ok((pfd, base)) => {
                        let r = host(
                            SYS_FCHOWNAT,
                            pfd as u64,
                            base.as_ptr() as u64,
                            owner,
                            group,
                            flags,
                            0,
                        );
                        host(SYS_CLOSE, pfd as u64, 0, 0, 0, 0, 0);
                        r
                    }
                    Err(e) => e,
                }
            }
            SYS_UTIMENSAT => {
                // utimensat(dirfd, path, times*, flags). path NULL ⇒ operate on
                // the dirfd itself (futimens) — translate the fd and forward.
                let times = if c != 0 {
                    let mut t = [0u8; 32];
                    vm_read(pid, c, &mut t);
                    Some(t)
                } else {
                    None
                };
                if b == 0 {
                    let hostfd = match fd_host(pid, a as i32) {
                        Some(h) => h as u64,
                        None => return -9,
                    };
                    let tp = times.as_ref().map(|t| t.as_ptr() as u64).unwrap_or(0);
                    host(SYS_UTIMENSAT, hostfd, 0, tp, d, 0, 0)
                } else {
                    match confined_parent(pid, a as i32, b) {
                        Ok((pfd, base)) => {
                            let tp = times.as_ref().map(|t| t.as_ptr() as u64).unwrap_or(0);
                            let r =
                                host(SYS_UTIMENSAT, pfd as u64, base.as_ptr() as u64, tp, d, 0, 0);
                            host(SYS_CLOSE, pfd as u64, 0, 0, 0, 0, 0);
                            r
                        }
                        Err(e) => e,
                    }
                }
            }
            SYS_TRUNCATE => {
                // truncate(path, len): open the path confined RW, then ftruncate.
                let p = pull_cwd_path(pid, a);
                let fd = open_path(pid, &p, libc::O_WRONLY as u64, 0);
                if fd < 0 {
                    fd
                } else {
                    let r = host(SYS_FTRUNCATE, fd as u64, b, 0, 0, 0, 0);
                    host(SYS_CLOSE, fd as u64, 0, 0, 0, 0, 0);
                    r
                }
            }
            SYS_RENAME | SYS_RENAMEAT | SYS_RENAMEAT2 => {
                // Resolve BOTH endpoints' parents confined, then renameat2.
                let (od, op, nd, np, flags) = match nr {
                    SYS_RENAME => (AT_FDCWD, a, AT_FDCWD, b, 0),
                    SYS_RENAMEAT => (a as i32, b, c as i32, d, 0),
                    _ => (a as i32, b, c as i32, d, e),
                };
                match confined_parent(pid, od, op) {
                    Ok((opfd, obase)) => match confined_parent(pid, nd, np) {
                        Ok((npfd, nbase)) => {
                            let r = host(
                                SYS_RENAMEAT2,
                                opfd as u64,
                                obase.as_ptr() as u64,
                                npfd as u64,
                                nbase.as_ptr() as u64,
                                flags,
                                0,
                            );
                            host(SYS_CLOSE, opfd as u64, 0, 0, 0, 0, 0);
                            host(SYS_CLOSE, npfd as u64, 0, 0, 0, 0, 0);
                            r
                        }
                        Err(e) => {
                            host(SYS_CLOSE, opfd as u64, 0, 0, 0, 0, 0);
                            e
                        }
                    },
                    Err(e) => e,
                }
            }
            SYS_LINK | SYS_LINKAT => {
                // hardlink: confine BOTH the existing target's parent and the new
                // link's parent. linkat(oldpfd, oldbase, newpfd, newbase, flags).
                let (od, op, nd, np, flags) = if nr == SYS_LINK {
                    (AT_FDCWD, a, AT_FDCWD, b, 0)
                } else {
                    (a as i32, b, c as i32, d, e)
                };
                // O_TMPFILE atomic-publish materialization. Two equivalent forms:
                //   (a) linkat(fd, "", newdir, name, AT_EMPTY_PATH)
                //   (b) linkat(AT_FDCWD, "/proc/self/fd/N", newdir, name, AT_SYMLINK_FOLLOW)
                // both link the inode an open fd refers to into `newdir/name`. apk-tools
                // (b), dpkg, and Go's os.CreateTemp use this to publish a download
                // atomically. The OLD side is a fd, not a rootfs path: routing it
                // through `confined_parent` resolves the inode via the host `/proc`,
                // and the kernel then refuses the proc-vs-rootfs link with a SPURIOUS
                // EXDEV (apk → "Cross-device link"). Resolve the guest fd to its real
                // host fd and link it directly via AT_EMPTY_PATH into the confined NEW
                // parent, which lands the inode in the rootfs dir as intended.
                let tmpfile_old_fd: Option<i32> = if nr == SYS_LINKAT {
                    if (flags & AT_EMPTY_PATH) != 0 {
                        Some(od)
                    } else {
                        // Recognize a `/proc/self/fd/N` (or `/proc/<pid>/fd/N`) old path.
                        let raw = pull_path(pid, op);
                        let bare = match raw.split_last() {
                            Some((0, head)) => head,
                            _ => raw.as_slice(),
                        };
                        proc_self_rel(pid, bare)
                            .and_then(|rest| rest.strip_prefix(b"fd/"))
                            .and_then(parse_u32)
                    }
                } else {
                    None
                };
                if let Some(gfd) = tmpfile_old_fd {
                    let oldh = match fd_host(pid, gfd) {
                        Some(h) => h,
                        None => return -9, // -EBADF
                    };
                    return match confined_parent(pid, nd, np) {
                        Ok((npfd, nbase)) => {
                            let r = host(
                                SYS_LINKAT,
                                oldh as u64,
                                b"\0".as_ptr() as u64,
                                npfd as u64,
                                nbase.as_ptr() as u64,
                                AT_EMPTY_PATH,
                                0,
                            );
                            host(SYS_CLOSE, npfd as u64, 0, 0, 0, 0, 0);
                            r
                        }
                        Err(e) => e,
                    };
                }
                match confined_parent(pid, od, op) {
                    Ok((opfd, obase)) => match confined_parent(pid, nd, np) {
                        Ok((npfd, nbase)) => {
                            let r = host(
                                SYS_LINKAT,
                                opfd as u64,
                                obase.as_ptr() as u64,
                                npfd as u64,
                                nbase.as_ptr() as u64,
                                flags,
                                0,
                            );
                            host(SYS_CLOSE, opfd as u64, 0, 0, 0, 0, 0);
                            host(SYS_CLOSE, npfd as u64, 0, 0, 0, 0, 0);
                            r
                        }
                        Err(e) => {
                            host(SYS_CLOSE, opfd as u64, 0, 0, 0, 0, 0);
                            e
                        }
                    },
                    Err(e) => e,
                }
            }
            SYS_GETCWD => {
                // Return the PID's guest cwd (NOT the supervisor's host cwd).
                // The raw getcwd(2) contract: write the NUL-terminated path and
                // return its length INCLUDING the NUL; -ERANGE if it won't fit.
                let mut cwd = cwd_of(pid);
                cwd.push(0);
                if cwd.len() > b as usize {
                    -34 // -ERANGE
                } else {
                    vm_write(pid, a, &cwd);
                    cwd.len() as i64
                }
            }
            // ---- fixed struct-out, no input pointer ----
            SYS_UNAME => {
                let mut sb = [0u8; 390];
                let n = host(SYS_UNAME, sb.as_mut_ptr() as u64, 0, 0, 0, 0, 0);
                if n == 0 {
                    vm_write(pid, a, &sb);
                }
                n
            }
            SYS_SYSINFO => {
                let mut sb = [0u8; 112];
                let n = host(SYS_SYSINFO, sb.as_mut_ptr() as u64, 0, 0, 0, 0, 0);
                if n == 0 {
                    vm_write(pid, a, &sb);
                }
                n
            }
            // getrusage(who@a, rusage*@b OUT): per-process CPU/memory accounting
            // (Node process.resourceUsage, Python resource.getrusage, /usr/bin/time).
            // `who` (RUSAGE_SELF/CHILDREN/THREAD) is a plain constant — no fd, no
            // cell pointer in. Host into a local 144B buf, copy out on success.
            SYS_GETRUSAGE => {
                let mut rb = [0u8; RUSAGE_SZ];
                let n = host(SYS_GETRUSAGE, a, rb.as_mut_ptr() as u64, 0, 0, 0, 0);
                if n == 0 {
                    vm_write(pid, b, &rb);
                }
                n
            }
            // times(tms*@a OUT): user/sys clock-ticks for self + children. 32B struct
            // OUT; the RETURN value is clock_t (ticks since an arbitrary past epoch),
            // forwarded as-is. Marshal like SYSINFO but write `a` (the only arg).
            SYS_TIMES => {
                let mut tb = [0u8; 32];
                let n = host(SYS_TIMES, tb.as_mut_ptr() as u64, 0, 0, 0, 0, 0);
                // times(NULL) is legal (just returns the tick count) — only copy out
                // the struct when the guest supplied a buffer.
                if n >= 0 && a != 0 {
                    vm_write(pid, a, &tb);
                }
                n
            }
            SYS_CLOCK_GETTIME => {
                if a as i32 == libc::CLOCK_MONOTONIC || a as i32 == libc::CLOCK_BOOTTIME {
                    if b == 0 {
                        -14
                    } else {
                        let ts = guest_monotonic_timespec();
                        vm_write(pid, b, &ts);
                        0
                    }
                } else {
                    let mut ts = [0u8; 16];
                    let n = host(SYS_CLOCK_GETTIME, a, ts.as_mut_ptr() as u64, 0, 0, 0, 0);
                    if n == 0 {
                        vm_write(pid, b, &ts);
                    }
                    n
                }
            }
            SYS_GETTIMEOFDAY => {
                let mut tv = [0u8; 16];
                let n = host(SYS_GETTIMEOFDAY, tv.as_mut_ptr() as u64, 0, 0, 0, 0, 0);
                if n == 0 {
                    vm_write(pid, a, &tv);
                }
                n
            }
            SYS_PRLIMIT64 => {
                // new (in) at arg2, old (out) at arg3; either may be NULL.
                let mut nbuf = [0u8; 16];
                let np = if c != 0 {
                    if vm_read(pid, c, &mut nbuf) != nbuf.len() as i64 {
                        return -14; // -EFAULT
                    }
                    nbuf.as_ptr() as u64
                } else {
                    0
                };
                let mut obuf = [0u8; 16];
                let op = if d != 0 { obuf.as_mut_ptr() as u64 } else { 0 };
                if b == libc::RLIMIT_NOFILE as u64 {
                    let target = if a == 0 { pid } else { a as i32 };
                    if target != pid && proctree::vpid_for(target).is_none() {
                        return -1; // -EPERM
                    }
                    let (old_cur, old_max) = guest_nofile_limit(target);
                    if c != 0 {
                        let new_cur = u64::from_le_bytes(nbuf[0..8].try_into().unwrap());
                        let new_max = u64::from_le_bytes(nbuf[8..16].try_into().unwrap());
                        if new_cur > new_max {
                            return -22; // -EINVAL
                        }
                        if new_max > old_max {
                            return -1; // -EPERM without CAP_SYS_RESOURCE in the guest
                        }
                        set_guest_nofile_limit(target, new_cur, new_max);
                    }
                    if d != 0 {
                        obuf[0..8].copy_from_slice(&old_cur.to_le_bytes());
                        obuf[8..16].copy_from_slice(&old_max.to_le_bytes());
                        if vm_write(pid, d, &obuf) != obuf.len() as i64 {
                            return -14; // -EFAULT
                        }
                    }
                    return 0;
                }
                let n = host(SYS_PRLIMIT64, a, b, np, op, 0, 0);
                if n == 0 && d != 0 {
                    if vm_write(pid, d, &obuf) != obuf.len() as i64 {
                        return -14; // -EFAULT
                    }
                }
                n
            }
            // ---- sendfile (offset may be NULL; common for cat) ----
            SYS_SENDFILE => {
                if c == 0 {
                    host(SYS_SENDFILE, a, b, 0, d, 0, 0)
                } else {
                    let mut ob = [0u8; 8];
                    vm_read(pid, c, &mut ob);
                    let n = host(SYS_SENDFILE, a, b, ob.as_mut_ptr() as u64, d, 0, 0);
                    vm_write(pid, c, &ob);
                    n
                }
            }
            // ---- event loop / timers (libuv + V8) ----
            // No cell pointer, no new fd: forward directly.
            // NOTE: SYS_MEMBARRIER is handled CELL-LOCAL in dispatch_simple (it must
            // fence the cell's own threads, not the supervisor's) — never delegated here.
            SYS_SCHED_YIELD | SYS_FADVISE64 | SYS_FSYNC | SYS_FDATASYNC | SYS_FTRUNCATE
            // fchown(fd,owner,group): the fd-based sibling of fchmod. dpkg's tarobject
            // unpacks a regular file by creating the fd, writing, then fchown+fchmod ON
            // THE FD (not the path) — so without this every `apt-get install` died with
            // "error setting ownership … Function not implemented" (ENOSYS) the moment it
            // configured the first package. Path-based chown/lchown/fchownat are handled
            // separately above; `a` is already translated to the host fd in the prelude.
            | SYS_FCHMOD | SYS_FCHOWN => host(nr, a, b, c, d, e, f),
            // flock(fd, op): advisory whole-file lock. `a` was translated to the
            // host fd in the prelude; no pointer args, so forward directly. Lets
            // package managers (apk/dpkg) and git lock their DB/index files.
            SYS_FLOCK => host(SYS_FLOCK, a, b, 0, 0, 0, 0),
            // fd creators: forward, then virtualize the new fd. (dup's `a` was
            // already translated in the prelude; the others' args aren't fds.)
            // inotify_init/init1 join here: init1(flags) carries IN_NONBLOCK/
            // IN_CLOEXEC in `a`, init takes no args — both `host(nr, a, …)`.
            SYS_EPOLL_CREATE1 | SYS_EVENTFD2 | SYS_TIMERFD_CREATE | SYS_DUP | SYS_INOTIFY_INIT
            | SYS_INOTIFY_INIT1 => {
                let n = host(nr, a, b, c, d, e, f);
                if n < 0 {
                    n
                } else {
                    fd_install(pid, n as i32, 0)
                }
            }
            // Legacy aliases of the *2/*1 creators (different host nr, same shape):
            // eventfd(initval) → eventfd2(initval, 0); epoll_create(size) →
            // epoll_create1(0) (size is an ignored historical hint). Create + install.
            SYS_EVENTFD | SYS_EPOLL_CREATE => {
                let n = if nr == SYS_EVENTFD {
                    host(SYS_EVENTFD2, a, 0, 0, 0, 0, 0)
                } else {
                    host(SYS_EPOLL_CREATE1, 0, 0, 0, 0, 0, 0)
                };
                if n < 0 {
                    n
                } else {
                    fd_install(pid, n as i32, 0)
                }
            }
            // signalfd/signalfd4(fd, mask, sizemask[, flags]): expose a readable
            // stream of `signalfd_siginfo` records. Host signalfd would observe
            // supervisor signals, not guest child exits, so model it with a pipe and
            // write SIGCHLD records from CTL_REAP.
            SYS_SIGNALFD | SYS_SIGNALFD4 => {
                let gfd = a as i32;
                let flags = if nr == SYS_SIGNALFD4 { d } else { 0 };
                let mut mask = vec![0u8; c.min(1024) as usize];
                if b != 0 {
                    if !mask.is_empty() && vm_read(pid, b, &mut mask) != mask.len() as i64 {
                        return -14; // -EFAULT
                    }
                }
                if gfd != -1 {
                    match fd_host(pid, gfd) {
                        Some(h) if signalfd_writers().lock().unwrap().contains_key(&h) => {
                            return gfd as i64;
                        }
                        Some(_) => return -22, // -EINVAL: not one of our signalfds
                        None => return -9,     // -EBADF
                    }
                }
                let mut fds = [0i32; 2];
                let n = host(SYS_PIPE2, fds.as_mut_ptr() as u64, flags, 0, 0, 0, 0);
                if n < 0 {
                    n
                } else {
                    signalfd_writers().lock().unwrap().insert(fds[0], fds[1]);
                    fd_install(pid, fds[0], 0)
                }
            }
            // SysV shared memory: model segments as slices of the inherited
            // cross-cell shared pool. This covers runtimes such as Postgres that
            // use shmget/shmat for tiny process-shared control areas.
            SYS_SHMGET => sysv_shmget(a, b, c),
            SYS_SHMAT => sysv_shmat(a as i32, b, c),
            SYS_SHMDT => sysv_shmdt(a),
            SYS_SHMCTL => sysv_shmctl(pid, a as i32, b, c),
            // memfd_create(name, flags): a real anonymous host memfd in the supervisor
            // (the guest gets a normal host-backed fd). The modern shared-memory
            // primitive — runtimes (Chromium's persistent_memory_allocator/mojo, the
            // JVM, glibc shm) create a memfd, ftruncate it, and mmap it MAP_SHARED.
            // `a` is the guest name pointer (advisory — the /proc/<pid>/fd label);
            // `b` is the flags (MFD_CLOEXEC/MFD_ALLOW_SEALING, host-compatible). Cross-
            // process MAP_SHARED coherence on the result is the shared-mmap path.
            SYS_MEMFD_CREATE => {
                let name = pull_path(pid, a); // NUL-terminated guest string
                let n = host(SYS_MEMFD_CREATE, name.as_ptr() as u64, b, 0, 0, 0, 0);
                if n < 0 {
                    n
                } else {
                    fd_install(pid, n as i32, 0)
                }
            }
            // mmap reaches the supervisor ONLY for MAP_SHARED on a real fd (guest_mmap
            // routes that case here for true cross-process sharing; everything else is
            // cell-local). Back it with a shared-pool slice keyed by the fd's identity
            // (so every cell mapping the same memfd/file shares pages) and return the
            // pool address. `e`=guest fd, `b`=len, `f`=offset. -1 ⇒ the cell falls back
            // to its private-copy mmap (loop/unmapped fd, or the pool is full).
            SYS_MMAP => match fd_host(pid, e as i32) {
                Some(h) => {
                    let r = shm_pool_map(pid, h, b, c, f);
                    if unsafe { IPCTRACE } && r < 0 {
                        let mut st: libc::stat = std::mem::zeroed();
                        let sr = host(SYS_FSTAT, h as u64, &mut st as *mut libc::stat as u64, 0, 0, 0, 0);
                        ipc_logf(
                            &[
                                (b"MMAPERR pid=", pid as i64),
                                (b"gfd=", e as i64),
                                (b"hfd=", h as i64),
                                (b"len=", b as i64),
                                (b"prot=", c as i64),
                                (b"flags=", d as i64),
                                (b"off=", f as i64),
                                (b"fstat=", sr),
                                (b"dev=", st.st_dev as i64),
                                (b"ino=", st.st_ino as i64),
                                (b"size=", st.st_size),
                                (b"ret=", r),
                            ],
                            &[],
                        );
                    }
                    r
                }
                None => {
                    if unsafe { IPCTRACE } {
                        ipc_logf(
                            &[
                                (b"MMAPERR pid=", pid as i64),
                                (b"gfd=", e as i64),
                                (b"hfd=", -1),
                                (b"len=", b as i64),
                                (b"prot=", c as i64),
                                (b"flags=", d as i64),
                                (b"off=", f as i64),
                                (b"ret=", -1),
                            ],
                            &[],
                        );
                    }
                    -1
                }
            },
            // inotify_add_watch(fd, pathname, mask): `fd` was translated to the host
            // inotify fd in the prelude. Resolve `pathname` rootfs-confined to an
            // O_PATH fd (RESOLVE_IN_ROOT, bind-mount aware — same confinement as
            // every path op), then add the watch on the host's /proc/self/fd/<n>
            // alias of that O_PATH fd. This keeps the watch strictly inside the
            // rootfs (no `..`/symlink/absolute escape) while still watching the real
            // backing inode. Returns the watch descriptor.
            SYS_INOTIFY_ADD_WATCH => {
                let cand = pull_cwd_path(pid, b);
                let pfd = opath(pid, &cand, false);
                if pfd < 0 {
                    pfd
                } else {
                    let mut nb = [0u8; 24];
                    let i = fmt_i64(pfd, &mut nb);
                    let mut proc_path = b"/proc/self/fd/".to_vec();
                    proc_path.extend_from_slice(&nb[i..]);
                    proc_path.push(0);
                    let r = host(
                        SYS_INOTIFY_ADD_WATCH,
                        a,
                        proc_path.as_ptr() as u64,
                        c,
                        0,
                        0,
                        0,
                    );
                    host(SYS_CLOSE, pfd as u64, 0, 0, 0, 0, 0);
                    r
                }
            }
            // inotify_rm_watch(fd, wd): `fd` translated in the prelude; `wd` is the
            // opaque watch descriptor (not an fd) — forward directly.
            SYS_INOTIFY_RM_WATCH => host(SYS_INOTIFY_RM_WATCH, a, b, 0, 0, 0, 0),
            // mknod[at]: create a FIFO / AF_UNIX socket node / regular file inside
            // the rootfs (mkfifo, AF_UNIX bind-via-node, exotic `touch`). REFUSE
            // device nodes (S_IFCHR/S_IFBLK) with -EPERM — a cell can't mint devices.
            // S_IFMT==0 means a regular file (mknod's historical default). Confined
            // via confined_parent + mknodat, exactly like SYS_MKDIR.
            SYS_MKNOD | SYS_MKNODAT => {
                let (dirfd, path_ptr, mode, dev) = if nr == SYS_MKNOD {
                    (AT_FDCWD, a, b, c)
                } else {
                    (a as i32, b, c, d)
                };
                let typ = (mode as u32) & S_IFMT;
                if typ == S_IFCHR || typ == S_IFBLK {
                    -1 // -EPERM: no device-node creation in the cell
                } else if typ != 0 && typ != S_IFREG && typ != S_IFIFO && typ != S_IFSOCK {
                    -22 // -EINVAL: unknown node type
                } else {
                    match confined_parent(pid, dirfd, path_ptr) {
                        Ok((pfd, base)) => {
                            let mode = (mode & !(S_IFMT as u64)) | apply_umask(pid, mode & 0o7777);
                            let r = host(
                                SYS_MKNODAT,
                                pfd as u64,
                                base.as_ptr() as u64,
                                mode,
                                dev,
                                0,
                                0,
                            );
                            if r == 0 {
                                host(SYS_FCHMODAT, pfd as u64, base.as_ptr() as u64, mode & 0o7777, 0, 0, 0);
                                chown_created_at_to_virtual(pid, pfd, &base, 0);
                            }
                            host(SYS_CLOSE, pfd as u64, 0, 0, 0, 0, 0);
                            r
                        }
                        Err(e) => e,
                    }
                }
            }
            // close_range(first, last, flags): close every guest fd in [first,last],
            // or with CLOSE_RANGE_CLOEXEC mark them for the next execve without
            // closing immediately. CLOSE_RANGE_UNSHARE is a table-isolation hint; our
            // per-pid fd tables are already isolated, so it does not need extra work.
            // Returns 0 (close_range's success), or -EINVAL for an inverted range or
            // unknown flags.
            SYS_CLOSE_RANGE => {
                let first = a as u32;
                let last = b as u32;
                let flags = c;
                if first > last || (flags & !(CLOSE_RANGE_UNSHARE | CLOSE_RANGE_CLOEXEC)) != 0 {
                    -22 // -EINVAL
                } else if (flags & CLOSE_RANGE_CLOEXEC) != 0 {
                    if unsafe { FDTRACE } && first <= 11 && last >= 10 {
                        ipc_logf(
                            &[
                                (b"FDTRACE_CLOSE_RANGE_CLOEXEC pid=", pid as i64),
                                (b" first=", first as i64),
                                (b" last=", last as i64),
                            ],
                            b"",
                        );
                    }
                    let targets: Vec<(i32, FdVal)> = {
                        let t = fdt().lock().unwrap();
                        t.get(&pid)
                            .map(|m| {
                                m.iter()
                                    .filter(|(&g, _)| (g as u32) >= first && (g as u32) <= last)
                                    .map(|(&g, &v)| (g, v))
                                    .collect()
                            })
                            .unwrap_or_default()
                    };
                    for (g, v) in targets {
                        let cur = fd_get_desc_flags(pid, g, v);
                        fd_set_desc_flags(pid, g, cur | FD_CLOEXEC as i32);
                    }
                    0
                } else {
                    if unsafe { FDTRACE } && first <= 11 && last >= 10 {
                        ipc_logf(
                            &[
                                (b"FDTRACE_CLOSE_RANGE pid=", pid as i64),
                                (b" first=", first as i64),
                                (b" last=", last as i64),
                            ],
                            b"",
                        );
                    }
                    // Snapshot the matching guest fds first (don't mutate the table
                    // while iterating it).
                    let victims: Vec<i32> = {
                        let t = fdt().lock().unwrap();
                        t.get(&pid)
                            .map(|m| {
                                m.keys()
                                    .copied()
                                    .filter(|&g| (g as u32) >= first && (g as u32) <= last)
                                    .collect()
                            })
                            .unwrap_or_default()
                    };
                    for g in victims {
                        match fd_remove(pid, g) {
                            Some(FdVal::Host(h)) => {
                                fd_forget_host_side_tables(h);
                                host(SYS_CLOSE, h as u64, 0, 0, 0, 0, 0);
                            }
                            Some(FdVal::Loop(s)) => loop_close(s),
                            None => {}
                        }
                    }
                    0
                }
            }
            // dup2/dup3(old, new): per-pid — make guest `new` refer to old's file
            // description. Host side: a fresh dup of old's host fd, installed AT
            // guest `new` (closing the previous occupant). Other processes' (and
            // the supervisor's own) fds are untouched — this is what redirection
            // and fd-save/restore in a shell rely on.
            SYS_DUP2 | SYS_DUP3 => {
                let (gold, gnew) = (a as i32, b as i32);
                if nr == SYS_DUP3 && (c & !(libc::O_CLOEXEC as u64)) != 0 {
                    return -22; // -EINVAL
                }
                match fd_host(pid, gold) {
                    None => -9, // -EBADF
                    Some(_) if gold == gnew => {
                        if nr == SYS_DUP2 {
                            gnew as i64
                        } else {
                            -22 // dup3: old == new is EINVAL
                        }
                    }
                    Some(h) => {
                        let n = host(SYS_DUP, h as u64, 0, 0, 0, 0, 0);
                        if n < 0 {
                            n
                        } else {
                            let flags = if nr == SYS_DUP3 && (c & libc::O_CLOEXEC as u64) != 0 {
                                FD_CLOEXEC as i32
                            } else {
                                0
                            };
                            fd_install_val_at_with_flags(pid, gnew, FdVal::Host(n as i32), flags);
                            if unsafe { FDTRACE } && (gnew == 10 || gnew == 11) {
                                ipc_logf(
                                    &[
                                        (b"FDTRACE_DUP pid=", pid as i64),
                                        (b" old=", gold as i64),
                                        (b" new=", gnew as i64),
                                        (b" hnew=", n),
                                        (b" flags=", flags as i64),
                                    ],
                                    b"",
                                );
                                fdtrace_dump_table(pid, b"POST_DUP");
                            }
                            gnew as i64
                        }
                    }
                }
            }
            // PKU/MPK is now handled CELL-LOCAL in dispatch_simple (a pkey is per-mm, so
            // it MUST run in the cell, not the supervisor). This delegated arm is a
            // defensive fallback only — pkey never reaches the supervisor anymore.
            SYS_PKEY_ALLOC | SYS_PKEY_FREE | SYS_PKEY_MPROTECT => -38,
            // epoll_ctl(epfd, op, fd, event*) — event is IN (12B), NULL for DEL.
            SYS_EPOLL_CTL => {
                // `a` = host epfd (prelude-translated). `c` = GUEST target fd (NOT
                // prelude-translated — EPOLL_CTL removed from the c-list so an owned
                // loop target is visible here). op = `b`, epoll_event* = `d`.
                if let Some(sid) = fd_loop(pid, c as i32) {
                    return epoll_ctl_loop(pid, a as i32, b, sid, d);
                }
                let h = match fd_host(pid, c as i32) {
                    Some(h) => h,
                    None => return -9, // -EBADF
                };
                let mut ev = [0u8; 12];
                let ep = if d != 0 {
                    vm_read(pid, d, &mut ev);
                    ev.as_ptr() as u64
                } else {
                    0
                };
                host(SYS_EPOLL_CTL, a, b, h as u64, ep, 0, 0)
            }
            // epoll_wait/pwait(epfd, events*, max, timeout[, sigmask,size]) — events OUT.
            // Blocks indefinitely when timeout < 0 — cancel-aware (Part C).
            SYS_EPOLL_WAIT | SYS_EPOLL_PWAIT => {
                let max = (c.min(4096)) as usize;
                // #34: if this epoll has owned loop-fd watches, use the merge path
                // (`d` is the int ms timeout; <0 = block forever).
                let hasloop = epoll_loops()
                    .lock()
                    .unwrap()
                    .get(&(a as i32))
                    .is_some_and(|m| !m.is_empty());
                if epdbg_on() {
                    ipc_logf(
                        &[
                            (b"EPDISP pid=", pid as i64),
                            (b" epfd=", a as i64),
                            (b" tmo=", (d as i32) as i64),
                            (b" hasloop=", hasloop as i64),
                        ],
                        b"",
                    );
                }
                if hasloop {
                    return epoll_wait_merge(pid, a as i32, b, max, (d as i32) as i64);
                }
                let mut buf = vec![0u8; max * 12];
                let n = host_cancellable(SYS_EPOLL_WAIT, a, buf.as_mut_ptr() as u64, c, d, 0, 0);
                if n == CANCEL_SENTINEL {
                    return CANCEL_SENTINEL;
                }
                // A loop-fd readiness efd may have fired while we blocked on an epoll that
                // had NO loop watch at entry — a reactor blocks on its host fds (waker /
                // timers), then a WORKER thread epoll_ctl-ADDs a freshly-connected loop
                // socket (tokio's connect-completion) + signals its efd (epoll_ctl_loop).
                // The host wait then returns BOTH any host events AND that loop efd event
                // (tagged). Translate the tag IN PLACE to the guest's real readiness +
                // token, keeping the host events from this same wake (re-running the host
                // wait would drop edge-triggered host events like the reactor's waker).
                if n > 0 {
                    if epdbg_on() {
                        let e0 = u32::from_le_bytes(buf[0..4].try_into().unwrap());
                        let d0 = u64::from_le_bytes(buf[4..12].try_into().unwrap());
                        let e1 = if n >= 2 {
                            u32::from_le_bytes(buf[12..16].try_into().unwrap())
                        } else {
                            0
                        };
                        let d1 = if n >= 2 {
                            u64::from_le_bytes(buf[16..24].try_into().unwrap())
                        } else {
                            0
                        };
                        ipc_logf(
                            &[
                                (b"EPHOST epfd=", a as i64),
                                (b" tmo=", (d as i32) as i64),
                                (b" n=", n),
                                (b" ev0=", e0 as i64),
                                (b" data0=", d0 as i64),
                                (b" ev1=", e1 as i64),
                                (b" data1=", d1 as i64),
                            ],
                            b"",
                        );
                    }
                    let has_tag = (0..n as usize).any(|i| {
                        let data =
                            u64::from_le_bytes(buf[i * 12 + 4..i * 12 + 12].try_into().unwrap());
                        data & EPOLL_LOOP_TAG != 0
                    });
                    if has_tag {
                        let mut out: Vec<u8> = Vec::with_capacity(n as usize * 12);
                        let mut lt = epoll_loops().lock().unwrap();
                        let ls = loop_state().lock().unwrap();
                        for i in 0..n as usize {
                            let ev =
                                u32::from_le_bytes(buf[i * 12..i * 12 + 4].try_into().unwrap());
                            let data = u64::from_le_bytes(
                                buf[i * 12 + 4..i * 12 + 12].try_into().unwrap(),
                            );
                            if data & EPOLL_LOOP_TAG != 0 {
                                let sid = (data & !EPOLL_LOOP_TAG) as u32;
                                // NB: do NOT drain the readiness efd here — a blocking
                                // SYS_READ while holding loop_state+epoll_loops would
                                // deadlock if a racing servicer drained it first. The efd
                                // is level-triggered; the next merge-path poll drains it.
                                if let Some(v) =
                                    lt.get_mut(&(a as i32)).and_then(|m| m.get_mut(&sid))
                                {
                                    let (wev, gdata) = (v.0, v.1);
                                    let mut ready = 0u32;
                                    if (wev & EPOLLIN_B) != 0 && ls.net.readable(sid) {
                                        ready |= EPOLLIN_B;
                                    }
                                    if (wev & EPOLLOUT_B) != 0 && ls.net.writable(sid) {
                                        ready |= EPOLLOUT_B;
                                    }
                                    let re = et_filter(wev, ready, &mut v.2);
                                    if re != 0 {
                                        out.extend_from_slice(&re.to_le_bytes());
                                        out.extend_from_slice(&gdata.to_le_bytes());
                                    }
                                } else {
                                    // (epfd=a, sid) has NO loop-watch table entry, yet the
                                    // kernel epoll `a` returned sid's readiness efd. This
                                    // is the dup-aliased-epoll desync: `a` shares its
                                    // kernel epoll object with a SIBLING fd (a fork/dup of
                                    // the guest's epoll). tokio registered sid's watch
                                    // (e.g. the freshly-connected Postgres socket, EPOLLOUT
                                    // connect-completion) via the SIBLING fd — so
                                    // `table[(sib, sid)]` exists — but the reactor calls
                                    // epoll_wait on `a`, whose table lacks (a, sid). The
                                    // shared kernel epoll still delivers sid's efd here, so
                                    // RESOLVE the readiness via the sibling watch and emit
                                    // it on `a`. Without this the connect-completion is
                                    // dropped → the reactor 0-event-wakes, re-polls, the
                                    // efd is still ready → a 100% busy-spin livelock that
                                    // never drives the connection (Prisma's P1001 under
                                    // sentry). Truly stale orphans (no sibling watch at
                                    // all) fall through to a non-blocking EPOLL_CTL_DEL so
                                    // the kernel stops re-firing them.
                                    let sib_watch = lt.iter().find_map(|(ep2, m)| {
                                        if *ep2 != a as i32 {
                                            m.get(&sid).map(|w| (w.0, w.1))
                                        } else {
                                            None
                                        }
                                    });
                                    if let Some((wev, gdata)) = sib_watch {
                                        // LAZILY ADOPT the sibling watch into THIS epfd's
                                        // table with its OWN edge-state. Reusing the
                                        // sibling's shared et-state across multiple aliases
                                        // races (the edge never gets suppressed → perpetual
                                        // re-delivery → reactor busy-spin under clean
                                        // timing). A per-(epfd,sid) entry gives this fd
                                        // independent edge tracking and lets future polls +
                                        // the merge pre-check find it directly.
                                        let v = lt
                                            .entry(a as i32)
                                            .or_default()
                                            .entry(sid)
                                            .or_insert((wev, gdata, 0));
                                        let mut ready = 0u32;
                                        if (wev & EPOLLIN_B) != 0 && ls.net.readable(sid) {
                                            ready |= EPOLLIN_B;
                                        }
                                        if (wev & EPOLLOUT_B) != 0 && ls.net.writable(sid) {
                                            ready |= EPOLLOUT_B;
                                        }
                                        let re = et_filter(wev, ready, &mut v.2);
                                        if re != 0 {
                                            out.extend_from_slice(&re.to_le_bytes());
                                            out.extend_from_slice(&gdata.to_le_bytes());
                                        }
                                    } else if let Some(&efd) = ls.efds.get(&sid) {
                                        unsafe {
                                            host(SYS_EPOLL_CTL, a, 2, efd as u64, 0, 0, 0);
                                        }
                                    }
                                }
                            } else {
                                out.extend_from_slice(&ev.to_le_bytes());
                                out.extend_from_slice(&data.to_le_bytes());
                            }
                        }
                        drop(ls);
                        drop(lt);
                        let cnt = out.len() / 12;
                        if cnt > 0 {
                            vm_write(pid, b, &out);
                        }
                        return cnt as i64;
                    }
                    vm_write(pid, b, &buf[..(n as usize) * 12]);
                }
                if unsafe { WAITTRACE } {
                    let (ev0, dat0) = if n > 0 {
                        (
                            u32::from_le_bytes(buf[0..4].try_into().unwrap()) as i64,
                            u64::from_le_bytes(buf[4..12].try_into().unwrap()) as i64,
                        )
                    } else {
                        (0, 0)
                    };
                    ipc_logf(
                        &[
                            (b"EPWAIT pid=", pid as i64),
                            (b"epfd=", a as i64),
                            (b"tmo=", (d as i32) as i64),
                            (b"ret=", n),
                            (b"ev0=", ev0),
                            (b"dat0=", dat0),
                        ],
                        &[],
                    );
                }
                n
            }
            // timerfd_settime(fd, flags, new*, old*) — new IN (32B itimerspec), old OUT.
            SYS_TIMERFD_SETTIME => {
                let mut nb = [0u8; 32];
                let np = if c != 0 {
                    vm_read(pid, c, &mut nb);
                    nb.as_ptr() as u64
                } else {
                    0
                };
                let mut ob = [0u8; 32];
                let op = if d != 0 { ob.as_mut_ptr() as u64 } else { 0 };
                let n = host(SYS_TIMERFD_SETTIME, a, b, np, op, 0, 0);
                if n == 0 && d != 0 {
                    vm_write(pid, d, &ob);
                }
                n
            }
            SYS_TIMERFD_GETTIME => {
                let mut ob = [0u8; 32];
                let n = host(SYS_TIMERFD_GETTIME, a, ob.as_mut_ptr() as u64, 0, 0, 0, 0);
                if n == 0 {
                    vm_write(pid, b, &ob);
                }
                n
            }
            // pipe(fds*) / pipe2(fds*, flags) — OUT int[2]; both ends virtualized.
            SYS_PIPE | SYS_PIPE2 => {
                let mut fds = [0u8; 8];
                let n = host(nr, fds.as_mut_ptr() as u64, b, 0, 0, 0, 0);
                if n != 0 {
                    n
                } else {
                    let h0 = i32::from_le_bytes(fds[0..4].try_into().unwrap());
                    let h1 = i32::from_le_bytes(fds[4..8].try_into().unwrap());
                    let g0 = fd_install(pid, h0, 0) as i32;
                    let g1 = fd_install(pid, h1, 0) as i32;
                    let mut out = [0u8; 8];
                    out[0..4].copy_from_slice(&g0.to_le_bytes());
                    out[4..8].copy_from_slice(&g1.to_le_bytes());
                    // The guest's OUT `int fds[2]` MUST receive the guest fd numbers. If
                    // the write-back fails (e.g. a wrong/dead process_vm target), DON'T
                    // mask it as host success: the guest would copy uninitialized stack
                    // into its fds and use a bogus fd downstream (libuv sized an ~8 GiB
                    // watcher array off one such fd → abort). Roll back the installs +
                    // host fds and fail the syscall with -EFAULT so it surfaces loudly.
                    if vm_write(pid, a, &out) < 0 {
                        let _ = fd_remove(pid, g0);
                        let _ = fd_remove(pid, g1);
                        host(SYS_CLOSE, h0 as u64, 0, 0, 0, 0, 0);
                        host(SYS_CLOSE, h1 as u64, 0, 0, 0, 0, 0);
                        -14 // -EFAULT
                    } else {
                        n // 0
                    }
                }
            }
            // socketpair(domain, type, protocol, sv*) — OUT int[2]; a CONNECTED
            // local pair, modeled exactly on pipe2 (both fds virtualized). The pair
            // is AF_UNIX/AF_LOCAL — entirely host-local, so no egress-policy /
            // netns mediation applies (unlike SYS_SOCKET). The args are passed
            // verbatim: arg0 is the domain (NOT an fd, so the fd-translation
            // prelude correctly leaves it alone); the sv* OUT pointer is in `d`.
            SYS_SOCKETPAIR => {
                let mut fds = [0u8; 8];
                let n = host(SYS_SOCKETPAIR, a, b, c, fds.as_mut_ptr() as u64, 0, 0);
                if n != 0 {
                    n
                } else {
                    let h0 = i32::from_le_bytes(fds[0..4].try_into().unwrap());
                    let h1 = i32::from_le_bytes(fds[4..8].try_into().unwrap());
                    let g0 = fd_install(pid, h0, 0) as i32;
                    let g1 = fd_install(pid, h1, 0) as i32;
                    let mut out = [0u8; 8];
                    out[0..4].copy_from_slice(&g0.to_le_bytes());
                    out[4..8].copy_from_slice(&g1.to_le_bytes());
                    // Same OUT-buffer contract as pipe2: fail loudly (-EFAULT) + roll
                    // back rather than masking a write-back failure as host success.
                    if vm_write(pid, d, &out) < 0 {
                        let _ = fd_remove(pid, g0);
                        let _ = fd_remove(pid, g1);
                        host(SYS_CLOSE, h0 as u64, 0, 0, 0, 0, 0);
                        host(SYS_CLOSE, h1 as u64, 0, 0, 0, 0, 0);
                        -14 // -EFAULT
                    } else {
                        n // 0
                    }
                }
            }
            SYS_TIME => {
                let mut t = [0u8; 8];
                let r = host(SYS_TIME, t.as_mut_ptr() as u64, 0, 0, 0, 0, 0);
                if a != 0 {
                    vm_write(pid, a, &t);
                }
                r
            }
            SYS_CLOCK_GETRES => {
                let mut ts = [0u8; 16];
                let n = host(SYS_CLOCK_GETRES, a, ts.as_mut_ptr() as u64, 0, 0, 0, 0);
                if n == 0 && b != 0 {
                    vm_write(pid, b, &ts);
                }
                n
            }
            // Scheduler metadata. Chromium's base::PlatformThread reads the current
            // thread policy/priority on startup and treats pthread_getschedparam
            // failure as fatal. The cell has no permission to mutate host scheduler
            // state, but the VM-equivalent guest-visible state for normal workloads is
            // SCHED_OTHER with priority 0. Model that directly for read paths and allow
            // no-op writes of the same state; deny privileged realtime changes.
            SYS_SCHED_GETPARAM => {
                if b == 0 {
                    -14 // -EFAULT
                } else {
                    vm_write(pid, b, &0i32.to_le_bytes());
                    0
                }
            }
            SYS_SCHED_GETSCHEDULER => libc::SCHED_OTHER as i64,
            SYS_SCHED_GET_PRIORITY_MAX | SYS_SCHED_GET_PRIORITY_MIN => match a as i32 {
                x if x == libc::SCHED_OTHER => 0,
                x if x == libc::SCHED_FIFO || x == libc::SCHED_RR => {
                    if nr == SYS_SCHED_GET_PRIORITY_MAX {
                        99
                    } else {
                        1
                    }
                }
                #[cfg(target_os = "linux")]
                x if x == libc::SCHED_BATCH || x == libc::SCHED_IDLE => 0,
                _ => -22, // -EINVAL
            },
            SYS_SCHED_RR_GET_INTERVAL => {
                if b == 0 {
                    -14
                } else {
                    // timespec { tv_sec: 0, tv_nsec: 0 }. For SCHED_OTHER this value
                    // is informational; callers only require a successful read.
                    vm_write(pid, b, &[0u8; 16]);
                    0
                }
            }
            SYS_SCHED_SETPARAM => {
                if b == 0 {
                    -14
                } else {
                    let mut param = [0u8; 4];
                    vm_read(pid, b, &mut param);
                    if i32::from_le_bytes(param) == 0 {
                        0
                    } else {
                        -1 // -EPERM: realtime priority changes are not granted.
                    }
                }
            }
            SYS_SCHED_SETSCHEDULER => {
                if c == 0 {
                    -14
                } else {
                    let mut param = [0u8; 4];
                    vm_read(pid, c, &mut param);
                    if b as i32 == libc::SCHED_OTHER && i32::from_le_bytes(param) == 0 {
                        0
                    } else {
                        -1 // -EPERM
                    }
                }
            }
            SYS_GETPRIORITY => match a as i32 {
                0 => {
                    let target = if b == 0 { pid } else { b as i32 };
                    let nice = nice_values()
                        .lock()
                        .unwrap()
                        .get(&target)
                        .copied()
                        .unwrap_or(0);
                    (20 - nice) as i64
                }
                1 | 2 => 20, // PRIO_PGRP / PRIO_USER: default nice 0.
                _ => -22,    // -EINVAL
            },
            SYS_SETPRIORITY => match a as i32 {
                0 => {
                    let nice = c as i32;
                    if !(-20..=19).contains(&nice) {
                        -22 // -EINVAL
                    } else {
                        let target = if b == 0 { pid } else { b as i32 };
                        nice_values().lock().unwrap().insert(target, nice);
                        0
                    }
                }
                1 | 2 => {
                    let nice = c as i32;
                    if !(-20..=19).contains(&nice) {
                        -22
                    } else {
                        // Group/user priority changes are accepted as guest-visible
                        // metadata, but sentry does not mutate supervisor scheduling.
                        0
                    }
                }
                _ => -22,
            },
            SYS_SCHED_SETAFFINITY => {
                // The guest-visible CPU set is already virtualized by
                // sched_getaffinity below. Accept self-affinity writes as a no-op so
                // runtimes that re-apply the current mask do not fail under sentry.
                0
            }
            SYS_SCHED_GETAFFINITY => {
                let sz = (b.min(1024)) as usize;
                let mut m = vec![0u8; sz];
                let n = host(SYS_SCHED_GETAFFINITY, a, b, m.as_mut_ptr() as u64, 0, 0, 0);
                if n > 0 {
                    let used = n as usize;
                    // VM-vCPU parity: a cgroup CPU cap means the guest "has" that
                    // many vCPUs, so report a mask of exactly `count` online CPUs
                    // (the lowest `count`). `nproc`, libuv, OpenMP, Go's
                    // `runtime.NumCPU`, etc. count sched_getaffinity bits (NOT
                    // /proc/cpuinfo), so without this the guest sees the HOST's CPU
                    // count instead of its configured vCPU count — diverging from the
                    // KVM/HVF guest, which sees exactly its vCPU set. Uncapped
                    // (`cgroup_cpu_count() == None`) passes the host mask through.
                    if let Some(count) = cgroup_cpu_count() {
                        for (i, byte) in m[..used].iter_mut().enumerate() {
                            let mut v = 0u8;
                            for bit in 0..8u32 {
                                if ((i * 8 + bit as usize) as u64) < count {
                                    v |= 1 << bit;
                                }
                            }
                            *byte = v;
                        }
                    }
                    vm_write(pid, c, &m[..used]);
                }
                n
            }
            SYS_GETCPU => {
                let mut cpu = [0u8; 4];
                let mut node = [0u8; 4];
                let cp = if a != 0 { cpu.as_mut_ptr() as u64 } else { 0 };
                let np = if b != 0 { node.as_mut_ptr() as u64 } else { 0 };
                let n = host(SYS_GETCPU, cp, np, 0, 0, 0, 0);
                if a != 0 {
                    vm_write(pid, a, &cpu);
                }
                if b != 0 {
                    vm_write(pid, b, &node);
                }
                n
            }
            // nanosleep(req*, rem*) / clock_nanosleep(clk, flags, req*, rem*).
            // A long sleep (e.g. a `sleep 100` straggler) is effectively indefinite
            // for reclamation, so cancel-aware (Part C). A normal-EINTR (the guest's
            // own signal) still returns -EINTR to the cell, which re-issues with the
            // remaining time as today — host_cancellable retries ONLY past our SIGURG
            // when cancel is unset, which never happens for a live cell.
            SYS_NANOSLEEP => {
                let mut req = [0u8; 16];
                vm_read(pid, a, &mut req);
                let mut rem = [0u8; 16];
                let rp = if b != 0 { rem.as_mut_ptr() as u64 } else { 0 };
                let n = host_cancellable(SYS_NANOSLEEP, req.as_ptr() as u64, rp, 0, 0, 0, 0);
                if n == CANCEL_SENTINEL {
                    return CANCEL_SENTINEL;
                }
                if b != 0 {
                    vm_write(pid, b, &rem);
                }
                n
            }
            SYS_CLOCK_NANOSLEEP => {
                let mut req = [0u8; 16];
                vm_read(pid, c, &mut req);
                let mut rem = [0u8; 16];
                let rp = if d != 0 { rem.as_mut_ptr() as u64 } else { 0 };
                let n = host_cancellable(SYS_CLOCK_NANOSLEEP, a, b, req.as_ptr() as u64, rp, 0, 0);
                if n == CANCEL_SENTINEL {
                    return CANCEL_SENTINEL;
                }
                if d != 0 {
                    vm_write(pid, d, &rem);
                }
                n
            }
            SYS_POLL => {
                // poll(fds*, nfds, timeout) — fds is IN/OUT (8B each). pollfd.fd
                // is a GUEST fd: translate in, restore on the way out (negative =
                // "ignore" passes through; unmapped → an absurd host fd ⇒ POLLNVAL).
                let n = (b.min(1024)) as usize;
                let mut fds = vec![0u8; n * 8];
                vm_read(pid, a, &mut fds);
                // #34: an owned loop fd in the set ⇒ the merge path (host fds via the
                // kernel, loop fds via LoopNet readiness). `c` is the int ms timeout.
                if (0..n).any(|i| {
                    let g = i32::from_le_bytes(fds[i * 8..i * 8 + 4].try_into().unwrap());
                    g >= 0 && fd_loop(pid, g).is_some()
                }) {
                    return poll_merge(pid, a, b, (c as i32) as i64);
                }
                let mut gf = vec![0i32; n];
                for i in 0..n {
                    let g = i32::from_le_bytes(fds[i * 8..i * 8 + 4].try_into().unwrap());
                    gf[i] = g;
                    let h = if g < 0 {
                        g
                    } else {
                        fd_host(pid, g).unwrap_or(0x3FFF_FFFF)
                    };
                    fds[i * 8..i * 8 + 4].copy_from_slice(&h.to_le_bytes());
                }
                // A poll with timeout < 0 blocks indefinitely — cancel-aware (Part C).
                let r = host_cancellable(SYS_POLL, fds.as_mut_ptr() as u64, b, c, 0, 0, 0);
                if r == CANCEL_SENTINEL {
                    return CANCEL_SENTINEL;
                }
                for i in 0..n {
                    fds[i * 8..i * 8 + 4].copy_from_slice(&gf[i].to_le_bytes());
                }
                vm_write(pid, a, &fds);
                r
            }
            // ppoll(fds*, nfds, tmo_ts* IN|0, sigmask* IN|0, sigsetsize). glibc's
            // poll() lowers to ppoll with NO fallback, so this MUST exist or every
            // libuv/glibc poll returns ENOSYS. The pollfd in/out guest-fd
            // translation is identical to SYS_POLL; the only differences are the
            // timeout (a const timespec*, NULL = infinite) and the sigmask (which
            // we IGNORE — the cell never blocks signals on the supervisor's behalf).
            // We convert to a host poll(2) ms timeout rather than serving a host
            // ppoll, so no sigmask handling is needed. The kernel ppoll does NOT
            // write the remaining time back, so the timespec is read-only here.
            SYS_PPOLL => {
                let n = (b.min(1024)) as usize;
                let mut fds = vec![0u8; n * 8];
                vm_read(pid, a, &mut fds);
                // #34: owned loop fd in the set ⇒ merge path. Decode the timespec
                // (NULL = block forever) to a poll(2) ms timeout, then delegate.
                if (0..n).any(|i| {
                    let g = i32::from_le_bytes(fds[i * 8..i * 8 + 4].try_into().unwrap());
                    g >= 0 && fd_loop(pid, g).is_some()
                }) {
                    let tmo_ms: i64 = if c == 0 {
                        -1
                    } else {
                        let mut ts = [0u8; 16];
                        vm_read(pid, c, &mut ts);
                        let secs = i64::from_le_bytes(ts[0..8].try_into().unwrap());
                        let nsecs = i64::from_le_bytes(ts[8..16].try_into().unwrap());
                        let ms = secs.saturating_mul(1000).saturating_add(nsecs / 1_000_000);
                        let ms = if ms == 0 && (secs > 0 || nsecs > 0) {
                            1
                        } else {
                            ms
                        };
                        ms.clamp(0, i32::MAX as i64)
                    };
                    return poll_merge(pid, a, b, tmo_ms);
                }
                let mut gf = vec![0i32; n];
                for i in 0..n {
                    let g = i32::from_le_bytes(fds[i * 8..i * 8 + 4].try_into().unwrap());
                    gf[i] = g;
                    let h = if g < 0 {
                        g
                    } else {
                        fd_host(pid, g).unwrap_or(0x3FFF_FFFF)
                    };
                    fds[i * 8..i * 8 + 4].copy_from_slice(&h.to_le_bytes());
                }
                // NULL timespec = block indefinitely → poll timeout -1. Otherwise
                // read the supervisor-local timespec and convert to ms (saturating;
                // a zero timespec = "return immediately" → 0ms).
                let tmo_ms: u64 = if c == 0 {
                    (-1i64) as u64
                } else {
                    let mut ts = [0u8; 16];
                    vm_read(pid, c, &mut ts);
                    let secs = i64::from_le_bytes(ts[0..8].try_into().unwrap());
                    let nsecs = i64::from_le_bytes(ts[8..16].try_into().unwrap());
                    let ms = secs.saturating_mul(1000).saturating_add(nsecs / 1_000_000);
                    // round a sub-ms but non-zero wait up to 1ms so we don't busy-spin.
                    let ms = if ms == 0 && (secs > 0 || nsecs > 0) {
                        1
                    } else {
                        ms
                    };
                    // clamp into poll(2)'s `int` range so a huge wait isn't truncated
                    // to a negative (== infinite) timeout.
                    ms.clamp(0, i32::MAX as i64) as u64
                };
                // tmo_ms == -1 (NULL timespec) blocks indefinitely — cancel-aware.
                let r = host_cancellable(SYS_POLL, fds.as_mut_ptr() as u64, b, tmo_ms, 0, 0, 0);
                if r == CANCEL_SENTINEL {
                    return CANCEL_SENTINEL;
                }
                for i in 0..n {
                    fds[i * 8..i * 8 + 4].copy_from_slice(&gf[i].to_le_bytes());
                }
                vm_write(pid, a, &fds);
                r
            }
            // select(nfds, readfds*, writefds*, exceptfds*, timeout*) and
            // pselect6(nfds, readfds*, writefds*, exceptfds*, timeout*, sigmask*).
            // The fd_set* args are bitmaps over GUEST fd numbers; the simplest
            // correct mediation is to fold the three sets into a pollfd array
            // (remapping guest→host via fd_host), call host poll, then fold
            // readiness back into the OUT bitmaps. timeout is a timeval (select)
            // or timespec (pselect6); both decode to a ms poll timeout. sigmask is
            // IGNORED (see ppoll). LIMITATION: exceptfds maps to POLLPRI/POLLERR
            // only (no out-of-band TCP urgent-data semantics) and the kernel-side
            // timeout-remaining writeback that legacy select(2) does on Linux is
            // not reproduced — both are unused by the libc poll/connect paths.
            SYS_SELECT | SYS_PSELECT6 => {
                let nfds = (a as i32).clamp(0, 1024) as usize;
                let words = nfds.div_ceil(8); // bytes per fd_set actually in use
                let read_set = |ptr: u64| -> Vec<u8> {
                    if ptr == 0 || words == 0 {
                        return Vec::new();
                    }
                    let mut s = vec![0u8; words];
                    vm_read(pid, ptr, &mut s);
                    s
                };
                let rd = read_set(b);
                let wr = read_set(c);
                let ex = read_set(d);
                let is_set = |s: &[u8], fd: usize| -> bool {
                    !s.is_empty() && (s[fd / 8] >> (fd % 8)) & 1 == 1
                };
                // Build one guest (fd, events) entry per fd present in any set (events
                // folded): POLLIN for readfds, POLLOUT for writefds, POLLPRI for
                // exceptfds. poll_merge_entries (below) maps each entry to host- or
                // loopback-socket readiness — so loop fds are NOT mis-polled as host fds.
                let (pollin, pollout, pollpri) = (0x001u16, 0x004u16, 0x002u16);
                let (pollerr, pollhup, pollnval) = (0x008u16, 0x010u16, 0x020u16);
                let mut gfd_ev: Vec<(i32, u16)> = Vec::new();
                for fd in 0..nfds {
                    let mut ev: u16 = 0;
                    if is_set(&rd, fd) {
                        ev |= pollin;
                    }
                    if is_set(&wr, fd) {
                        ev |= pollout;
                    }
                    if is_set(&ex, fd) {
                        ev |= pollpri;
                    }
                    if ev == 0 {
                        continue;
                    }
                    gfd_ev.push((fd as i32, ev));
                }
                // timeout: NULL = infinite (-1). select uses timeval (sec+usec),
                // pselect6 uses timespec (sec+nsec); both are 16 bytes, two i64s.
                let tmo_ms: u64 = if e == 0 {
                    (-1i64) as u64
                } else {
                    let mut t = [0u8; 16];
                    vm_read(pid, e, &mut t);
                    let secs = i64::from_le_bytes(t[0..8].try_into().unwrap());
                    let sub = i64::from_le_bytes(t[8..16].try_into().unwrap());
                    let sub_ms = if nr == SYS_PSELECT6 {
                        sub / 1_000_000
                    } else {
                        sub / 1_000
                    };
                    let ms = secs.saturating_mul(1000).saturating_add(sub_ms);
                    let ms = if ms == 0 && (secs > 0 || sub > 0) {
                        1
                    } else {
                        ms
                    };
                    ms.clamp(0, i32::MAX as i64) as u64
                };
                // LOOP-AWARE poll: select/pselect6 fd_sets may include owned-loopback
                // sockets (e.g. Postgres' postmaster select()s its listen sockets, the
                // node backend its accept fd). poll_merge_entries derives a loop fd's
                // readiness from LoopNet (net.readable / accept backlog) instead of the
                // old fd_host→POLLNVAL path that reported a loop listener "ready" every
                // call → blocking accept() → hang. tmo_ms (-1 = infinite) → i64.
                let (r, rev) = poll_merge_entries(pid, &gfd_ev, tmo_ms as i64);
                if r == CANCEL_SENTINEL {
                    return CANCEL_SENTINEL;
                }
                // Fold readiness back into freshly-zeroed OUT bitmaps. select returns
                // the count of ready fds across all three sets (each readiness bit
                // counts once per set it satisfies — matching the kernel).
                let mut out_rd = vec![0u8; words];
                let mut out_wr = vec![0u8; words];
                let mut out_ex = vec![0u8; words];
                let mut count: i64 = 0;
                if r >= 0 {
                    for (i, (fd, _ev)) in gfd_ev.iter().enumerate() {
                        let revents = rev[i];
                        let fd = *fd as usize;
                        // POLLERR/POLLHUP/POLLNVAL surface as readable+writable ready
                        // for whichever set requested the fd (glibc/poll convention).
                        let err = revents & (pollerr | pollhup | pollnval) != 0;
                        if is_set(&rd, fd) && (revents & (pollin | pollhup) != 0 || err) {
                            out_rd[fd / 8] |= 1 << (fd % 8);
                            count += 1;
                        }
                        if is_set(&wr, fd) && (revents & pollout != 0 || err) {
                            out_wr[fd / 8] |= 1 << (fd % 8);
                            count += 1;
                        }
                        if is_set(&ex, fd) && (revents & pollpri != 0) {
                            out_ex[fd / 8] |= 1 << (fd % 8);
                            count += 1;
                        }
                    }
                }
                if b != 0 && words > 0 {
                    vm_write(pid, b, &out_rd);
                }
                if c != 0 && words > 0 {
                    vm_write(pid, c, &out_wr);
                }
                if d != 0 && words > 0 {
                    vm_write(pid, d, &out_ex);
                }
                if r < 0 {
                    r
                } else {
                    count
                }
            }
            SYS_SIGALTSTACK => {
                // The guest may query/set its own alt-stack. The real kernel alt-stack
                // remains sentry-owned for SIGSYS, so mirror only the guest-visible ABI
                // state per ring slot/thread.
                let old = guest_sigaltstack(slot);
                if b != 0 {
                    if vm_write(pid, b, &old) != old.len() as i64 {
                        return -14; // -EFAULT
                    }
                }
                if a != 0 {
                    let mut next = [0u8; 24];
                    if vm_read(pid, a, &mut next) != next.len() as i64 {
                        return -14; // -EFAULT
                    }
                    if (slot as usize) < MAX_SLOTS {
                        unsafe {
                            GUEST_SIGALTSTACKS[slot as usize] = next;
                        }
                    }
                }
                0
            }

            // ---- networking: mediated to host sockets by the supervisor ----
            // The cell never gets raw net (socket/* aren't in the seccomp allowlist
            // — a hostile direct socket() is killed by the wall). The supervisor is
            // the single egress-policy control point (TSI's role): production applies
            // an allowlist HERE; the spike mediates all (the demo is loopback).
            SYS_LISTEN | SYS_SHUTDOWN => host(nr, a, b, c, d, e, f),
            SYS_SOCKET => {
                // v6-less host: refuse AF_INET6 (Linux family 10) up front. The
                // guest's libc treats EAFNOSUPPORT as "this address family is
                // unavailable" and falls back to A/v4 — exactly what we want when
                // the host can't route v6. Without this, a dual-stack client opens
                // a v6 socket, gets AAAA records (or a v4-mapped dest), and hangs
                // on a connect the host can never complete. (`a` is the guest's
                // Linux AF constant, passed verbatim — AF_INET6 == 10.)
                if a == 10 && !host_v6_route() {
                    return -97; // -EAFNOSUPPORT
                }
                let n = host(SYS_SOCKET, a, b, c, 0, 0, 0);
                if n < 0 {
                    n
                } else {
                    fd_install(pid, n as i32, 0)
                }
            }
            SYS_BIND => {
                // `a` is the GUEST fd (BIND is no longer translated in the prelude —
                // a loopback bind must switch the fd to the owned plane).
                let len = (c.min(128)) as usize;
                let mut addr = vec![0u8; len];
                vm_read(pid, b, &mut addr);
                // Loopback STREAM bind → owned netstack, but ONLY for a warm pool
                // (own_loopback): the NEV warm-restore primitive needs the listener in
                // serializable supervisor state. Normal sandboxes fall through to the
                // host socket so loopback is the real kernel `lo` of the cell's netns
                // (full mac/linux parity). UDP loopback always uses the host `lo`.
                if let Some(ep) = ep_from_sockaddr(&addr) {
                    // A WILDCARD (0.0.0.0 / ::) bind on a NON-published port is
                    // loopback-reachable in a sandbox (no external ifaces), so route it
                    // to the owned LoopNet too — a server that binds INADDR_ANY (nginx
                    // `0.0.0.0:80`) is then reachable via a guest-local 127.0.0.1
                    // connect (the LoopNet connect does the wildcard match). Published
                    // ports stay host-plane + re-homed for external reachability.
                    let wildcard_local =
                        ep.ip == [0u8; 16] && !published_ports().lock().unwrap().contains(&ep.port);
                    if own_loopback()
                        && (ep_is_loopback(&ep) || wildcard_local)
                        && fd_host(pid, a as i32)
                            .map(host_sock_is_stream)
                            .unwrap_or(false)
                    {
                        let sid = loop_socket_for(pid, a as i32);
                        return loop_state()
                            .lock()
                            .unwrap()
                            .net
                            .bind(sid, ep)
                            .map(|_| 0)
                            .unwrap_or_else(|e| -(e as i64));
                    }
                }
                // Host plane: translate the guest fd → host fd, then the existing logic.
                let h = match fd_host(pid, a as i32) {
                    Some(h) => h,
                    None => return -9,
                };
                // Published-port INGRESS: in a netns sandbox, a bind to a published
                // port is re-homed into the host netns so the host can reach the
                // listener. Non-published binds stay isolated in the sandbox netns.
                if let Some(sa) = parse_connect_addr(&addr) {
                    if published_ports().lock().unwrap().contains(&sa.port()) {
                        rehome_to_host_netns(h);
                    }
                }
                // AF_UNIX pathname socket: bind at the rootfs-confined HOST path
                // (the supervisor isn't chroot'd, so the raw guest path would miss
                // the node the cell created in the rootfs → ENOENT). Chromium's
                // ProcessSingleton binds /tmp/.../SingletonSocket exactly this way.
                if let Some(rw) = rewrite_unix_sun_path(&addr) {
                    let orig = unix_sockaddr_host_path(&addr);
                    let rewritten = unix_sockaddr_path(&rw).map(|p| p.to_vec());
                    let r = host(
                        SYS_BIND,
                        h as u64,
                        rw.as_ptr() as u64,
                        rw.len() as u64,
                        0,
                        0,
                        0,
                    );
                    if r == 0 {
                        if let (Some(orig), Some(rewritten)) = (orig.as_deref(), rewritten.as_deref()) {
                            if orig != rewritten {
                                publish_unix_socket_alias_placeholder(orig);
                                unix_alias_placeholder_record(h, orig);
                            }
                        }
                    }
                    return r;
                }
                host(
                    SYS_BIND,
                    h as u64,
                    addr.as_ptr() as u64,
                    len as u64,
                    0,
                    0,
                    0,
                )
            }
            // connect is the single EGRESS control point (the supervisor is the
            // only thing that ever opens a real host socket — the cell has no raw
            // net). Enforce the per-sandbox egress policy on the destination IP
            // before connecting: a denied target gets -EACCES, never a host
            // socket. Non-IP families (AF_UNIX, …) fall through unfiltered.
            SYS_CONNECT => {
                // `a` is the GUEST fd (CONNECT is no longer translated in the prelude).
                let mut len = (c.min(128)) as usize;
                let mut addr = vec![0u8; len];
                vm_read(pid, b, &mut addr);
                // v4-mapped (::ffff:a.b.c.d) AF_INET6 dest → native AF_INET, so
                // v4-mapped egress works on a v4-only host (mirrors the muxer
                // connect path). Done BEFORE the egress check + the host syscall.
                if let Some(nl) = unwrap_v4mapped_sockaddr(&mut addr) {
                    len = nl;
                }
                // Loopback STREAM connect → owned netstack, but ONLY for a warm pool
                // (own_loopback): owned + serializable so a live connection survives
                // warm-restore. Normal sandboxes fall through to the host socket, so a
                // loopback connect uses the real kernel `lo` of the cell's netns (full
                // parity: reaches its own netns listeners, or — under without_netns —
                // the host's loopback services). UDP loopback always uses the host `lo`.
                if let Some(ep) = ep_from_sockaddr(&addr) {
                    if own_loopback()
                        && ep_is_loopback(&ep)
                        && fd_host(pid, a as i32)
                            .map(host_sock_is_stream)
                            .unwrap_or(false)
                    {
                        let sid = loop_socket_for(pid, a as i32);
                        let ls = loop_state().lock().unwrap();
                        let mut ls = ls;
                        return match ls.net.connect(sid, ep) {
                            Ok(listener) => {
                                loop_wake(&ls, listener);
                                0
                            }
                            Err(e) => -(e as i64),
                        };
                    }
                }
                // Host plane (egress): translate the guest fd → host fd, existing policy.
                let h = match fd_host(pid, a as i32) {
                    Some(h) => h,
                    None => return -9,
                };
                if let Some(sa) = parse_connect_addr(&addr) {
                    if let Err(reason) = crate::vmm::egress_policy::check_addr(sa) {
                        if TRACE {
                            log(b"[supervisor] egress DENY: ");
                            log(reason.as_bytes());
                            log(b"\n");
                        }
                        return -13; // -EACCES
                    }
                    // In a netns sandbox, a non-loopback destination is EGRESS: the
                    // sandbox netns has no route, so re-home the socket into the
                    // host netns (loopback stays isolated in-netns). No-op outside
                    // a netns sandbox.
                    if !sa.ip().is_loopback() {
                        rehome_to_host_netns(h);
                    }
                    // Latch port-53 (DNS) flows on a v6-less host so the recv path
                    // can strip AAAA answers.
                    note_dns_dest(h, &sa);
                }
                // AF_UNIX pathname socket: connect to the rootfs-confined HOST path
                // (mirrors SYS_BIND — the supervisor isn't chroot'd into the rootfs).
                if let Some(rw) = rewrite_unix_sun_path(&addr) {
                    return host(
                        SYS_CONNECT,
                        h as u64,
                        rw.as_ptr() as u64,
                        rw.len() as u64,
                        0,
                        0,
                        0,
                    );
                }
                let r = host(
                    SYS_CONNECT,
                    h as u64,
                    addr.as_ptr() as u64,
                    len as u64,
                    0,
                    0,
                    0,
                );
                if unsafe { IPCTRACE } {
                    log_connect_debug(pid, h, &addr[..len], r);
                }
                r
            }
            // sendto(fd, buf IN, len, flags, dest_addr IN|0, addrlen) — the UDP
            // datagram send path (DNS, etc.). Like connect, it's an egress control
            // point: enforce the policy on the (non-loopback) destination and, in a
            // netns sandbox, re-home the socket into the host netns before sending.
            SYS_SENDTO => {
                let len = (c as usize).min(1 << 20);
                let mut buf = vec![0u8; len];
                vm_read(pid, b, &mut buf);
                // dest addr (absent — 0 — on a connected socket).
                let mut addr: Vec<u8> = Vec::new();
                let mut addr_len = 0usize;
                if e != 0 && f != 0 {
                    addr.resize((f as usize).min(128), 0);
                    addr_len = addr.len();
                    vm_read(pid, e, &mut addr);
                    // v4-mapped AF_INET6 dest → native AF_INET (see SYS_CONNECT).
                    if let Some(nl) = unwrap_v4mapped_sockaddr(&mut addr) {
                        addr_len = nl;
                    }
                    if let Some(sa) = parse_connect_addr(&addr) {
                        if let Err(reason) = crate::vmm::egress_policy::check_addr(sa) {
                            if TRACE {
                                log(b"[supervisor] egress DENY (sendto): ");
                                log(reason.as_bytes());
                                log(b"\n");
                            }
                            return -13; // -EACCES
                        }
                        if !sa.ip().is_loopback() {
                            rehome_to_host_netns(a as i32);
                        }
                        // Latch port-53 (DNS) flows on a v6-less host (unconnected
                        // UDP sendto carries the dest per-datagram). `a` = host fd.
                        note_dns_dest(a as i32, &sa);
                    }
                }
                let (aptr, alen) = if addr.is_empty() {
                    (e, f)
                } else {
                    (addr.as_ptr() as u64, addr_len as u64)
                };
                let r = host(
                    SYS_SENDTO,
                    a,
                    buf.as_ptr() as u64,
                    len as u64,
                    d,
                    aptr,
                    alen,
                );
                if unsafe { IPCTRACE } && addr.is_empty() {
                    ipc_logf(
                        &[
                            (b"SENDTO pid=", pid as i64),
                            (b"hfd=", a as i64),
                            (b"len=", len as i64),
                            (b"ret=", r),
                        ],
                        &buf[..len.min(72)],
                    );
                }
                r
            }
            // recvfrom(fd, buf OUT, len, flags, src_addr OUT|0, addrlen IN/OUT|0).
            // A blocking recv on a peer that never sends/closes is indefinite —
            // cancel-aware (Part C).
            SYS_RECVFROM => {
                let len = (c as usize).min(1 << 20);
                let mut buf = vec![0u8; len];
                if e != 0 && f != 0 {
                    let mut lenbuf = [0u8; 4];
                    vm_read(pid, f, &mut lenbuf);
                    let cap = (u32::from_le_bytes(lenbuf) as usize).min(128);
                    let mut addr = vec![0u8; cap];
                    let mut outlen = cap as u32;
                    let n = host_cancellable(
                        SYS_RECVFROM,
                        a,
                        buf.as_mut_ptr() as u64,
                        len as u64,
                        d,
                        addr.as_mut_ptr() as u64,
                        &mut outlen as *mut u32 as u64,
                    );
                    if n == CANCEL_SENTINEL {
                        return CANCEL_SENTINEL;
                    }
                    if n >= 0 {
                        // v6-less host: rewrite AAAA answers to NODATA on DNS flows
                        // so non-happy-eyeballs clients fall through to A/v4 instead
                        // of hanging on an unreachable v6 address. No-op otherwise.
                        let n = maybe_strip_aaaa(a as i32, &mut buf, n);
                        vm_write(pid, b, &buf[..n as usize]);
                        let w = (outlen as usize).min(cap);
                        vm_write(pid, e, &addr[..w]);
                        vm_write(pid, f, &outlen.to_le_bytes());
                        return n;
                    }
                    n
                } else {
                    let n = host_cancellable(
                        SYS_RECVFROM,
                        a,
                        buf.as_mut_ptr() as u64,
                        len as u64,
                        d,
                        e,
                        f,
                    );
                    if n == CANCEL_SENTINEL {
                        return CANCEL_SENTINEL;
                    }
                    if n >= 0 {
                        let n = maybe_strip_aaaa(a as i32, &mut buf, n);
                        vm_write(pid, b, &buf[..n as usize]);
                        return n;
                    }
                    n
                }
            }
            // sendmsg(fd, msghdr* IN, flags): gather the guest's iovec segments and
            // (if present) its msg_name destination, enforce the egress policy +
            // netns re-home on a non-loopback dest, then send as one local msghdr.
            // Ancillary data (msg_control) is dropped — fine for datagram egress/DNS.
            SYS_SENDMSG => {
                let mut mh = [0u8; 56];
                vm_read(pid, b, &mut mh);
                let rd = |o: usize| u64::from_le_bytes(mh[o..o + 8].try_into().unwrap());
                let name_ptr = rd(0);
                let namelen = u32::from_le_bytes(mh[8..12].try_into().unwrap());
                let (iov_ptr, iovlen) = (rd(16), (rd(24) as usize).min(1024));
                let mut iovs = vec![0u8; iovlen * 16];
                vm_read(pid, iov_ptr, &mut iovs);
                let mut data = Vec::new();
                for i in 0..iovlen {
                    let base = u64::from_le_bytes(iovs[i * 16..i * 16 + 8].try_into().unwrap());
                    let l = (u64::from_le_bytes(iovs[i * 16 + 8..i * 16 + 16].try_into().unwrap())
                        .min(CAP)) as usize;
                    let mut seg = vec![0u8; l];
                    if l > 0 {
                        vm_read(pid, base, &mut seg);
                    }
                    data.extend_from_slice(&seg);
                }
                let mut addr = Vec::new();
                let mut addr_len = 0usize;
                if name_ptr != 0 && namelen > 0 {
                    addr.resize((namelen as usize).min(128), 0);
                    addr_len = addr.len();
                    vm_read(pid, name_ptr, &mut addr);
                    // v4-mapped AF_INET6 dest → native AF_INET (see SYS_CONNECT).
                    if let Some(nl) = unwrap_v4mapped_sockaddr(&mut addr) {
                        addr_len = nl;
                    }
                    if let Some(sa) = parse_connect_addr(&addr) {
                        if let Err(reason) = crate::vmm::egress_policy::check_addr(sa) {
                            if TRACE {
                                log(b"[supervisor] egress DENY (sendmsg): ");
                                log(reason.as_bytes());
                                log(b"\n");
                            }
                            return -13;
                        }
                        if !sa.ip().is_loopback() {
                            rehome_to_host_netns(a as i32);
                        }
                        // Latch port-53 (DNS) flows on a v6-less host. `a` = host fd.
                        note_dns_dest(a as i32, &sa);
                    }
                }
                // SCM_RIGHTS ancillary: translate the guest's passed fd numbers to
                // host fds (Chromium's Mojo channel passes its children the IPC +
                // shared-memory handles this way). Held alive across the host call.
                let ctl_ptr = rd(32); // msg_control
                let ctl_len = rd(40) as usize; // msg_controllen
                let mut sendctl: Vec<u8> = Vec::new();
                let mut sendfds = 0usize; // fds carried in this sendmsg (for conservation)
                if ctl_ptr != 0 && ctl_len >= 16 {
                    let mut gctl = vec![0u8; ctl_len.min(SCM_CTL_CAP)];
                    vm_read(pid, ctl_ptr, &mut gctl);
                    match scm_send_control(pid, &gctl) {
                        Ok(c) => {
                            sendfds = scm_count_fds(&c);
                            sendctl = c;
                        }
                        Err(e) => return e,
                    }
                }
                if getsockopt_int(a as i32, 39) == libc::AF_UNIX {
                    append_scm_credentials(&mut sendctl, pid);
                }
                if unsafe { FDTRACE }
                    && (data
                        .windows(b"pseudonymization-salt-handle".len())
                        .any(|w| w == b"pseudonymization-salt-handle")
                        || data
                            .windows(b"--type=renderer".len())
                            .any(|w| w == b"--type=renderer")
                        || data
                            .windows(b"--type=gpu-process".len())
                            .any(|w| w == b"--type=gpu-process"))
                {
                    ipc_logf(
                        &[
                            (b"FDTRACE_ZYGMSG pid=", pid as i64),
                            (b"hfd=", a as i64),
                            (b"datalen=", data.len() as i64),
                            (b"scmfds=", sendfds as i64),
                        ],
                        &data[..data.len().min(512)],
                    );
                }
                let iov = LocalIov {
                    base: data.as_ptr() as u64,
                    len: data.len() as u64,
                };
                let lmh = LocalMsghdr {
                    name: if addr.is_empty() {
                        0
                    } else {
                        addr.as_ptr() as u64
                    },
                    namelen: addr_len as u32,
                    _p: 0,
                    iov: &iov as *const _ as u64,
                    iovlen: 1,
                    control: if sendctl.is_empty() {
                        0
                    } else {
                        sendctl.as_ptr() as u64
                    },
                    controllen: sendctl.len() as u64,
                    flags: 0,
                    _p2: 0,
                };
                let r = host(SYS_SENDMSG, a, &lmh as *const _ as u64, c, 0, 0, 0);
                // SOCK_STREAM SCM_RIGHTS atomicity: the kernel attaches the WHOLE
                // fd-list to the first byte of this message's byte range, even if the
                // socket buffer only accepted a PREFIX of `data` (a short send).
                // Chromium relies on this — after a short send it marks the handles as
                // fully sent and re-sends only the remaining bytes WITHOUT fds
                // (channel_posix.cc WriteNoLock). Because we forward the cell's data +
                // its ancillary as ONE host sendmsg, the real kernel gives us that
                // guarantee for free. Count the fds as sent only when the call did not
                // error (r >= 0; a short send r in [0,len) still attached every fd).
                if r >= 0 && sendfds > 0 {
                    SCM_FDS_SENT.fetch_add(sendfds as u64, Ordering::Relaxed);
                }
                if unsafe { IPCTRACE } {
                    ipc_logf(
                        &[
                            (b"SENDMSG pid=", pid as i64),
                            (b"hfd=", a as i64),
                            (b"datalen=", data.len() as i64),
                            (b"scmbytes=", sendctl.len() as i64),
                            (b"scmfds=", sendfds as i64),
                            (b"ret=", r),
                            (b"fds_sent_total=", SCM_FDS_SENT.load(Ordering::Relaxed) as i64),
                        ],
                        &data[..data.len().min(72)],
                    );
                    // SCM_RIGHTS REQUIRES ≥1 byte of ordinary data in the same
                    // sendmsg (unix(7)); a short send that accepted 0 bytes would
                    // strand the fds. Chromium always co-sends payload, so flag the
                    // anomaly rather than silently dropping it.
                    if sendfds > 0 && r == 0 {
                        ipc_logf_raw(b"SENDMSG SCM anomaly: fds attached but 0 data bytes accepted");
                    }
                }
                r
            }
            // recvmsg(fd, msghdr* IN/OUT, flags): receive into a bounce buffer, then
            // scatter to the guest's iovec segments and write back the source addr +
            // namelen + flags. Ancillary data is not delivered (controllen → 0).
            SYS_RECVMSG => {
                let mut mh = [0u8; 56];
                vm_read(pid, b, &mut mh);
                let rd = |o: usize| u64::from_le_bytes(mh[o..o + 8].try_into().unwrap());
                let name_ptr = rd(0);
                let namecap = u32::from_le_bytes(mh[8..12].try_into().unwrap());
                let (iov_ptr, iovlen) = (rd(16), (rd(24) as usize).min(1024));
                let mut iovs = vec![0u8; iovlen * 16];
                vm_read(pid, iov_ptr, &mut iovs);
                let mut segs: Vec<(u64, usize)> = Vec::new();
                let mut total = 0usize;
                for i in 0..iovlen {
                    let base = u64::from_le_bytes(iovs[i * 16..i * 16 + 8].try_into().unwrap());
                    let l = (u64::from_le_bytes(iovs[i * 16 + 8..i * 16 + 16].try_into().unwrap())
                        .min(CAP)) as usize;
                    segs.push((base, l));
                    total += l;
                }
                let mut bounce = vec![0u8; total];
                let mut lname = vec![0u8; (namecap as usize).min(128)];
                // SCM_RIGHTS ancillary: give the host a control buffer so passed fds
                // are delivered (Chromium's Mojo children receive the IPC + shm
                // handles here); translated to guest fds after the call.
                let ctl_ptr = rd(32); // msg_control
                let ctl_cap = rd(40) as usize; // msg_controllen
                // Give the HOST recvmsg a FULL-width control buffer (not the cell's
                // possibly-smaller cap). The kernel attaches an SCM_RIGHTS fd-list to
                // exactly one recvmsg (the one that reads the first byte of the
                // message's byte range — the SOCK_STREAM ancillary "barrier"); if the
                // buffer we hand the kernel is too small it sets MSG_CTRUNC and
                // permanently DROPS the overflow fds right here, before we can do
                // anything faithful with them. Sizing to SCM_CTL_CAP (room for ~2040
                // fds, vs Chromium's ≤128) means the host NEVER truncates; we then
                // re-impose the cell's real `ctl_cap` ourselves in scm_recv_control,
                // closing (not leaking) any fd that genuinely doesn't fit. We still
                // only allocate when the cell asked for ancillary at all.
                let mut hctl: Vec<u8> = if ctl_ptr != 0 && ctl_cap >= 16 {
                    vec![0u8; SCM_CTL_CAP]
                } else {
                    Vec::new()
                };
                let iov = LocalIov {
                    base: bounce.as_mut_ptr() as u64,
                    len: total as u64,
                };
                let mut lmh = LocalMsghdr {
                    name: if lname.is_empty() {
                        0
                    } else {
                        lname.as_mut_ptr() as u64
                    },
                    namelen: lname.len() as u32,
                    _p: 0,
                    iov: &iov as *const _ as u64,
                    iovlen: 1,
                    control: if hctl.is_empty() {
                        0
                    } else {
                        hctl.as_mut_ptr() as u64
                    },
                    controllen: hctl.len() as u64,
                    flags: 0,
                    _p2: 0,
                };
                // A blocking recvmsg on an idle peer is indefinite — cancel-aware.
                let n = host_cancellable(SYS_RECVMSG, a, &mut lmh as *mut _ as u64, c, 0, 0, 0);
                if n == CANCEL_SENTINEL {
                    return CANCEL_SENTINEL;
                }
                if n >= 0 {
                    // v6-less host: strip AAAA answers on a DNS flow (the datagram
                    // was gathered contiguously into `bounce`). Shrinks `n`; the
                    // resolver reads the returned length. No-op otherwise.
                    let n = maybe_strip_aaaa(a as i32, &mut bounce, n);
                    let got = n as usize;
                    let mut off = 0usize;
                    for (base, l) in segs {
                        if off >= got {
                            break;
                        }
                        let take = l.min(got - off);
                        vm_write(pid, base, &bounce[off..off + take]);
                        off += take;
                    }
                    if name_ptr != 0 && namecap > 0 {
                        let w = (lmh.namelen as usize).min(lname.len());
                        vm_write(pid, name_ptr, &lname[..w]);
                        vm_write(pid, b + 8, &lmh.namelen.to_le_bytes());
                    }
                    // Deliver SCM_RIGHTS ancillary: install received host fds into
                    // this pid's fd table, rewrite the cmsg with guest fd numbers.
                    // ORDERING: the rewritten ancillary and the scattered data bytes
                    // are written to the SAME guest msghdr from the SAME servicer call
                    // before we return, so the guest observes the fds and the bytes
                    // they were attached to as one atomic recvmsg — identical to the
                    // kernel's "fds ride the recvmsg that reads the message's first
                    // byte" contract. We must NOT advance the byte stream (consume the
                    // message) while silently dropping its fds; that is exactly what
                    // strands an ipcz AddBlockBuffer/ProvideMemory buffer on
                    // is_pending() forever.
                    let recvd = (lmh.controllen as usize).min(hctl.len());
                    let mut msg_flags = lmh.flags;
                    if ctl_ptr != 0 && recvd >= 16 {
                        let scm = scm_recv_control(pid, &hctl[..recvd], ctl_cap);
                        let w = scm.gctl.len().min(ctl_cap);
                        if w > 0 {
                            vm_write(pid, ctl_ptr, &scm.gctl[..w]);
                        }
                        vm_write(pid, b + 40, &(w as u64).to_le_bytes()); // msg_controllen
                        if scm.truncated {
                            // Kernel parity: too-small ancillary buffer ⇒ MSG_CTRUNC
                            // (0x8). The undeliverable host fds were already closed in
                            // scm_recv_control (no leak); the guest sees the partial
                            // list + the flag, exactly as on bare Linux.
                            msg_flags |= 0x8;
                            SCM_RECV_TRUNC.fetch_add(1, Ordering::Relaxed);
                        }
                        SCM_FDS_RECVD.fetch_add(scm.installed as u64, Ordering::Relaxed);
                    } else {
                        vm_write(pid, b + 40, &0u64.to_le_bytes()); // msg_controllen = 0
                    }
                    vm_write(pid, b + 48, &msg_flags.to_le_bytes()); // msg_flags
                    if unsafe { IPCTRACE } {
                        let hl = (n.max(0) as usize).min(bounce.len()).min(72);
                        ipc_logf(
                            &[
                                (b"RECVMSG pid=", pid as i64),
                                (b"hfd=", a as i64),
                                (b"got=", n),
                                (b"scmbytes=", lmh.controllen as i64),
                                (b"ctrunc=", ((msg_flags & 0x8) != 0) as i64),
                                (b"trunc_total=", SCM_RECV_TRUNC.load(Ordering::Relaxed) as i64),
                                (b"fds_recvd_total=", SCM_FDS_RECVD.load(Ordering::Relaxed) as i64),
                                (b"fds_sent_total=", SCM_FDS_SENT.load(Ordering::Relaxed) as i64),
                            ],
                            &bounce[..hl],
                        );
                        // Conservation assertion: a recvmsg that pulled ancillary bytes
                        // off the wire (controllen>0) but installed ZERO fds, or that
                        // had to truncate, means an SCM_RIGHTS fd was dropped end-to-end
                        // — the precise failure that parks an ipcz parcel forever. Log
                        // it loudly so a smoke run flags the regression at the source.
                        if recvd >= 16 && ((msg_flags & 0x8) != 0) {
                            ipc_logf_raw(b"RECVMSG SCM_DROP: ancillary truncated, fd(s) lost (ipcz buffer would strand on is_pending)");
                        }
                    }
                    return n;
                }
                n
            }
            // sendmmsg(fd, mmsghdr* IN/OUT, vlen, flags): the SENDMSG gather path
            // looped over the mmsghdr array (glibc's resolver batches the A + AAAA
            // queries through this). struct mmsghdr = struct msghdr (56B) +
            // msg_len:u32 @ off 56 + 4B pad = 64B on x86_64. We process each entry,
            // applying egress policy + netns re-home per-message exactly like
            // SENDMSG, and write the sent byte-count back to that entry's msg_len.
            // Returns the number of messages sent (0 = error on the FIRST, per the
            // kernel contract); a mid-batch error stops the loop after the prior
            // successes, mirroring sendmmsg(2).
            SYS_SENDMMSG => {
                const MMH: u64 = 64;
                let vlen = (c.min(1024)) as usize;
                let flags = d;
                let mut sent: i64 = 0;
                let mut last_err: i64 = 0;
                for m in 0..vlen {
                    let hp = b + (m as u64) * MMH; // this entry's msghdr (== mmsghdr base)
                    let mut mh = [0u8; 56];
                    vm_read(pid, hp, &mut mh);
                    let rd = |o: usize| u64::from_le_bytes(mh[o..o + 8].try_into().unwrap());
                    let name_ptr = rd(0);
                    let namelen = u32::from_le_bytes(mh[8..12].try_into().unwrap());
                    let (iov_ptr, iovlen) = (rd(16), (rd(24) as usize).min(1024));
                    let mut iovs = vec![0u8; iovlen * 16];
                    vm_read(pid, iov_ptr, &mut iovs);
                    let mut data = Vec::new();
                    for i in 0..iovlen {
                        let base = u64::from_le_bytes(iovs[i * 16..i * 16 + 8].try_into().unwrap());
                        let l =
                            (u64::from_le_bytes(iovs[i * 16 + 8..i * 16 + 16].try_into().unwrap())
                                .min(CAP)) as usize;
                        let mut seg = vec![0u8; l];
                        if l > 0 {
                            vm_read(pid, base, &mut seg);
                        }
                        data.extend_from_slice(&seg);
                    }
                    let mut addr = Vec::new();
                    let mut addr_len = 0usize;
                    if name_ptr != 0 && namelen > 0 {
                        addr.resize((namelen as usize).min(128), 0);
                        addr_len = addr.len();
                        vm_read(pid, name_ptr, &mut addr);
                        if let Some(nl) = unwrap_v4mapped_sockaddr(&mut addr) {
                            addr_len = nl;
                        }
                        if let Some(sa) = parse_connect_addr(&addr) {
                            if let Err(reason) = crate::vmm::egress_policy::check_addr(sa) {
                                if TRACE {
                                    log(b"[supervisor] egress DENY (sendmmsg): ");
                                    log(reason.as_bytes());
                                    log(b"\n");
                                }
                                // EACCES on the first entry is the whole call's error;
                                // on a later entry, stop and report prior successes.
                                if sent == 0 {
                                    return -13;
                                }
                                break;
                            }
                            if !sa.ip().is_loopback() {
                                rehome_to_host_netns(a as i32);
                            }
                            note_dns_dest(a as i32, &sa);
                        }
                    }
                    let iov = LocalIov {
                        base: data.as_ptr() as u64,
                        len: data.len() as u64,
                    };
                    let lmh = LocalMsghdr {
                        name: if addr.is_empty() {
                            0
                        } else {
                            addr.as_ptr() as u64
                        },
                        namelen: addr_len as u32,
                        _p: 0,
                        iov: &iov as *const _ as u64,
                        iovlen: 1,
                        control: 0,
                        controllen: 0,
                        flags: 0,
                        _p2: 0,
                    };
                    let r = host(SYS_SENDMSG, a, &lmh as *const _ as u64, flags, 0, 0, 0);
                    if r < 0 {
                        last_err = r;
                        break;
                    }
                    // write the sent length back into this entry's msg_len (off 56).
                    vm_write(pid, hp + 56, &(r as u32).to_le_bytes());
                    sent += 1;
                }
                if sent == 0 && last_err < 0 {
                    last_err
                } else {
                    sent
                }
            }
            // recvmmsg(fd, mmsghdr* IN/OUT, vlen, flags, timeout* IN|0): the RECVMSG
            // scatter path looped over the mmsghdr array. timeout (a timespec) is
            // honored only as a host-poll readiness gate BEFORE the first receive —
            // a precise per-message deadline rework would need a non-blocking
            // servicer, which is out of scope; without MSG_DONTWAIT this keeps the
            // existing blocking-host-call shape (no worse than RECVMSG). Each entry's
            // msg_len is written back with the received byte count.
            SYS_RECVMMSG => {
                const MMH: u64 = 64;
                let vlen = (c.min(1024)) as usize;
                let flags = d;
                // timeout (e): if present and non-zero, gate the FIRST recv on a host
                // poll so a 0/short timeout doesn't block the servicer indefinitely.
                if e != 0 {
                    let mut ts = [0u8; 16];
                    vm_read(pid, e, &mut ts);
                    let secs = i64::from_le_bytes(ts[0..8].try_into().unwrap());
                    let nsecs = i64::from_le_bytes(ts[8..16].try_into().unwrap());
                    let ms = secs.saturating_mul(1000).saturating_add(nsecs / 1_000_000);
                    let ms = if ms == 0 && (secs > 0 || nsecs > 0) {
                        1
                    } else {
                        ms
                    };
                    let ms = ms.clamp(0, i32::MAX as i64);
                    let mut pfd = [0u8; 8];
                    pfd[0..4].copy_from_slice(&(a as i32).to_le_bytes());
                    pfd[4..6].copy_from_slice(&0x001u16.to_le_bytes()); // POLLIN
                    let pr = host(SYS_POLL, pfd.as_mut_ptr() as u64, 1, ms as u64, 0, 0, 0);
                    if pr == 0 {
                        return 0; // timed out before any datagram arrived
                    }
                    if pr < 0 {
                        return pr;
                    }
                }
                let mut got: i64 = 0;
                for m in 0..vlen {
                    let hp = b + (m as u64) * MMH;
                    let mut mh = [0u8; 56];
                    vm_read(pid, hp, &mut mh);
                    let rd = |o: usize| u64::from_le_bytes(mh[o..o + 8].try_into().unwrap());
                    let name_ptr = rd(0);
                    let namecap = u32::from_le_bytes(mh[8..12].try_into().unwrap());
                    let (iov_ptr, iovlen) = (rd(16), (rd(24) as usize).min(1024));
                    let mut iovs = vec![0u8; iovlen * 16];
                    vm_read(pid, iov_ptr, &mut iovs);
                    let mut segs: Vec<(u64, usize)> = Vec::new();
                    let mut total = 0usize;
                    for i in 0..iovlen {
                        let base = u64::from_le_bytes(iovs[i * 16..i * 16 + 8].try_into().unwrap());
                        let l =
                            (u64::from_le_bytes(iovs[i * 16 + 8..i * 16 + 16].try_into().unwrap())
                                .min(CAP)) as usize;
                        segs.push((base, l));
                        total += l;
                    }
                    let mut bounce = vec![0u8; total];
                    let mut lname = vec![0u8; (namecap as usize).min(128)];
                    let iov = LocalIov {
                        base: bounce.as_mut_ptr() as u64,
                        len: total as u64,
                    };
                    let mut lmh = LocalMsghdr {
                        name: if lname.is_empty() {
                            0
                        } else {
                            lname.as_mut_ptr() as u64
                        },
                        namelen: lname.len() as u32,
                        _p: 0,
                        iov: &iov as *const _ as u64,
                        iovlen: 1,
                        control: 0,
                        controllen: 0,
                        flags: 0,
                        _p2: 0,
                    };
                    // After the first message, force MSG_DONTWAIT (0x40) so the loop
                    // drains only what's already queued rather than blocking on the
                    // next datagram — matching recvmmsg(2)'s "return what's ready".
                    let mflags = if got > 0 { flags | 0x40 } else { flags };
                    // The FIRST recv (got==0, no MSG_DONTWAIT, no timeout gate) can
                    // block indefinitely — cancel-aware (Part C). Drain passes use
                    // MSG_DONTWAIT so they never block (a plain host() is fine there).
                    let n = host_cancellable(
                        SYS_RECVMSG,
                        a,
                        &mut lmh as *mut _ as u64,
                        mflags,
                        0,
                        0,
                        0,
                    );
                    if n == CANCEL_SENTINEL {
                        return CANCEL_SENTINEL;
                    }
                    if n < 0 {
                        // An error on the FIRST message is the call's error; any
                        // error after ≥1 received message (EAGAIN once the queue is
                        // drained, or otherwise) ends the batch — return the count.
                        if got == 0 {
                            return n;
                        }
                        break;
                    }
                    let n = maybe_strip_aaaa(a as i32, &mut bounce, n);
                    let recvd = n as usize;
                    let mut off = 0usize;
                    for (base, l) in segs {
                        if off >= recvd {
                            break;
                        }
                        let take = l.min(recvd - off);
                        vm_write(pid, base, &bounce[off..off + take]);
                        off += take;
                    }
                    if name_ptr != 0 && namecap > 0 {
                        let w = (lmh.namelen as usize).min(lname.len());
                        vm_write(pid, name_ptr, &lname[..w]);
                        vm_write(pid, hp + 8, &lmh.namelen.to_le_bytes());
                    }
                    vm_write(pid, hp + 48, &lmh.flags.to_le_bytes()); // msg_flags
                    vm_write(pid, hp + 40, &0u64.to_le_bytes()); // msg_controllen = 0
                    vm_write(pid, hp + 56, &(n as u32).to_le_bytes()); // msg_len
                    got += 1;
                }
                got
            }
            SYS_ACCEPT | SYS_ACCEPT4 => {
                // accept[4](fd, addr* OUT, addrlen* IN/OUT, flags). A blocking accept
                // on a listener with no incoming connection is indefinite —
                // cancel-aware (Part C).
                let n = if b == 0 {
                    host_cancellable(nr, a, 0, 0, d, 0, 0)
                } else {
                    let mut lenbuf = [0u8; 4];
                    vm_read(pid, c, &mut lenbuf);
                    let cap = (u32::from_le_bytes(lenbuf) as usize).min(128);
                    let mut addr = vec![0u8; cap];
                    let mut outlen = cap as u32;
                    let n = host_cancellable(
                        nr,
                        a,
                        addr.as_mut_ptr() as u64,
                        &mut outlen as *mut u32 as u64,
                        d,
                        0,
                        0,
                    );
                    if n >= 0 {
                        let w = (outlen as usize).min(cap);
                        vm_write(pid, b, &addr[..w]);
                        vm_write(pid, c, &outlen.to_le_bytes());
                    }
                    n
                };
                if n == CANCEL_SENTINEL {
                    CANCEL_SENTINEL
                } else if n < 0 {
                    n
                } else {
                    fd_install(pid, n as i32, 0)
                }
            }
            SYS_GETSOCKNAME | SYS_GETPEERNAME => {
                let mut lenbuf = [0u8; 4];
                vm_read(pid, c, &mut lenbuf);
                let cap = (u32::from_le_bytes(lenbuf) as usize).min(128);
                let mut addr = vec![0u8; cap];
                let mut outlen = cap as u32;
                let n = host(
                    nr,
                    a,
                    addr.as_mut_ptr() as u64,
                    &mut outlen as *mut u32 as u64,
                    0,
                    0,
                    0,
                );
                if n == 0 {
                    let w = (outlen as usize).min(cap);
                    vm_write(pid, b, &addr[..w]);
                    vm_write(pid, c, &outlen.to_le_bytes());
                }
                n
            }
            SYS_SETSOCKOPT => {
                // setsockopt(fd, level, optname, optval* IN, optlen)
                let len = (e.min(256)) as usize;
                let mut opt = vec![0u8; len];
                vm_read(pid, d, &mut opt);
                host(SYS_SETSOCKOPT, a, b, c, opt.as_ptr() as u64, len as u64, 0)
            }
            SYS_GETSOCKOPT => {
                // getsockopt(fd, level, optname, optval* OUT, optlen* IN/OUT)
                let mut lenbuf = [0u8; 4];
                vm_read(pid, e, &mut lenbuf);
                let cap = (u32::from_le_bytes(lenbuf) as usize).min(256);
                let mut opt = vec![0u8; cap];
                let mut outlen = cap as u32;
                let n = host(
                    SYS_GETSOCKOPT,
                    a,
                    b,
                    c,
                    opt.as_mut_ptr() as u64,
                    &mut outlen as *mut u32 as u64,
                    0,
                );
                if n == 0 {
                    let w = (outlen as usize).min(cap);
                    vm_write(pid, d, &opt[..w]);
                    vm_write(pid, e, &outlen.to_le_bytes());
                }
                n
            }
            // readv(fd, iov*, cnt): read contiguously (stream-equivalent), scatter.
            SYS_READV => {
                let cnt = (c.min(1024)) as usize;
                let mut iovs = vec![0u8; cnt * 16];
                vm_read(pid, b, &mut iovs);
                let mut segs: Vec<(u64, usize)> = Vec::new();
                let mut total = 0usize;
                for i in 0..cnt {
                    let base = u64::from_le_bytes(iovs[i * 16..i * 16 + 8].try_into().unwrap());
                    let len =
                        (u64::from_le_bytes(iovs[i * 16 + 8..i * 16 + 16].try_into().unwrap())
                            .min(CAP)) as usize;
                    segs.push((base, len));
                    total += len;
                }
                let mut bounce = vec![0u8; total];
                let n = host(
                    SYS_READ,
                    a,
                    bounce.as_mut_ptr() as u64,
                    total as u64,
                    0,
                    0,
                    0,
                );
                if n > 0 {
                    let got = n as usize;
                    let mut off = 0usize;
                    for (base, len) in segs {
                        if off >= got {
                            break;
                        }
                        let take = len.min(got - off);
                        let w = vm_write(pid, base, &bounce[off..off + take]);
                        if w != take as i64 {
                            return -14; // -EFAULT: guest iov was not fully writable
                        }
                        off += take;
                    }
                }
                n
            }
            // ---- no-pointer passthroughs ----
            SYS_LSEEK | SYS_GETUID | SYS_GETEUID | SYS_GETGID | SYS_GETEGID | SYS_GETPID
            | SYS_GETTID => host(nr, a, b, c, d, e, f),
            // close: drop the guest fd from THIS pid's table, close our host fd
            // (and untag it if it was a synthetic /proc/self/fd dir, so a later
            // reuse of the host fd number isn't mis-synthesized).
            SYS_CLOSE => {
                if unsafe { FDTRACE } && (a == 10 || a == 11) {
                    ipc_logf(
                        &[(b"FDTRACE_CLOSE pid=", pid as i64), (b" g=", a as i64)],
                        b"",
                    );
                }
                match fd_remove(pid, a as i32) {
                    Some(FdVal::Host(h)) => {
                        fd_forget_host_side_tables(h);
                        host(SYS_CLOSE, h as u64, 0, 0, 0, 0, 0)
                    }
                    Some(FdVal::Loop(s)) => {
                        loop_close(s);
                        0
                    }
                    None => -9, // -EBADF
                }
            }
            // fcntl: F_DUPFD/F_DUPFD_CLOEXEC create a new fd (guest nr ≥ arg);
            // everything else passes through on the translated host fd.
            SYS_FCNTL => match b {
                0 | 1030 => {
                    let n = host(SYS_FCNTL, a, b, 0, 0, 0, 0);
                    if n < 0 {
                        n
                    } else {
                        let flags = if b == F_DUPFD_CLOEXEC {
                            FD_CLOEXEC as i32
                        } else {
                            0
                        };
                        fd_install_val_with_flags(pid, FdVal::Host(n as i32), c as i32, flags)
                    }
                }
                F_GETFD => {
                    if let Some(g) = fd_guest_for_host(pid, a as i32) {
                        fd_get_desc_flags(pid, g, FdVal::Host(a as i32)) as i64
                    } else {
                        host(SYS_FCNTL, a, b, c, 0, 0, 0)
                    }
                }
                F_SETFD => {
                    let r = host(SYS_FCNTL, a, b, c, 0, 0, 0);
                    if r == 0 {
                        if let Some(g) = fd_guest_for_host(pid, a as i32) {
                            fd_set_desc_flags(pid, g, c as i32);
                            if unsafe { FDTRACE } && (g == 10 || g == 11) {
                                ipc_logf(
                                    &[
                                        (b"FDTRACE_F_SETFD pid=", pid as i64),
                                        (b" g=", g as i64),
                                        (b" h=", a as i64),
                                        (b" flags=", c as i64),
                                    ],
                                    b"",
                                );
                            }
                        }
                    }
                    r
                }
                // record-lock cmds: arg c is a GUEST ptr to `struct flock` (32B).
                // Marshal it into a supervisor-local buffer, call host fcntl on the
                // (already-translated) host fd, and for *GETLK push the result back.
                // F_SETLKW / F_OFD_SETLKW block on contention — same blocking
                // host-call shape as neighboring arms (e.g. SYS_FLOCK); no
                // non-blocking-servicer rework (see risks).
                //
                // ONLY these six cmds take the struct-pointer path. Every other
                // fcntl cmd (F_GETFD/F_SETFD/F_GETFL/F_SETFL/F_SETOWN/F_GETOWN/…)
                // carries an INTEGER (or no) arg and MUST stay on the raw
                // passthrough below — routing one of them here would dereference
                // its integer arg as a guest pointer and fault. The match is
                // exhaustive on the lock cmds precisely so that can't happen.
                F_GETLK | F_SETLK | F_SETLKW | F_OFD_GETLK | F_OFD_SETLK | F_OFD_SETLKW => {
                    // A NULL struct pointer is EFAULT, not a marshal attempt
                    // (vm_read of 0 would already fail, but be explicit).
                    if c == 0 {
                        return -14; // -EFAULT
                    }
                    // 32-byte supervisor-local buffer: the host kernel copy_to/
                    // from_user's it (byte-wise, so [u8; 32]'s align-1 is fine),
                    // never the guest VA. Sized to `struct flock` on x86_64.
                    let mut fl = [0u8; FLOCK_SZ];
                    if vm_read(pid, c, &mut fl) != FLOCK_SZ as i64 {
                        return -14; // -EFAULT (short/failed read of the guest struct)
                    }
                    // F_SETLKW/F_OFD_SETLKW block indefinitely on contention from a
                    // dead lock-holder — cancel-aware (Part C). The non-blocking lock
                    // cmds never hit the cancel path (no EINTR), so this is additive.
                    let n = host_cancellable(SYS_FCNTL, a, b, fl.as_mut_ptr() as u64, 0, 0, 0);
                    if n == CANCEL_SENTINEL {
                        return CANCEL_SENTINEL;
                    }
                    // *GETLK reports the conflicting lock (or F_UNLCK) back into the
                    // guest's struct flock; write it back only on success.
                    if n >= 0 && (b == F_GETLK || b == F_OFD_GETLK) {
                        vm_write(pid, c, &fl);
                    }
                    n
                }
                // All other cmds: integer/no arg, forwarded raw on the translated
                // host fd. (No struct-pointer cmds reach here — see the lock arm.)
                _ => host(SYS_FCNTL, a, b, c, d, e, f),
            },
            // ioctl: forward the specific cmds libuv/glibc use (FIONBIO is how
            // libuv sets non-blocking — returning a blanket ENOTTY breaks node's
            // stdio setup). Unknown cmds → ENOTTY.
            SYS_IOCTL => match b {
                0x5421 | 0x5452 => {
                    // FIONBIO (0x5421) — set non-blocking; FIOASYNC (0x5452) — toggle
                    // O_ASYNC/SIGIO. Both are `int`-IN on the host fd. FIOASYNC is
                    // load-bearing for nginx: ngx_spawn_process does
                    // ioctl(channel, FIOASYNC, &on) on the master↔worker socketpair and
                    // ABORTS the worker fork (NGX_INVALID_PID) if it fails — a blanket
                    // ENOTTY here left nginx master-only, so nothing accepted and every
                    // loopback request timed out. The channel is a real host AF_UNIX
                    // socketpair (FdVal::Host), so forwarding succeeds.
                    let mut v = [0u8; 4];
                    vm_read(pid, c, &mut v);
                    host(SYS_IOCTL, a, b, v.as_ptr() as u64, 0, 0, 0)
                }
                0x541B => {
                    // FIONREAD — int OUT
                    let mut v = [0u8; 4];
                    let n = host(SYS_IOCTL, a, b, v.as_mut_ptr() as u64, 0, 0, 0);
                    if n >= 0 {
                        vm_write(pid, c, &v);
                    }
                    n
                }
                0x5413 => {
                    // TIOCGWINSZ — winsize OUT (8B)
                    let mut v = [0u8; 8];
                    let n = host(SYS_IOCTL, a, b, v.as_mut_ptr() as u64, 0, 0, 0);
                    if n >= 0 {
                        vm_write(pid, c, &v);
                    }
                    n
                }
                0x5401 => {
                    // TCGETS — termios OUT (60B; ENOTTY on a pipe, which is correct)
                    let mut v = [0u8; 60];
                    let n = host(SYS_IOCTL, a, b, v.as_mut_ptr() as u64, 0, 0, 0);
                    if n >= 0 {
                        vm_write(pid, c, &v);
                    }
                    n
                }
                0x5402 | 0x5403 | 0x5404 => {
                    // TCSETS/TCSETSW/TCSETSF — termios IN
                    let mut v = [0u8; 60];
                    vm_read(pid, c, &mut v);
                    host(SYS_IOCTL, a, b, v.as_ptr() as u64, 0, 0, 0)
                }
                0x5414 => {
                    // TIOCSWINSZ — winsize IN (8B). Lets an in-guest `resize`/
                    // `stty rows/cols` set the pty size; the host also drives it
                    // out-of-band via RESIZE frames (`set_winsize` on the master).
                    let mut v = [0u8; 8];
                    vm_read(pid, c, &mut v);
                    host(SYS_IOCTL, a, b, v.as_ptr() as u64, 0, 0, 0)
                }
                _ => -25, // -ENOTTY
            },
            // (rt_sigaction / rt_sigprocmask are handled CELL-LOCALLY in cell_layer1
            // now — they install real guest handlers + arm masks in the guest's own
            // process — so they never reach this delegated path.)
            // xattrs: report "no data" so `ls` etc. fall through cleanly
            SYS_GETXATTR | SYS_LGETXATTR | SYS_FGETXATTR => -61,
            // chdir: per-pid guest cwd. Resolve the target within the rootfs,
            // require it be a directory, then store the SYMLINK-CANONICAL guest
            // path (read back from /proc/self/fd so a later `cd ..` across a
            // symlinked dir is POSIX-correct). Future relative paths from this
            // pid resolve against it (see `pull_cwd_path`).
            SYS_CHDIR => {
                let cand = pull_cwd_path(pid, a);
                let fd = open_path(pid, &cand, O_PATH | O_DIRECTORY, 0);
                if fd < 0 {
                    return fd; // -ENOENT / -ENOTDIR
                }
                let guest_cwd = canonical_guest_cwd(fd as i32);
                host(SYS_CLOSE, fd as u64, 0, 0, 0, 0, 0);
                match guest_cwd {
                    Some(c) => {
                        cwds().lock().unwrap().insert(pid, c);
                        0
                    }
                    None => -2, // -ENOENT (couldn't read it back)
                }
            }
            // fchdir(fd): set the per-pid guest cwd from an open dir fd. `fd` is a
            // guest fd (NOT translated in the prelude) → host fd. Derive that fd's
            // GUEST path (readlink /proc/self/fd/<h> → host path → guest path), then
            // run the exact chdir validation on it: re-open O_PATH|O_DIRECTORY
            // (rejects a non-dir with -ENOTDIR, confined within the rootfs) and store
            // the symlink-CANONICAL guest cwd. `cd -` / fd-based cwd save-restore and
            // libc `fchdir` after an `open(O_DIRECTORY)` now work.
            SYS_FCHDIR => {
                let h = match fd_host(pid, a as i32) {
                    Some(h) => h,
                    None => return -9, // -EBADF
                };
                let mut cand = match canonical_guest_cwd(h) {
                    Some(g) => g,
                    None => return -2, // -ENOENT (fd outside the rootfs / unreadable)
                };
                cand.push(0); // canonical_guest_cwd returns no trailing NUL
                let fd = open_path(pid, &cand, O_PATH | O_DIRECTORY, 0);
                if fd < 0 {
                    return fd; // -ENOTDIR (fd wasn't a directory) / -ENOENT
                }
                let guest_cwd = canonical_guest_cwd(fd as i32);
                host(SYS_CLOSE, fd as u64, 0, 0, 0, 0, 0);
                match guest_cwd {
                    Some(c) => {
                        cwds().lock().unwrap().insert(pid, c);
                        0
                    }
                    None => -2, // -ENOENT (couldn't read it back)
                }
            }
            // a cell process is gone (its own exit_group, or reaped via wait4):
            // close its host fds, drop its table — this delivers pipe EOFs.
            CTL_REAP => {
                signalfd_notify_sigchld(a as i32);
                shm_reap_pid(a as i32);
                fd_drop(a as i32);
                0
            }
            CTL_SHM_FLUSH => {
                if c == 2 {
                    shm_flush_pid_range_for_exec(pid, a, b)
                } else {
                    shm_flush_pid_range(pid, a, b, c != 0)
                }
            }
            CTL_SHM_ALIAS => shm_alias_pid_range(pid, a, b, c),
            CTL_SHM_PROTECT => shm_protect_pid_range(pid, a, b, c),
            CTL_SHM_MOVE => shm_move_pid_range(pid, a, b, c, d),
            // pre-fork: snapshot the caller's fd table for the child that will
            // claim ring slot `a`.
            CTL_FORK_TABLE => {
                if TRACE {
                    logn(b"  [CTL_FORK_TABLE slot=", a as i64, b"");
                    logn(b" spawn_nr=", b as i64, b"");
                    logn(b" flags=", c as i64, b"]\n");
                }
                fd_snapshot(pid, a);
                0
            }
            // lazily spawn the servicer for a just-claimed slot (clone/fork).
            CTL_ENSURE_SERVICER => {
                if ensure_servicer(a) {
                    0
                } else {
                    -11 // -EAGAIN (OS refused the thread)
                }
            }
            CTL_BIND_SLOT => {
                proctree::bind_slot(a, b as i32);
                0
            }
            CTL_SET_CREDS => {
                let (ruid, euid) = unpack_u32_pair(a);
                let (suid, fsuid) = unpack_u32_pair(b);
                let (rgid, egid) = unpack_u32_pair(c);
                let (sgid, fsgid) = unpack_u32_pair(d);
                supervisor_creds().lock().unwrap().insert(
                    pid,
                    CellCredSnapshot {
                        ruid,
                        euid,
                        suid,
                        fsuid,
                        rgid,
                        egid,
                        sgid,
                        fsgid,
                    },
                );
                0
            }
            CTL_SET_UMASK => {
                supervisor_umasks()
                    .lock()
                    .unwrap()
                    .insert(pid, (a as u32) & 0o777);
                0
            }
            // record the caller's new /proc/self/exe (a=ptr, b=len in the cell's
            // address space) so the supervisor-served readlink/open is correct.
            CTL_SET_EXE => {
                let len = (b as usize).min(4096);
                if a != 0 && len > 0 {
                    let mut buf = vec![0u8; len];
                    if vm_read(pid, a, &mut buf) == len as i64 {
                        if unsafe { IPCTRACE } {
                            ipc_logf_raw(
                                &[b"SETEXE pid=", pid.to_string().as_bytes(), b" exe=", &buf]
                                    .concat(),
                            );
                        }
                        proc_exe().lock().unwrap().insert(pid, buf);
                    }
                }
                0
            }
            // CTL_SET_CMDLINE(ptr, len): the cell forwards its NUL-separated guest
            // argv on each in-cell execve. Recorded per-pid so `/proc/<pid>/cmdline`
            // (served supervisor-side) reflects the real workload command — in-guest
            // `pgrep -f`/`ps` then match it.
            CTL_SET_CMDLINE => {
                let len = (b as usize).min(8192);
                if a != 0 && len > 0 {
                    let mut buf = vec![0u8; len];
                    if vm_read(pid, a, &mut buf) == len as i64 {
                        if unsafe { IPCTRACE } {
                            ipc_logf_raw(
                                &[b"SETCMD pid=", pid.to_string().as_bytes(), b" ", &buf].concat(),
                            );
                        }
                        proc_cmdline().lock().unwrap().insert(pid, buf);
                        repair_chromium_zygote_salt_fd(pid);
                        fdtrace_dump_table(pid, b"SETCMD");
                    }
                }
                0
            }
            CTL_ENVDBG => {
                let len = (b as usize).min(8192);
                if a != 0 && len > 0 {
                    let mut buf = vec![0u8; len];
                    if vm_read(pid, a, &mut buf) == len as i64 {
                        ipc_logf_raw(
                            &[b"ENVDBG_CTL pid=", pid.to_string().as_bytes(), b" ", &buf].concat(),
                        );
                    }
                }
                0
            }
            CTL_PROCEXE_EXEC_FAIL => {
                let len = (b as usize).min(1024);
                if a != 0 && len > 0 {
                    let mut buf = vec![0u8; len];
                    if vm_read(pid, a, &mut buf) == len as i64 {
                        ipc_logf_raw(
                            &[
                                b"PROCEXE_EXEC_FAIL pid=".as_slice(),
                                pid.to_string().as_bytes(),
                                b" ".as_slice(),
                                &buf,
                            ]
                            .concat(),
                        );
                    }
                }
                0
            }
            CTL_WATCHADDR => {
                // a = guest addr to watch, b = original byte. Poll the cell's memory
                // cross-process (process_vm_readv); on change, log the pid + every thread's
                // current syscall+PC (/proc/<pid>/task/*/syscall). Layout-neutral observation.
                use std::sync::atomic::{AtomicU32, Ordering};
                static WATCH_N: AtomicU32 = AtomicU32::new(0);
                if a != 0 && WATCH_N.fetch_add(1, Ordering::Relaxed) < 12 {
                    let watch_pid = pid;
                    let addr = a;
                    let orig = (b & 0xff) as u8;
                    {
                        let mut arm: Vec<u8> = Vec::with_capacity(80);
                        arm.extend_from_slice(b"WATCHARM pid=");
                        arm.extend_from_slice(watch_pid.to_string().as_bytes());
                        arm.extend_from_slice(b" addr=0x");
                        const HX2: &[u8; 16] = b"0123456789abcdef";
                        for sh in (0..16u32).rev() {
                            arm.push(HX2[((addr >> (sh * 4)) & 0xf) as usize]);
                        }
                        arm.extend_from_slice(b" orig=");
                        arm.extend_from_slice(orig.to_string().as_bytes());
                        ipc_logf_raw(&arm);
                    }
                    let _ = orig;
                    std::thread::spawn(move || {
                        // Arm a 1-byte HW WRITE-breakpoint on this byte THE MOMENT build_stack
                        // wrote the env (so we beat the early stray + target the exact copy the
                        // new program reads). On the corrupting write, perf records the IP.
                        let fd = perf_open_wbp(watch_pid, addr);
                        if fd < 0 {
                            ipc_logf_raw(
                                &[b"WATCHBP_FAIL fd=".as_slice(), fd.to_string().as_bytes()]
                                    .concat(),
                            );
                            return;
                        }
                        unsafe { host(SYS_IOCTL, fd as u64, 0x2400, 0, 0, 0, 0) }; // ENABLE
                        let ring = perf_mmap_ring(fd);
                        if ring.is_null() {
                            ipc_logf_raw(b"WATCHBP_NORING");
                            return;
                        }
                        const HX: &[u8; 16] = b"0123456789abcdef";
                        for _ in 0..3_000_000u64 {
                            if let Some(sample) = perf_read_sample(ring) {
                                let rip = sample.ip;
                                let mut out: Vec<u8> = Vec::with_capacity(320);
                                out.extend_from_slice(b"WATCHBP_RIP pid=");
                                out.extend_from_slice(watch_pid.to_string().as_bytes());
                                out.extend_from_slice(b" addr=0x");
                                for sh in (0..16u32).rev() {
                                    out.push(HX[((addr >> (sh * 4)) & 0xf) as usize]);
                                }
                                out.extend_from_slice(b" rip=0x");
                                for sh in (0..16u32).rev() {
                                    out.push(HX[((rip >> (sh * 4)) & 0xf) as usize]);
                                }
                                if let Some(regs) = sample.regs {
                                    macro_rules! reg {
                                        ($name:expr, $value:expr) => {{
                                            out.extend_from_slice($name);
                                            out.extend_from_slice(b"=0x");
                                            for sh in (0..16u32).rev() {
                                                out.push(HX[((($value) >> (sh * 4)) & 0xf) as usize]);
                                            }
                                        }};
                                    }
                                    reg!(b" ax", regs.ax);
                                    reg!(b" cx", regs.cx);
                                    reg!(b" dx", regs.dx);
                                    reg!(b" si", regs.si);
                                    reg!(b" di", regs.di);
                                    reg!(b" bp", regs.bp);
                                    reg!(b" sp", regs.sp);
                                    reg!(b" ipreg", regs.ip);
                                }
                                // Is this the REAL truncation (value prefix still intact, only
                                // offset 59 changed) or benign reuse (whole buffer replaced)?
                                {
                                    let mut ctx = [0u8; 96];
                                    let cs = addr.wrapping_sub(72); // "DATABASE_URL=" start
                                    if vm_read(watch_pid, cs, &mut ctx) == 96 {
                                        let real = ctx.starts_with(b"DATABASE_URL=postgresql");
                                        out.extend_from_slice(if real {
                                            b" real=1 ctx=|"
                                        } else {
                                            b" real=0 ctx=|"
                                        });
                                        for &c in ctx.iter() {
                                            out.push(if (0x20..0x7f).contains(&c) { c } else { b'.' });
                                        }
                                        out.extend_from_slice(b"|");
                                    }
                                }
                                // symbolize: find the maps line whose range contains rip
                                if let Ok(maps) =
                                    std::fs::read_to_string(format!("/proc/{watch_pid}/maps"))
                                {
                                    for line in maps.lines() {
                                        let mut it = line.split([' ', '-']);
                                        let s = it.next().and_then(|x| u64::from_str_radix(x, 16).ok());
                                        let e = it.next().and_then(|x| u64::from_str_radix(x, 16).ok());
                                        if let (Some(s), Some(e)) = (s, e) {
                                            if rip >= s && rip < e {
                                                out.extend_from_slice(b" map=");
                                                out.extend_from_slice(line.as_bytes());
                                                break;
                                            }
                                        }
                                    }
                                }
                                // Dump the JIT instruction bytes around rip so the write can be
                                // disassembled offline (memcpy/rep-movs + the length register tell
                                // us whether V8 computed a short length, and where it came from).
                                {
                                    let mut code = [0u8; 64];
                                    let cstart = rip.wrapping_sub(48);
                                    if vm_read(watch_pid, cstart, &mut code) == 64 {
                                        out.extend_from_slice(b" code@rip-48=");
                                        for &cb in code.iter() {
                                            out.push(HX[(cb >> 4) as usize]);
                                            out.push(HX[(cb & 0xf) as usize]);
                                        }
                                    }
                                }
                                ipc_logf_raw(&out);
                                break;
                            }
                            std::thread::sleep(std::time::Duration::from_micros(20));
                        }
                    });
                }
                0
            }
            CTL_COREDUMP => {
                // Rather than gcore a multi-GB full core (and fight the supervisor's own
                // SIGCHLD reaper), PAUSE the origin cell here — it stays blocked in this
                // delegate, so its heap is frozen — and log its pid. An external gdb then
                // attaches and reads just the 64 MiB brk-heap region live.
                use std::sync::atomic::{AtomicU32, Ordering};
                static COREDUMP_N: AtomicU32 = AtomicU32::new(0);
                if COREDUMP_N.fetch_add(1, Ordering::Relaxed) == 0 {
                    ipc_logf_raw(
                        &[
                            b"COREDUMP_READY pid=",
                            pid.to_string().as_bytes(),
                            b" frozen 240s for external gdb",
                        ]
                        .concat(),
                    );
                    std::thread::sleep(std::time::Duration::from_secs(240));
                    ipc_logf_raw(
                        &[b"COREDUMP_RESUME pid=", pid.to_string().as_bytes()].concat(),
                    );
                }
                0
            }
            CTL_CLOSE_CLOEXEC => {
                fdtrace_dump_table(pid, b"PRE_CLOEXEC");
                let victims: Vec<i32> = {
                    let t = fdt().lock().unwrap();
                    t.get(&pid)
                        .map(|m| {
                            m.iter()
                                .filter_map(|(&g, &v)| {
                                    let flags = fd_get_desc_flags(pid, g, v);
                                    ((flags & FD_CLOEXEC as i32) != 0).then_some(g)
                                })
                                .collect()
                        })
                        .unwrap_or_default()
                };
                for g in victims {
                    match fd_remove(pid, g) {
                        Some(FdVal::Host(h)) => {
                            fd_forget_host_side_tables(h);
                            host(SYS_CLOSE, h as u64, 0, 0, 0, 0, 0);
                        }
                        Some(FdVal::Loop(s)) => {
                            loop_close(s)
                        }
                        None => {}
                    }
                }
                fdtrace_dump_table(pid, b"POST_CLOEXEC");
                0
            }
            CTL_DETHREAD_FOR_EXEC => supervisor_dethread_for_exec(a, b as i32, c as i32),
            // the fork failed after the snapshot: drop it, release the slot
            // (still unclaimed, so it's safe to free directly here).
            CTL_FORK_CANCEL => {
                if let Some(m) = pending().lock().unwrap().remove(&a) {
                    for (_, v) in m {
                        if let FdVal::Host(h) = v {
                            host(SYS_CLOSE, h as u64, 0, 0, 0, 0, 0);
                        }
                    }
                }
                pending_fd_flags().lock().unwrap().remove(&a);
                pending_creds().lock().unwrap().remove(&a);
                pending_umasks().lock().unwrap().remove(&a);
                pending_cwd().lock().unwrap().remove(&a);
                pending_exe().lock().unwrap().remove(&a);
                pending_cmdline().lock().unwrap().remove(&a);
                proctree::cancel_slot(a);
                free_slot(a as u32);
                0
            }
            // ---- fallocate(fd, mode, offset, len): preallocate / punch holes.
            // `a` was translated in the prelude; no cell pointers — forward. Lets
            // databases (sqlite/rocksdb) and `fallocate(1)` size files up front.
            SYS_FALLOCATE => host(SYS_FALLOCATE, a, b, c, d, 0, 0),
            // ---- copy_file_range(fd_in, off_in*, fd_out, off_out*, len, flags):
            // both fds translated in the prelude (a, c). off_in/off_out are
            // 8-byte IN/OUT cursors that may be NULL (the SENDFILE offset shape,
            // doubled). When non-NULL the kernel both reads the start offset and
            // writes back the advanced one, so bounce each in and back out.
            SYS_COPY_FILE_RANGE => {
                let mut ib = [0u8; 8];
                let ip = if b != 0 {
                    vm_read(pid, b, &mut ib);
                    ib.as_mut_ptr() as u64
                } else {
                    0
                };
                let mut ob = [0u8; 8];
                let op = if d != 0 {
                    vm_read(pid, d, &mut ob);
                    ob.as_mut_ptr() as u64
                } else {
                    0
                };
                let n = host(SYS_COPY_FILE_RANGE, a, ip, c, op, e, f);
                if b != 0 {
                    vm_write(pid, b, &ib);
                }
                if d != 0 {
                    vm_write(pid, d, &ob);
                }
                n
            }
            // ---- preadv/preadv2(fd, iov*, cnt, pos_l, pos_h[, flags]): positioned
            // scatter read. Like SYS_READV, but a single POSITIONED read into a
            // bounce buffer (pos = pos_l | pos_h<<32), then scatter. preadv2's
            // RWF_* flags (arg5) are dropped — pread64 ignores them, which is the
            // safe default for the common RWF=0 case.
            SYS_PREADV | SYS_PREADV2 => {
                let cnt = (c.min(1024)) as usize;
                let mut iovs = vec![0u8; cnt * 16];
                vm_read(pid, b, &mut iovs);
                let mut segs: Vec<(u64, usize)> = Vec::new();
                let mut total = 0usize;
                for i in 0..cnt {
                    let base = u64::from_le_bytes(iovs[i * 16..i * 16 + 8].try_into().unwrap());
                    let len =
                        (u64::from_le_bytes(iovs[i * 16 + 8..i * 16 + 16].try_into().unwrap())
                            .min(CAP)) as usize;
                    segs.push((base, len));
                    total += len;
                }
                // Kernel composes pos_from_hilo: ((u64)pos_h << 32) | (u32)pos_l.
                let pos = ((e & 0xffff_ffff) << 32) | (d & 0xffff_ffff);
                let mut bounce = vec![0u8; total];
                let n = host(
                    SYS_PREAD64,
                    a,
                    bounce.as_mut_ptr() as u64,
                    total as u64,
                    pos,
                    0,
                    0,
                );
                if n > 0 {
                    let got = n as usize;
                    let mut off = 0usize;
                    for (base, len) in segs {
                        if off >= got {
                            break;
                        }
                        let take = len.min(got - off);
                        vm_write(pid, base, &bounce[off..off + take]);
                        off += take;
                    }
                }
                n
            }
            // ---- pwritev/pwritev2(fd, iov*, cnt, pos_l, pos_h[, flags]): positioned
            // gather write. Like SYS_WRITEV, but a single POSITIONED write of the
            // concatenation (pos = pos_l | pos_h<<32). pwritev2's RWF_* flags
            // (arg5) are dropped (pwrite64 ignores them; safe for RWF=0).
            SYS_PWRITEV | SYS_PWRITEV2 => {
                let cnt = (c.min(1024)) as usize;
                let mut iovs = vec![0u8; cnt * 16];
                vm_read(pid, b, &mut iovs);
                let mut data = Vec::new();
                for i in 0..cnt {
                    let base = u64::from_le_bytes(iovs[i * 16..i * 16 + 8].try_into().unwrap());
                    let len = u64::from_le_bytes(iovs[i * 16 + 8..i * 16 + 16].try_into().unwrap());
                    let len = len.min(CAP) as usize;
                    let mut seg = vec![0u8; len];
                    if len > 0 {
                        vm_read(pid, base, &mut seg);
                    }
                    data.extend_from_slice(&seg);
                }
                // Kernel composes pos_from_hilo: ((u64)pos_h << 32) | (u32)pos_l.
                let pos = ((e & 0xffff_ffff) << 32) | (d & 0xffff_ffff);
                host(
                    SYS_PWRITE64,
                    a,
                    data.as_ptr() as u64,
                    data.len() as u64,
                    pos,
                    0,
                    0,
                )
            }
            // ---- fstatfs(fd, buf*): 120-byte struct OUT. `a` translated in the
            // prelude (mirrors the path-based SYS_STATFS arm, fd-based). Lets
            // `df`, `statvfs`, and node's fs.statfs read filesystem geometry.
            SYS_FSTATFS => {
                let mut sb = [0u8; 120];
                let n = host(SYS_FSTATFS, a, sb.as_mut_ptr() as u64, 0, 0, 0, 0);
                if n == 0 {
                    vm_write(pid, b, &sb);
                }
                n
            }
            // ---- splice(fd_in, off_in*, fd_out, off_out*, len, flags): zero-copy
            // pipe<->fd move. Both fds translated in the prelude (a, c). off_in/
            // off_out are 8-byte IN/OUT cursors that may be NULL (must be NULL for
            // the pipe end). Bounce each in and back out, like copy_file_range.
            SYS_SPLICE => {
                let mut ib = [0u8; 8];
                let ip = if b != 0 {
                    vm_read(pid, b, &mut ib);
                    ib.as_mut_ptr() as u64
                } else {
                    0
                };
                let mut ob = [0u8; 8];
                let op = if d != 0 {
                    vm_read(pid, d, &mut ob);
                    ob.as_mut_ptr() as u64
                } else {
                    0
                };
                let n = host(SYS_SPLICE, a, ip, c, op, e, f);
                if b != 0 {
                    vm_write(pid, b, &ib);
                }
                if d != 0 {
                    vm_write(pid, d, &ob);
                }
                n
            }
            // ---- tee(fd_in, fd_out, len, flags): duplicate pipe contents. Both
            // fds translated in the prelude (a, b); no cell pointers — forward.
            SYS_TEE => host(SYS_TEE, a, b, c, d, 0, 0),
            // ---- vmsplice(fd, iov*, nr_segs, flags): map user pages into a pipe.
            // The cell's iovec memory isn't in our address space, so gather it into
            // a bounce buffer and WRITE it to the (translated) pipe fd — the
            // dominant write-to-pipe direction, mirroring SYS_WRITEV's gather. The
            // read direction (vmsplice filling user iovecs from a pipe) is not
            // supported; callers that need it fall back via short writes.
            SYS_VMSPLICE => {
                let cnt = (c.min(1024)) as usize;
                let mut iovs = vec![0u8; cnt * 16];
                vm_read(pid, b, &mut iovs);
                let mut data = Vec::new();
                for i in 0..cnt {
                    let base = u64::from_le_bytes(iovs[i * 16..i * 16 + 8].try_into().unwrap());
                    let len = u64::from_le_bytes(iovs[i * 16 + 8..i * 16 + 16].try_into().unwrap());
                    let len = len.min(CAP) as usize;
                    let mut seg = vec![0u8; len];
                    if len > 0 {
                        vm_read(pid, base, &mut seg);
                    }
                    data.extend_from_slice(&seg);
                }
                host(
                    SYS_WRITE,
                    a,
                    data.as_ptr() as u64,
                    data.len() as u64,
                    0,
                    0,
                    0,
                )
            }
            // (exit/exit_group are cell-local now; the SIGCHLD reaper tears the
            //  sandbox down when the MAIN cell process dies.)
            other => {
                // A guest will harmlessly probe syscalls we don't serve (e.g.
                // wget's `setitimer` for `-T`); it gets ENOSYS and falls back, so
                // don't spam the workload's stderr — surface the gap under
                // SENTRY_TRACE=1 (the diagnostic loop) only.
                if TRACE {
                    logn(
                        b"[supervisor] UNIMPLEMENTED delegated syscall nr=",
                        other,
                        b" -> ENOSYS\n",
                    );
                }
                -38
            }
        }
    }
}

// The MAIN cell process (slot 0). The sandbox lives until THIS process exits;
// forked children dying are reaped but do not tear the sandbox down.
static MAIN_CELL_PID: AtomicU32 = AtomicU32::new(0);

/// POOL-PATH straggler sweep (Part D). After a pooled command's foreground cell has
/// been wait_exit'd, SIGKILL every remaining STRAGGLER cell process — a slot-owning
/// pid that isn't `foreground` (and isn't init/the supervisor) — then REAP and
/// reclaim its slot. A straggler is e.g. a `( sleep & )` orphan whose parent
/// subshell already exited: it sits mid-delegated-syscall (blocked on the ring
/// futex), so its servicer holds the exec_capture pipe's write-end and the reader
/// never EOFs even though the foreground command already returned. The per-exec
/// supervisor closed this via on_sigchld at MAIN-cell death (d719712); this extends
/// the same EOF invariant to the warm/pool path (persistent_supervisor_main has no
/// SIGCHLD handler and reaps only via per-request wait_exit). After the SIGKILL the
/// straggler is dead, so free_slots_of reclaims its slot (quiescent → freed
/// outright; wedged → cancel-on-reap, Part C). NOT a signal-handler path — the pool
/// loop calls this between commands, so the blocking waitpid is safe. `foreground`
/// is the pid to SPARE (0 = none — the pool already wait_exit'd the foreground).
/// Live, DETACHED warm-daemon instances (L0b): pids acquired via
/// `Ctrl::AcquireRunning` that must survive across subsequent client execs. Both the
/// foreground arg and this set are spared by `sweep_stragglers`. Mutated only on the
/// supervisor's control thread (AcquireRunning adds, Release removes), so contention
/// is nil; the lock is just for the snapshot `sweep_stragglers` takes.
fn running_instances() -> &'static std::sync::Mutex<Vec<i32>> {
    static R: std::sync::OnceLock<std::sync::Mutex<Vec<i32>>> = std::sync::OnceLock::new();
    R.get_or_init(|| std::sync::Mutex::new(Vec::new()))
}

fn live_running_instances_snapshot() -> Vec<i32> {
    sweep_dead_cells();
    let mut running = running_instances().lock().unwrap();
    running.retain(|&pid| pid > 1 && !pid_is_dead(pid));
    running.clone()
}

fn preserve_stragglers_as_running(foreground: i32) {
    let protected = running_instances().lock().unwrap().clone();
    let mut additions = Vec::new();
    unsafe {
        for i in 0..MAX_SLOTS as u64 {
            let p = std::ptr::read_volatile(std::ptr::addr_of!((*ring_at(i)).pid));
            if p > 1 && p != foreground && !protected.contains(&p) && !additions.contains(&p) {
                additions.push(p);
            }
        }
    }
    if additions.is_empty() {
        return;
    }
    let mut running = running_instances().lock().unwrap();
    for pid in additions {
        if pid > 1 && !pid_is_dead(pid) && !running.contains(&pid) {
            fd_detach_stdio_to_dev_null(pid);
            running.push(pid);
        }
    }
}

fn sweep_stragglers(foreground: i32) {
    // Snapshot the protected detached-instance set: a warm daemon acquired via
    // `acquire_running` owns a ring slot (p > 1) but MUST NOT be swept between client
    // execs — only an explicit `release` kills it.
    let protected = running_instances().lock().unwrap().clone();
    unsafe {
        let mut victims: [i32; MAX_SLOTS] = [0; MAX_SLOTS];
        let mut n = 0usize;
        for i in 0..MAX_SLOTS as u64 {
            let p = std::ptr::read_volatile(std::ptr::addr_of!((*ring_at(i)).pid));
            let protected_tree = proctree::slot_descends_from_any(i, p, &protected);
            if p > 1 && p != foreground && !protected.contains(&p) && !protected_tree {
                libc::kill(p, libc::SIGKILL);
                // record DISTINCT pids so we reap+reclaim each once below.
                if !victims[..n].contains(&p) {
                    victims[n] = p;
                    n += 1;
                }
            }
        }
        // Reap the SIGKILL'd stragglers (so they aren't zombies) and reclaim their
        // ring slots. A straggler with no in-cell waiter never sent CTL_REAP, so do
        // the bookkeeping here: it is definitively dead now (we just SIGKILL'd +
        // wait4'd it), the same liveness CTL_REAP guarantees free_slots_of.
        for &p in &victims[..n] {
            let mut st: c_int = 0;
            libc::waitpid(p, &mut st, 0);
            // FULL reap (fd_drop + DRAIN pending[slot] + free slots), not just
            // fd_drop. A straggler that never made a delegated request — e.g.
            // `( sleep 100 & )`, whose `sleep` only does cell-local nanosleep —
            // never ran fd_adopt, so its inherited stdout/stderr dups (INCLUDING
            // the exec_capture pipe's write-end, stashed by CTL_FORK_TABLE at the
            // fork) still sit in pending[slot], NOT fdt[p]. fd_drop(p) alone closes
            // fdt[p] (empty here) and leaks the pending dups, so the capture pipe
            // never EOFs and the library's reader hangs ~forever. reap_dead_pid
            // drains pending too (it does NOT waitpid — we already did).
            reap_dead_pid(p);
        }
    }
}

/// SIGCHLD: reap dead cell processes. When the MAIN cell exits (or is killed by
/// the seccomp wall), the sandbox is done — exit the supervisor with its status.
/// A forked subprocess exiting is reaped and otherwise ignored (normal Unix).
extern "C" fn on_sigchld(_sig: c_int) {
    loop {
        let mut status: c_int = 0;
        let r = unsafe { libc::waitpid(-1, &mut status, libc::WNOHANG) };
        if r <= 0 {
            return;
        }
        if r as u32 != MAIN_CELL_PID.load(Ordering::Relaxed) {
            continue; // a forked subprocess — reap and move on
        }
        // The MAIN cell (the foreground command) is done, so the exec is done.
        // SIGKILL any STRAGGLER cell process before we tear our servicers down —
        // e.g. a `( sleep & )` orphan whose parent subshell already exited. Such a
        // straggler is mid-delegated-syscall (blocked on the ring futex); once our
        // servicer threads die with this _exit it would wedge there FOREVER,
        // holding the exec_capture pipe's write-end and hanging the reader. We
        // can't `killpg` our own group (we lead it; that would SIGKILL us too and
        // lose the cell's exit status), so we sweep by the cell pids the ring
        // slots already record. kill() is async-signal-safe; a stale slot pid is
        // the pre-existing pid-recycle window (see CTL_REAP), acceptable here. This
        // is the SIGKILL-only sweep (we `_exit` right after, so no reap/reclaim is
        // needed — that's the pool path's job, see sweep_stragglers / Part D).
        unsafe {
            for i in 0..MAX_SLOTS as u64 {
                let p = std::ptr::read_volatile(std::ptr::addr_of!((*ring_at(i)).pid));
                if p > 1 && p != r {
                    libc::kill(p, libc::SIGKILL);
                }
            }
        }
        // Warm/zygote bench: the warm cell stashed its restore-latency result in
        // the shared ring page. Report it (raw write; signal-safe) before exiting.
        if WARM_MODE.load(Ordering::Relaxed) {
            unsafe {
                if !ZYG.is_null() {
                    logn(b"[zygote] restores=", ZYG_RESTORES as i64, b"");
                    logn(b" first_exit_code=", (*ZYG).exit as i64, b"");
                    logn(b" mean_ns=", (*ZYG).mean_ns as i64, b"");
                    logn(b" min_ns=", (*ZYG).done as i64, b"\n");
                }
            }
        }
        // Classify the cell's death + report the STRUCTURED REASON over the status
        // pipe (a no-op if the caller didn't ask for one) BEFORE we `_exit`, which
        // flattens a signal-kill to the integer `128+sig` and loses the distinction
        // between a seccomp-wall kill and a guest that exited that same number.
        let (code, err) = classify_status(status);
        if matches!(err, Some(SentryError::SeccompViolation { .. })) {
            log(b"[supervisor] cell was KILLED by the seccomp wall (SIGSYS) on a forbidden syscall.\n");
        }
        report_status(err);
        unsafe { libc::_exit(code) };
    }
}

thread_local! {
    /// The ring slot THIS servicer thread owns (set once in servicer_loop). Read
    /// by the cancel-aware blocking host wrapper (`host_cancellable`) so a stranded
    /// servicer interrupted by SIGURG can find its own `(*r).cancel` flag and tear
    /// its slot down. Plain Cell — only this thread reads/writes it.
    static SERVICER_RING: std::cell::Cell<*mut Ring> = const { std::cell::Cell::new(std::ptr::null_mut()) };
    /// Per-servicer reusable bounce buffer for delegated read/write/getrandom — the
    /// data staging area between the cell's memory (process_vm_*) and the host
    /// syscall. Reused across delegations so the hot I/O path doesn't malloc+free a
    /// fresh `vec![0u8; len]` every syscall (a bigger relative cost now that the
    /// rewrite fast-path is the default). `service()` is non-reentrant per servicer
    /// thread (it forwards to `host*`, never back into `service`), so the borrow in
    /// `with_scratch` can never nest.
    static SCRATCH: std::cell::RefCell<Vec<u8>> = const { std::cell::RefCell::new(Vec::new()) };
}
/// Largest buffer kept in the per-servicer scratch. The common delegated I/O
/// (pipe/socket reads/writes, getrandom) is well under this; a rare larger transfer
/// (up to CAP = 64 MiB) takes a one-off `vec!` rather than pinning a huge buffer in
/// every servicer thread forever (48 threads x 64 MiB would be ~3 GiB).
const SCRATCH_MAX: usize = 1024 * 1024;
/// Run `f` with a scratch slice of exactly `len` bytes. For `len <= SCRATCH_MAX` this
/// is the thread-local `SCRATCH` (grown as needed, never shrunk — converges to the
/// servicer's high-water mark, so the hot path stops malloc/free-ing per syscall);
/// larger requests fall back to a one-off allocation (no retention). See `SCRATCH`.
fn with_scratch<R>(len: usize, f: impl FnOnce(&mut [u8]) -> R) -> R {
    if len > SCRATCH_MAX {
        let mut v = vec![0u8; len];
        return f(&mut v);
    }
    SCRATCH.with(|s| {
        let mut b = s.borrow_mut();
        if b.len() < len {
            b.resize(len, 0);
        }
        f(&mut b[..len])
    })
}

/// SENTRY_ENVSCAN=1: supervisor-side EXTERNAL watchpoint for the DATABASE_URL 108→59
/// truncation. In-cell `delegate()` diagnostics get optimizer-DCE'd; the supervisor's
/// own code is not, so it reads each live cell's memory via /proc/<pid>/mem (no cell
/// code at all), finds the FULL DATABASE_URL value, then tight-polls the byte that
/// becomes the stray NUL. On the flip it dumps that cell's threads' current syscall+PC
/// (/proc/<pid>/task/*/syscall) — the corrupting context. Read a single byte at `addr`.
fn pvm_read(pid: i32, addr: u64, buf: &mut [u8]) -> bool {
    let local = libc::iovec {
        iov_base: buf.as_mut_ptr() as *mut libc::c_void,
        iov_len: buf.len(),
    };
    let remote = libc::iovec {
        iov_base: addr as *mut libc::c_void,
        iov_len: buf.len(),
    };
    let n = unsafe {
        host(
            SYS_PROCESS_VM_READV,
            pid as u64,
            &local as *const _ as u64,
            1,
            &remote as *const _ as u64,
            1,
            0,
        )
    };
    n == buf.len() as i64
}
/// Like pvm_read but returns the byte count actually read (process_vm_readv stops at the first
/// unreadable page and returns the committed prefix). Used to chunk-scan V8's giant heap
/// reservation, most of which is uncommitted (a partial/zero read just means "skip this chunk").
fn pvm_read_n(pid: i32, addr: u64, buf: &mut [u8]) -> i64 {
    let local = libc::iovec {
        iov_base: buf.as_mut_ptr() as *mut libc::c_void,
        iov_len: buf.len(),
    };
    let remote = libc::iovec {
        iov_base: addr as *mut libc::c_void,
        iov_len: buf.len(),
    };
    unsafe {
        host(
            SYS_PROCESS_VM_READV,
            pid as u64,
            &local as *const _ as u64,
            1,
            &remote as *const _ as u64,
            1,
            0,
        )
    }
}
fn envscan_byte(pid: i32, addr: u64) -> Option<u8> {
    let mut b = [0u8; 1];
    if pvm_read(pid, addr, &mut b) {
        Some(b[0])
    } else {
        None
    }
}
/// Scan a cell's rw regions for `needle` via process_vm_readv; push the first-byte address of
/// EVERY occurrence (capped) into `out`. The value lives in multiple copies (stack environ +
/// brk-heap buffer node builds for the spawn); only one gets corrupted, so we must watch ALL.
fn envscan_find_all(pid: i32, needle: &[u8], out: &mut Vec<u64>) {
    let maps = match std::fs::read_to_string(format!("/proc/{pid}/maps")) {
        Ok(m) => m,
        Err(_) => return,
    };
    let mut scanned: u64 = 0;
    for line in maps.lines() {
        let mut parts = line.split_whitespace();
        let range = match parts.next() {
            Some(r) => r,
            None => continue,
        };
        let perms = parts.next().unwrap_or("");
        if !perms.starts_with("rw") {
            continue;
        }
        let mut rp = range.split('-');
        let start = rp.next().and_then(|s| u64::from_str_radix(s, 16).ok());
        let end = rp.next().and_then(|s| u64::from_str_radix(s, 16).ok());
        let (start, end) = match (start, end) {
            (Some(s), Some(e)) if e > s => (s, e),
            _ => continue,
        };
        let len = (end - start) as usize;
        // The downstream env buffer/stack copy lives in the high stack/brk range
        // (>=0x7000_0000_0000). The ORIGINATING object — dbConn's V8 String, whose length
        // field is corrupted 108->59 — lives in V8's HEAP (the arena, observed at ~0x1c0-0x1ff
        // GiB just under WINDOW_FLOOR 0x20_0000_0000 in the VMWRITE_ARENA capture). Scan BOTH.
        let in_brk_stack = start >= 0x7000_0000_0000;
        let in_arena = start >= 0x1000_0000_00 && start < 0x20_0000_0000;
        if len == 0 || !(in_brk_stack || in_arena) {
            continue;
        }
        // V8's heap is one HUGE reservation (>96MB, mostly uncommitted under the pointer-cage
        // sandbox). The prior 96MB cap skipped it entirely, so dbConn (a V8 String) was never
        // found — every hit landed in the small stack/brk copies. Chunk-scan giant ARENA regions
        // with the partial-read so committed string pages get scanned and uncommitted ones are
        // cheaply skipped. (Giant brk/stack regions stay capped — the value's copies there are
        // small-region.)
        if len > 96 * 1024 * 1024 {
            if !in_arena {
                continue;
            }
            let chunk = 4 * 1024 * 1024usize;
            let mut buf = vec![0u8; chunk];
            let mut off = 0u64;
            let mut region_scanned = 0u64;
            let region_cap = 3 * 1024 * 1024 * 1024u64; // 3 GiB of committed pages per region
                                                        // Also bound the SPAN walked, so a mostly-uncommitted 1 TB reservation can't make us
                                                        // iterate forever (committed string pages cluster in the low part of V8's heap).
            let span_cap = 96 * 1024 * 1024 * 1024u64; // walk at most 96 GiB of the reservation
            while off < (len as u64).min(span_cap) && region_scanned < region_cap {
                let want = chunk.min((len as u64 - off) as usize);
                let n = pvm_read_n(pid, start + off, &mut buf[..want]);
                if n > 0 {
                    let nn = n as usize;
                    region_scanned += nn as u64;
                    let mut i = 0usize;
                    while i + needle.len() <= nn {
                        if &buf[i..i + needle.len()] == needle {
                            out.push(start + off + i as u64);
                            if out.len() >= 24 {
                                return;
                            }
                            i += needle.len();
                        } else {
                            i += 1;
                        }
                    }
                }
                // overlap by needle.len() so a match straddling a chunk boundary is not missed
                off += (chunk - needle.len()) as u64;
            }
            continue;
        }
        if scanned > 1536 * 1024 * 1024 {
            break;
        }
        scanned += len as u64;
        let mut buf = vec![0u8; len];
        if !pvm_read(pid, start, &mut buf) {
            continue;
        }
        let mut i = 0usize;
        while i + needle.len() <= buf.len() {
            if &buf[i..i + needle.len()] == needle {
                out.push(start + i as u64);
                if out.len() >= 24 {
                    return;
                }
                i += needle.len();
            } else {
                i += 1;
            }
        }
    }
}
fn envscan_comm(pid: i32) -> String {
    std::fs::read_to_string(format!("/proc/{pid}/comm"))
        .map(|c| c.trim().to_string())
        .unwrap_or_default()
}
/// Resolve `rip` against the cell's /proc/pid/maps: returns "<basename>+0x<off>" for a file-backed
/// region (e.g. node, libc — off = file offset of the instruction), or "[anon-rwx]+0x<off>" /
/// "[anon-r-x]+0x<off>" for an anonymous region (V8's JIT code is anon r-x/rwx). Lets us tell a
/// node `.text` builtin from JITted code from a libc routine without a post-hoc dead-cell autopsy.
fn envscan_sym(pid: i32, rip: u64) -> String {
    let maps = match std::fs::read_to_string(format!("/proc/{pid}/maps")) {
        Ok(m) => m,
        Err(_) => return "noproc".to_string(),
    };
    for line in maps.lines() {
        let mut parts = line.split_whitespace();
        let range = parts.next().unwrap_or("");
        let perms = parts.next().unwrap_or("");
        let mut rp = range.split('-');
        let start = rp.next().and_then(|s| u64::from_str_radix(s, 16).ok());
        let end = rp.next().and_then(|s| u64::from_str_radix(s, 16).ok());
        if let (Some(s), Some(e)) = (start, end) {
            if rip >= s && rip < e {
                let off = rip - s;
                let path = line.split_whitespace().nth(5).unwrap_or("");
                let name = if path.is_empty() {
                    format!("[anon-{perms}]")
                } else {
                    path.rsplit('/').next().unwrap_or(path).to_string()
                };
                return format!("{name}+0x{off:x}({perms})");
            }
        }
    }
    "unmapped".to_string()
}
// perf_event_open a HW WRITE-breakpoint (8 bytes) on (tid, addr). NON-intrusive: node never
// stops; the kernel records a SAMPLE carrying the writing instruction's IP into an mmap'd
// ring. Used to catch the in-cell stray that corrupts the glibc chunk SIZE field (at
// DATABASE_URL_data-8) → the chunk overlap → the truncation. Needs perf_event_paranoid<=1 or
// -1 (es_run.sh sets -1). Returns fd or -1.
fn perf_open_wbp(tid: i32, addr: u64) -> i32 {
    const PERF_SAMPLE_IP: u64 = 1;
    const PERF_SAMPLE_REGS_USER: u64 = 1 << 12;
    const PERF_REG_X86_AX: u64 = 1 << 0;
    const PERF_REG_X86_CX: u64 = 1 << 2;
    const PERF_REG_X86_DX: u64 = 1 << 3;
    const PERF_REG_X86_SI: u64 = 1 << 4;
    const PERF_REG_X86_DI: u64 = 1 << 5;
    const PERF_REG_X86_BP: u64 = 1 << 6;
    const PERF_REG_X86_SP: u64 = 1 << 7;
    const PERF_REG_X86_IP: u64 = 1 << 8;
    const PERF_REGS: u64 = PERF_REG_X86_AX
        | PERF_REG_X86_CX
        | PERF_REG_X86_DX
        | PERF_REG_X86_SI
        | PERF_REG_X86_DI
        | PERF_REG_X86_BP
        | PERF_REG_X86_SP
        | PERF_REG_X86_IP;
    let mut attr = [0u8; 128];
    attr[0..4].copy_from_slice(&5u32.to_le_bytes()); // type = PERF_TYPE_BREAKPOINT
    attr[4..8].copy_from_slice(&128u32.to_le_bytes()); // size
    attr[16..24].copy_from_slice(&1u64.to_le_bytes()); // sample_period = 1
    attr[24..32].copy_from_slice(&(PERF_SAMPLE_IP | PERF_SAMPLE_REGS_USER).to_le_bytes());
    attr[40..48].copy_from_slice(&1u64.to_le_bytes()); // flags: disabled=1 (enable via ioctl)
    attr[52..56].copy_from_slice(&2u32.to_le_bytes()); // bp_type = HW_BREAKPOINT_W
    attr[56..64].copy_from_slice(&addr.to_le_bytes()); // bp_addr (config1)
    attr[64..72].copy_from_slice(&1u64.to_le_bytes()); // bp_len = HW_BREAKPOINT_LEN_1 (config2)
    attr[80..88].copy_from_slice(&PERF_REGS.to_le_bytes()); // sample_regs_user
    let fd = unsafe {
        host(
            298, // SYS_perf_event_open
            attr.as_ptr() as u64,
            tid as u64,
            (-1i64) as u64, // cpu = -1 (any)
            (-1i64) as u64, // group_fd = -1
            0,              // flags
            0,
        )
    };
    fd as i32
}
fn perf_mmap_ring(fd: i32) -> *mut u8 {
    let p = unsafe {
        host(
            SYS_MMAP,
            0,
            8192, // 1 metadata page + 1 data page
            (libc::PROT_READ | libc::PROT_WRITE) as u64,
            libc::MAP_SHARED as u64,
            fd as u64,
            0,
        )
    };
    if p < 0 {
        std::ptr::null_mut()
    } else {
        p as *mut u8
    }
}
#[derive(Clone, Copy)]
struct PerfRegs {
    ax: u64,
    cx: u64,
    dx: u64,
    si: u64,
    di: u64,
    bp: u64,
    sp: u64,
    ip: u64,
}

#[derive(Clone, Copy)]
struct PerfSample {
    ip: u64,
    regs: Option<PerfRegs>,
}

/// Poll the perf ring; if a SAMPLE record is present, return its IP/registers.
fn perf_read_sample(base: *mut u8) -> Option<PerfSample> {
    if base.is_null() {
        return None;
    }
    unsafe {
        let head = std::ptr::read_volatile(base.add(1024) as *const u64);
        let tail = std::ptr::read_volatile(base.add(1032) as *const u64);
        if head <= tail {
            return None;
        }
        let data_off = std::ptr::read_volatile(base.add(1040) as *const u64) as usize;
        let data_size = std::ptr::read_volatile(base.add(1048) as *const u64) as usize;
        if data_size == 0 {
            return None;
        }
        let rec = base.add(data_off + (tail as usize % data_size));
        let rtype = std::ptr::read_volatile(rec as *const u32);
        let sample = if rtype == 9 {
            // PERF_RECORD_SAMPLE: 8-byte header then the IP (sample_type=IP)
            let ip = std::ptr::read_volatile(rec.add(8) as *const u64);
            let abi = std::ptr::read_volatile(rec.add(16) as *const u64);
            let regs = if abi != 0 {
                Some(PerfRegs {
                    ax: std::ptr::read_volatile(rec.add(24) as *const u64),
                    cx: std::ptr::read_volatile(rec.add(32) as *const u64),
                    dx: std::ptr::read_volatile(rec.add(40) as *const u64),
                    si: std::ptr::read_volatile(rec.add(48) as *const u64),
                    di: std::ptr::read_volatile(rec.add(56) as *const u64),
                    bp: std::ptr::read_volatile(rec.add(64) as *const u64),
                    sp: std::ptr::read_volatile(rec.add(72) as *const u64),
                    ip: std::ptr::read_volatile(rec.add(80) as *const u64),
                })
            } else {
                None
            };
            Some(PerfSample { ip, regs })
        } else {
            None
        };
        std::ptr::write_volatile(base.add(1032) as *mut u64, head); // consume
        sample
    }
}

/// Poll the perf ring; if a SAMPLE record is present, return its IP. Consumes the ring.
fn perf_read_ip(base: *mut u8) -> Option<u64> {
    perf_read_sample(base).map(|s| s.ip)
}
fn env_scan_watchdog() -> ! {
    // The value: postgresql://root@localhost:5432/shopify_test_harness_self_test_offline_…
    // The byte that becomes a stray NUL is the 't' of "test_offline" — right after this
    // needle (= value-offset 59). Watch (needle_addr + needle.len()). We scan only node/npm
    // cells (the spawn chain that corrupts the env) to keep process_vm_readv cheap, and emit
    // the heartbeat TICK at the TOP of the loop so liveness is visible regardless of scan cost.
    let needle: &[u8] = b"shopify_test_harness_self_";
    let nl = needle.len() as u64;
    let mut watched: std::collections::HashSet<(i32, u64)> = std::collections::HashSet::new();
    let mut tries: std::collections::HashMap<i32, u32> = std::collections::HashMap::new();
    // Perf HW write-breakpoints on dbConn's V8 length field (≤4 — the CPU has 4 debug regs).
    // (ring_ptr, watched_addr, pid). On a write we get the corrupting IP, symbolized live.
    let mut bps: Vec<(*mut u8, u64, i32)> = Vec::new();
    let mut tick: u64 = 0;
    loop {
        tick += 1;
        // cheap census (no memory reads): which live cells are node/npm?
        let mut slots_set = 0u32;
        let mut pids = 0u32;
        let mut nodes: Vec<i32> = Vec::new();
        for i in 0..(MAX_SLOTS as u64) {
            if !slot_is_set(i) {
                continue;
            }
            slots_set += 1;
            let pid = unsafe { std::ptr::read_volatile(std::ptr::addr_of!((*ring_at(i)).pid)) };
            if pid <= 1 {
                continue;
            }
            pids += 1;
            let c = envscan_comm(pid);
            if c == "node" || c.starts_with("npm") || c == "npx" {
                nodes.push(pid);
            }
        }
        if tick <= 12 || tick % 120 == 1 {
            let mut t: Vec<u8> = Vec::with_capacity(96);
            t.extend_from_slice(b"ENVSCAN_TICK tick=");
            t.extend_from_slice(tick.to_string().as_bytes());
            t.extend_from_slice(b" slots=");
            t.extend_from_slice(slots_set.to_string().as_bytes());
            t.extend_from_slice(b" pids=");
            t.extend_from_slice(pids.to_string().as_bytes());
            t.extend_from_slice(b" nodes=");
            t.extend_from_slice((nodes.len() as u32).to_string().as_bytes());
            t.extend_from_slice(b" watched=");
            t.extend_from_slice((watched.len() as u32).to_string().as_bytes());
            ipc_logf_raw(&t);
        }
        // scan node cells (bounded per-pid re-scans); arm EVERY value copy, not just the first
        for &pid in &nodes {
            let t = tries.entry(pid).or_insert(0);
            if *t >= 60 {
                continue;
            }
            *t += 1;
            let mut addrs: Vec<u64> = Vec::new();
            envscan_find_all(pid, needle, &mut addrs);
            for a in addrs {
                let wb = a + nl;
                if watched.contains(&(pid, wb)) {
                    continue;
                }
                if envscan_byte(pid, wb) == Some(b't') {
                    // Choose the breakpoint TARGET. Discriminators 8a/8b exonerated the supervisor
                    // (no vm_write of 59 to the arena; mm handlers clean) — so node/V8 ITSELF
                    // writes dbConn's length 108->59. To catch the ORIGINATING write (not the
                    // downstream env copy), watch the V8 String LENGTH FIELD, not the value byte:
                    //   - ARENA copy (a < WINDOW_FLOOR 0x20_0000_0000) = dbConn's flat V8
                    //     SeqString. Char data starts 33 bytes before the needle; the length u32
                    //     sits just before the data (data-4 pointer-compressed, data-8 otherwise).
                    //     Arm at whichever nearby offset currently reads the correct length 108 —
                    //     that is dbConn's length field, and the next write to it is the bug.
                    //   - non-arena (brk/stack) = the downstream C copy; watch the value byte.
                    let mut sizeaddr = wb;
                    let mut is_len = false;
                    if a < 0x20_0000_0000 {
                        // Read candidate V8 length fields around this arena hit and LOG them, so
                        // even if we can't arm before the corruption we SEE dbConn's V8 length
                        // directly: l-37 = dbConn flat SeqString length (needle at value-offset
                        // 33, length at data-4); l-4 = dbName SeqString length (needle at offset
                        // 0) — expect 75, intact. 108 = pre-corruption (arm to catch 108->59);
                        // 59 = ALREADY corrupted (confirms the length-field hypothesis, no race).
                        let deltas = [37u64, 41, 45, 33, 4, 8];
                        let mut vals = [0u32; 6];
                        for (k, &delta) in deltas.iter().enumerate() {
                            if a >= delta {
                                let mut b4 = [0u8; 4];
                                if pvm_read(pid, a - delta, &mut b4) {
                                    vals[k] = u32::from_le_bytes(b4);
                                }
                            }
                        }
                        let mut m: Vec<u8> = Vec::with_capacity(112);
                        m.extend_from_slice(b"ENVSCAN_ARENA a=0x");
                        m.extend_from_slice(format!("{a:x}").as_bytes());
                        for (k, &delta) in deltas.iter().enumerate() {
                            m.extend_from_slice(b" l-");
                            m.extend_from_slice(delta.to_string().as_bytes());
                            m.extend_from_slice(b"=");
                            m.extend_from_slice(vals[k].to_string().as_bytes());
                        }
                        ipc_logf_raw(&m);
                        let mut found = false;
                        for (k, &delta) in deltas.iter().enumerate() {
                            if vals[k] == 108 {
                                sizeaddr = a - delta;
                                is_len = true;
                                found = true;
                                break;
                            }
                        }
                        if !found {
                            // No length field reading 108 nearby ⇒ either dbConn is already 59
                            // (the ENVSCAN_ARENA line above shows it), or this hit is dbName / the
                            // env-entry / a ConsString. Don't waste a debug reg; the log suffices.
                            continue;
                        }
                    }
                    watched.insert((pid, wb));
                    ipc_logf_raw(
                        &[
                            b"ENVSCAN_ARM pid=".as_slice(),
                            pid.to_string().as_bytes(),
                            b" comm=".as_slice(),
                            envscan_comm(pid).as_bytes(),
                            b" addr=0x".as_slice(),
                            format!("{sizeaddr:x}").as_bytes(),
                            if is_len {
                                b" LENFIELD".as_slice()
                            } else {
                                b" valbyte".as_slice()
                            },
                        ]
                        .concat(),
                    );
                    if bps.len() < 4 {
                        let fd = perf_open_wbp(pid, sizeaddr);
                        if fd >= 0 {
                            unsafe { host(SYS_IOCTL, fd as u64, 0x2400, 0, 0, 0, 0) }; // ENABLE
                            let ring = perf_mmap_ring(fd);
                            ipc_logf_raw(
                                &[
                                    b"ENVSCAN_BPARM pid=".as_slice(),
                                    pid.to_string().as_bytes(),
                                    b" sizeaddr=0x".as_slice(),
                                    format!("{sizeaddr:x}").as_bytes(),
                                    b" fd=".as_slice(),
                                    fd.to_string().as_bytes(),
                                    b" ring=".as_slice(),
                                    if ring.is_null() { b"0" } else { b"1" },
                                ]
                                .concat(),
                            );
                            if !ring.is_null() {
                                bps.push((ring, sizeaddr, pid));
                            }
                        } else {
                            ipc_logf_raw(
                                &[b"ENVSCAN_BPFAIL fd=".as_slice(), fd.to_string().as_bytes()]
                                    .concat(),
                            );
                        }
                    }
                }
            }
        }
        // poll the perf HW-breakpoint rings: a sample = the IP of the instruction that wrote
        // a watched chunk SIZE field (the stray that causes the overlap). THIS is the catch.
        for &(ring, sizeaddr, bppid) in &bps {
            if let Some(rip) = perf_read_ip(ring) {
                // Symbolize the writing IP against the LIVE cell's maps (it's gone once the cell
                // dies). Names the V8 op: node `.text`/builtin, an anonymous JIT page, or libc.
                let region = envscan_sym(bppid, rip);
                ipc_logf_raw(
                    &[
                        b"ENVSCAN_BPRIP rip=0x".as_slice(),
                        format!("{rip:x}").as_bytes(),
                        b" sizeaddr=0x".as_slice(),
                        format!("{sizeaddr:x}").as_bytes(),
                        b" in=".as_slice(),
                        region.as_bytes(),
                    ]
                    .concat(),
                );
            }
        }
        // poll watched bytes for the flip
        let mut hit: Option<(i32, u64, u8)> = None;
        for &(pid, wb) in watched.iter() {
            match envscan_byte(pid, wb) {
                Some(b't') => {}
                Some(other) => {
                    hit = Some((pid, wb, other));
                    break;
                }
                None => {}
            }
        }
        if let Some((pid, wb, val)) = hit {
            let mut out: Vec<u8> = Vec::with_capacity(640);
            out.extend_from_slice(b"ENVSCAN_HIT pid=");
            out.extend_from_slice(pid.to_string().as_bytes());
            out.extend_from_slice(b" comm=");
            out.extend_from_slice(envscan_comm(pid).as_bytes());
            out.extend_from_slice(b" newbyte=");
            out.extend_from_slice((val as u32).to_string().as_bytes());
            out.extend_from_slice(b" addr=0x");
            for sh in (0..16u32).rev() {
                out.push(b"0123456789abcdef"[((wb >> (sh * 4)) & 0xf) as usize]);
            }
            // Dump the env-var region [start("DATABASE_URL="), +128) so we can see WHAT overwrote
            // the value past offset 59 — coherent other-allocation data ⇒ heap overlap/corruption.
            {
                let mut ctx = [0u8; 128];
                let cs = wb.saturating_sub(72);
                if pvm_read(pid, cs, &mut ctx) {
                    // REAL truncation = the value prefix is still intact (only offset 59 flipped),
                    // vs benign free+reuse where the whole buffer (incl. "DATABASE_URL=") is gone.
                    let real = ctx.starts_with(b"DATABASE_URL=postgresql");
                    out.extend_from_slice(if real {
                        b" real=1 ctx=|"
                    } else {
                        b" real=0 ctx=|"
                    });
                    for &c in ctx.iter() {
                        out.push(if (0x20..0x7f).contains(&c) { c } else { b'.' });
                    }
                    out.extend_from_slice(b"|");
                }
            }
            if let Ok(rd) = std::fs::read_dir(format!("/proc/{pid}/task")) {
                for e in rd.flatten() {
                    let tid = e.file_name();
                    let tids = tid.to_string_lossy();
                    if let Ok(sc) =
                        std::fs::read_to_string(format!("/proc/{pid}/task/{tids}/syscall"))
                    {
                        out.extend_from_slice(b" t");
                        out.extend_from_slice(tids.as_bytes());
                        out.push(b'=');
                        out.extend_from_slice(sc.trim().as_bytes());
                    }
                }
            }
            ipc_logf_raw(&out);
            watched.remove(&(pid, wb));
        }
        std::thread::sleep(std::time::Duration::from_micros(120));
    }
}

/// One servicer per ring slot. A blocking syscall here (e.g. `epoll_wait`) stalls
/// only this slot's cell thread, not the others. `process_vm_*` targets the slot's
/// OWNING pid (`ring.pid`) — a fork()'d child runs on a different pid.
fn servicer_loop(slot: u64) -> ! {
    // DIAGNOSTIC: spawn the external DATABASE_URL-truncation watchpoint ONCE, from whichever
    // supervisor path first reaches a servicer (cold supervisor_main OR the warm/napi restore
    // at the spawn(|| servicer_loop(0)) site) — a supervisor_main-only spawn missed the
    // warm-restore path the conformance actually uses, so nothing was ever scanning.
    if ENVSCAN.load(Ordering::Relaxed) {
        static SCAN_ONCE: std::sync::Once = std::sync::Once::new();
        SCAN_ONCE.call_once(|| {
            ipc_logf_raw(b"ENVSCAN_SPAWN_LAZY");
            let _ = std::thread::Builder::new().spawn(env_scan_watchdog);
        });
    }
    let r = ring_at(slot);
    let req = ring_word(unsafe { std::ptr::addr_of_mut!((*r).request) });
    let resp = ring_word(unsafe { std::ptr::addr_of_mut!((*r).response) });
    // Record this slot for the cancel-aware blocking wrapper, and create this
    // servicer's CANCEL EVENTFD: every blocking host fd-wait polls it alongside the
    // target, so free_slots_of can wake us (race-free, level-triggered) to abandon a
    // syscall whose cell was reaped. Created NONBLOCK|CLOEXEC; -1 on failure (then the
    // wrapper degrades to a plain retrying host(), i.e. no cancellation — never wrong).
    SERVICER_RING.with(|c| c.set(r));
    let efd = unsafe { host(SYS_EVENTFD2, 0, EFD_NONBLOCK_CLOEXEC, 0, 0, 0, 0) } as i32;
    let intr_efd = unsafe { host(SYS_EVENTFD2, 0, EFD_NONBLOCK_CLOEXEC, 0, 0, 0, 0) } as i32;
    unsafe { std::ptr::write_volatile(std::ptr::addr_of_mut!((*r).cancel_efd), efd) };
    unsafe { std::ptr::write_volatile(std::ptr::addr_of_mut!((*r).intr_efd), intr_efd) };
    unsafe { std::ptr::write_volatile(std::ptr::addr_of_mut!((*r).cancel_pending), 0) };
    unsafe { std::ptr::write_volatile(std::ptr::addr_of_mut!((*r).intr_pending), 0) };
    let mut last = 0u32;
    loop {
        let mut spins = 0u32;
        loop {
            let cur = req.load(Ordering::Acquire);
            if cur != last {
                break;
            }
            // Spin for the budget (catches a back-to-back next request without a
            // context switch); block on the futex only once it's spent.
            if spins < RING_SPIN {
                spins += 1;
                std::hint::spin_loop();
                continue;
            }
            unsafe {
                host(
                    SYS_FUTEX,
                    req.as_ptr() as u64,
                    FUTEX_WAIT,
                    last as u64,
                    0,
                    0,
                    0,
                )
            };
        }
        let k = req.load(Ordering::Acquire);
        // DRAIN any STALE cancel / guest-interrupt token before this fresh request's
        // (possibly blocking) service runs. Tokens are wakeups for an in-flight wait;
        // if the wait returned normally or the signal was already delivered, the token
        // can linger. A later unrelated read/poll must not see that stale wakeup as a
        // fresh cancel/EINTR. Eventfds are NONBLOCK, so empty drains are no-ops.
        unsafe {
            let mut drain = [0u8; 8];
            if std::ptr::read_volatile(std::ptr::addr_of!((*r).cancel_pending)) != 0 {
                host(SYS_READ, efd as u64, drain.as_mut_ptr() as u64, 8, 0, 0, 0);
                std::ptr::write_volatile(std::ptr::addr_of_mut!((*r).cancel_pending), 0);
            }
            if std::ptr::read_volatile(std::ptr::addr_of!((*r).intr_pending)) != 0 {
                host(
                    SYS_READ,
                    intr_efd as u64,
                    drain.as_mut_ptr() as u64,
                    8,
                    0,
                    0,
                    0,
                );
                std::ptr::write_volatile(std::ptr::addr_of_mut!((*r).intr_pending), 0);
            }
            std::ptr::write_volatile(
                std::ptr::addr_of_mut!((*r).cancel_mode),
                CANCEL_REAP_PROCESS,
            );
        }
        let (nr, args) = unsafe {
            let nr = std::ptr::read_volatile(std::ptr::addr_of!((*r).nr));
            let ap = std::ptr::addr_of!((*r).args) as *const u64;
            (
                nr,
                [
                    ap.read_volatile(),
                    ap.add(1).read_volatile(),
                    ap.add(2).read_volatile(),
                    ap.add(3).read_volatile(),
                    ap.add(4).read_volatile(),
                    ap.add(5).read_volatile(),
                ],
            )
        };
        // The pid owning this slot (set by the cell/fork child) — what the
        // supervisor's process_vm_* must target.
        let pid = unsafe { std::ptr::read_volatile(std::ptr::addr_of!((*r).pid)) };
        if unsafe { TRACE } {
            logn(b"  [s", slot as i64, b"");
            logn(b" pid=", pid as i64, b"");
            logn(b"] nr=", nr, b"");
            logn(b" a=", args[0] as i64, b"");
            logn(b" b=", args[1] as i64, b"\n");
        }
        // First request from a fork/spawn child: adopt the fd-table snapshot the
        // parent took (synchronously) before forking.
        let fp = unsafe { std::ptr::read_volatile(std::ptr::addr_of!((*r).fork_parent)) };
        if fp != 0 {
            fd_adopt(slot, pid);
            unsafe { std::ptr::write_volatile(std::ptr::addr_of_mut!((*r).fork_parent), 0) };
        }
        let ret = service(
            slot, pid, nr, args[0], args[1], args[2], args[3], args[4], args[5],
        );
        // Skip the high-volume startup/file noise (close, prlimit64, openat, fstat,
        // pread64, newfstatat, mmap/munmap/mprotect, lseek). Logging these throttles
        // the post-fork brute close-fds loop (base::CloseSuperfluousFds: prlimit64 +
        // close per fd up to RLIMIT_NOFILE) so badly the child never finishes pre-exec
        // setup — hiding the REAL post-connect stall. We want the socket/wait/futex
        // syscalls of the steady state.
        let noisy = matches!(nr, 3 | 302 | 257 | 5 | 17 | 262 | 11 | 10 | 8);
        if unsafe { SYSCALLTRACE } && ret != CANCEL_SENTINEL && !noisy {
            let rip = unsafe { std::ptr::read_volatile(std::ptr::addr_of!((*r).dbg_rip)) };
            let caller = unsafe { std::ptr::read_volatile(std::ptr::addr_of!((*r).dbg_caller)) };
            ipc_logf(
                &[
                    (b"DSYS pid=", pid as i64),
                    (b"slot=", slot as i64),
                    (b"nr=", nr),
                    (b"a=", args[0] as i64),
                    (b"b=", args[1] as i64),
                    (b"c=", args[2] as i64),
                    (b"ret=", ret),
                    (b"rip=", rip as i64),
                    (b"caller=", caller as i64),
                ],
                &[],
            );
        }
        // Diagnostic: surface every delegated syscall that returns an UNEXPECTED
        // error (not the normal nonblocking/interrupted/probe ones) — catches a
        // handshake-breaking syscall (epoll/eventfd/getsockopt/fcntl/...) that
        // succeeds at the byte level elsewhere. Gated by SENTRY_IPCTRACE.
        if unsafe { IPCTRACE } && ret < 0 && ret != CANCEL_SENTINEL {
            let e = -ret;
            // skip: EAGAIN(11) EINTR(4) EINPROGRESS(115) ENOENT(2) EEXIST(17)
            //       ENOTTY(25) ECHILD(10) ESRCH(3) ETIMEDOUT(110)
            if !matches!(e, 11 | 4 | 115 | 2 | 17 | 25 | 10 | 3 | 110) {
                ipc_logf(
                    &[
                        (b"DELGERR pid=", pid as i64),
                        (b"nr=", nr),
                        (b"a=", args[0] as i64),
                        (b"b=", args[1] as i64),
                        (b"c=", args[2] as i64),
                        (b"d=", args[3] as i64),
                        (b"e=", args[4] as i64),
                        (b"f=", args[5] as i64),
                        (b"ret=", ret),
                    ],
                    &[],
                );
            }
        }
        // CANCEL_SENTINEL: this servicer was blocked in a host fd-wait for a cell that
        // DIED wedged; free_slots_of wrote our cancel eventfd, the blocking poll saw it
        // and abandoned the call. Tear our OWN slot down — drain the eventfd, close the
        // dead pid's host fds, free the slot — WITHOUT a response store (the peer is
        // dead, nobody waits; no torn ring). Advance `last` past this stale request so
        // we don't re-service it, then go back to waiting (the slot may be re-leased to
        // a fresh cell, which re-keys it via its own request gen).
        if ret == CANCEL_SENTINEL {
            let cancel_mode =
                unsafe { std::ptr::read_volatile(std::ptr::addr_of!((*r).cancel_mode)) };
            if cancel_mode != CANCEL_SLOT_ONLY {
                shm_reap_pid(pid);
                fd_drop(pid);
            }
            unsafe {
                let mut drain = [0u8; 8];
                host(SYS_READ, efd as u64, drain.as_mut_ptr() as u64, 8, 0, 0, 0);
                std::ptr::write_volatile(std::ptr::addr_of_mut!((*r).cancel_pending), 0);
                clear_ring_owner(slot);
                std::ptr::write_volatile(
                    std::ptr::addr_of_mut!((*r).cancel_mode),
                    CANCEL_REAP_PROCESS,
                );
            }
            if slot != 0 {
                free_slot(slot as u32);
            }
            last = k;
            continue;
        }
        unsafe { std::ptr::write_volatile(std::ptr::addr_of_mut!((*r).ret), ret) };
        resp.store(k, Ordering::Release);
        unsafe { host(SYS_FUTEX, resp.as_ptr() as u64, FUTEX_WAKE, 1, 0, 0, 0) };
        last = k;
        // CTL_REAP: recycle the dead process's slots AFTER responding, so its
        // own slot (this one, if it called exit_group) shows request == response
        // and is freed too. A re-REAP after slot reuse is safe: the reused
        // slot's pid no longer matches. (Theoretical hole: kernel pid-number
        // recycling within a sandbox could alias a dead pid — pids wrap at
        // pid_max, so this is acceptable until namespaces land in M5.)
        if nr == CTL_REAP {
            free_slots_of(args[0] as i32);
        }
    }
}

/// Which ring slots already have a servicer thread (bit per slot; bit 0 = the
/// supervisor's main thread). Supervisor-local (only supervisor threads spawn
/// servicers), so a plain `Mutex` suffices.
fn servicer_spawned() -> &'static Mutex<[bool; MAX_SLOTS]> {
    static S: OnceLock<Mutex<[bool; MAX_SLOTS]>> = OnceLock::new();
    // One flag per ring slot. MUST be MAX_SLOTS-wide: this was a `u64` bitmask
    // (`1u64 << slot`), which after MAX_SLOTS was raised 64→512 aliased every slot
    // modulo 64 (slot 64→bit 0, 66→bit 2, …). `ensure_servicer` then saw the aliased
    // LOW slot's bit already set and falsely reported slots ≥64 as already-serviced,
    // so it never spawned their servicer — their delegated syscalls (e.g. a new
    // MessagePumpEpoll thread's epoll_create1) sat in the ring forever. Chromium
    // spawns 65+ threads, so every renderer wedged at pump bring-up → the in-guest
    // "renderer hang" under sentry. Per-slot flags make it correct for all 512 slots.
    S.get_or_init(|| {
        let mut a = [false; MAX_SLOTS];
        a[0] = true; // slot 0 = the main thread (supervisor_main runs servicer_loop(0))
        Mutex::new(a)
    })
}
/// Ensure ring `slot` has a servicer thread, spawning one lazily on first use.
/// Idempotent. Returns false only if the OS refuses the thread (→ the caller
/// fails the clone/fork rather than leaving a slot no one services).
fn ensure_servicer(slot: u64) -> bool {
    let i = slot as usize;
    if i >= MAX_SLOTS {
        return false; // out of range — refuse so the caller fails the clone/fork
    }
    let mut bm = servicer_spawned().lock().unwrap();
    if bm[i] {
        return true;
    }
    match std::thread::Builder::new().spawn(move || servicer_loop(slot)) {
        Ok(_) => {
            bm[i] = true;
            true
        }
        Err(_) => false,
    }
}

/// Pre-spawn the DELEG-pool servicers (the top DELEG_SLOTS slots). A nested
/// (depth>0) delegated syscall leases one of these slots from the SIGNAL HANDLER
/// while the base slot's servicer is BUSY running the outer (parked) syscall — so
/// it can't route a CTL_ENSURE_SERVICER through the base slot to spawn its
/// servicer lazily (the lazy path needs a free servicer to run `ensure_servicer`).
/// Spawning the bounded DELEG pool up front (always-idle, blocked in FUTEX_WAIT at
/// ~0 CPU) makes the lease path purely cell-side (alloc + delegate_on, no control
/// round-trip). Called by both supervisor entry points after slot 0 is live.
/// Best-effort: a failed spawn just means that DELEG slot's servicer is absent, so
/// a re-entry that leases it falls back to the same OS-refusal-style stall the
/// lazy path would hit — never a corruption.
fn spawn_deleg_servicers() {
    for slot in PROCESS_SLOTS as u64..MAX_SLOTS as u64 {
        let _ = ensure_servicer(slot);
    }
}

/// Seed a cell's fd table: guest 0/1/2 are DUPs of the SUPERVISOR's stdio, so a
/// guest `close(1)`/`dup2`-over-stdout can never touch the supervisor's own fds.
fn seed_cell_fds(pid: i32) {
    let mut t = fdt().lock().unwrap();
    let m = t.entry(pid).or_default();
    for fd in 0..3i32 {
        let d = unsafe { host(SYS_DUP, fd as u64, 0, 0, 0, 0, 0) };
        m.insert(fd, FdVal::Host(if d >= 0 { d as i32 } else { fd }));
    }
}

/// Seed a cell's fd table for capture: guest stdin (0) DUPs the supervisor's, but
/// guest stdout (1) and stderr (2) DUP the passed `cap_fd` (a pipe write-end) so
/// the guest's output flows to the library's reader. The supervisor must drop its
/// own `cap_fd` after this so the pipe EOFs once these dups (closed by `fd_drop`)
/// and the cell's inherited copy are gone.
fn seed_cell_fds_capture(pid: i32, cap_fd: c_int) {
    let mut t = fdt().lock().unwrap();
    let m = t.entry(pid).or_default();
    let d0 = unsafe { host(SYS_DUP, 0, 0, 0, 0, 0, 0) };
    m.insert(0, FdVal::Host(if d0 >= 0 { d0 as i32 } else { 0 }));
    for fd in 1..3i32 {
        let d = unsafe { host(SYS_DUP, cap_fd as u64, 0, 0, 0, 0, 0) };
        m.insert(fd, FdVal::Host(if d >= 0 { d as i32 } else { cap_fd }));
    }
}

/// Seed a cell's fd table for STREAMING exec (the in-process unified backend):
/// fd 0 → `in_r` (the supervisor feeds host stdin into `in_w`), fd 1 → `out_w`,
/// fd 2 → `err_w` (the supervisor reads `out_r`/`err_r` and frames them). Each is
/// DUP'd so the cell owns an independent copy (closed by `fd_drop` on exit); the
/// supervisor drops its own `in_r`/`out_w`/`err_w` after this so EOF propagates.
/// Unlike [`seed_cell_fds_capture`] this keeps stdout and stderr SEPARATE and
/// gives the cell a real readable stdin — full ExecServer parity, but the cell is
/// forked in-process (shares the supervisor's loop_state + proctree).
fn seed_cell_fds_streaming(pid: i32, in_r: c_int, out_w: c_int, err_w: c_int) {
    let mut t = fdt().lock().unwrap();
    let m = t.entry(pid).or_default();
    for (gfd, host_fd) in [(0i32, in_r), (1, out_w), (2, err_w)] {
        let d = unsafe { host(SYS_DUP, host_fd as u64, 0, 0, 0, 0, 0) };
        m.insert(gfd, FdVal::Host(if d >= 0 { d as i32 } else { host_fd }));
    }
}

/// Redirect the WARM zygote's guest stdout/stderr (fd 1/2) to `cap_fd` (dup'd) so
/// the next fork-from-warm INSTANCE inherits them via the fd-table snapshot —
/// RACE-FREE, because that snapshot (`CTL_FORK_TABLE`) is taken only after this
/// runs (the supervisor seeds here, then bumps the acquire request). Returns the
/// warm cell's previous host fds for 1/2 to restore once the instance is done.
fn warm_capture_redirect(warm_pid: i32, cap_fd: c_int) -> (i32, i32) {
    let mut t = fdt().lock().unwrap();
    let m = t.entry(warm_pid).or_default();
    let old1 = match m.get(&1) {
        Some(FdVal::Host(h)) => *h,
        _ => 1,
    };
    let old2 = match m.get(&2) {
        Some(FdVal::Host(h)) => *h,
        _ => 2,
    };
    let d1 = unsafe { host(SYS_DUP, cap_fd as u64, 0, 0, 0, 0, 0) };
    let d2 = unsafe { host(SYS_DUP, cap_fd as u64, 0, 0, 0, 0, 0) };
    m.insert(1, FdVal::Host(if d1 >= 0 { d1 as i32 } else { old1 }));
    m.insert(2, FdVal::Host(if d2 >= 0 { d2 as i32 } else { old2 }));
    (old1, old2)
}
/// Restore the warm zygote's fd 1/2 after a capture acquire: close the cap-fd
/// dups installed by [`warm_capture_redirect`] (the last pipe write-ends → the
/// pipe EOFs) and put the saved host fds back.
fn warm_capture_restore(warm_pid: i32, old1: i32, old2: i32) {
    let mut t = fdt().lock().unwrap();
    let m = t.entry(warm_pid).or_default();
    if let Some(FdVal::Host(h)) = m.get(&1) {
        if *h != old1 {
            unsafe { host(SYS_CLOSE, *h as u64, 0, 0, 0, 0, 0) };
        }
    }
    if let Some(FdVal::Host(h)) = m.get(&2) {
        if *h != old2 {
            unsafe { host(SYS_CLOSE, *h as u64, 0, 0, 0, 0, 0) };
        }
    }
    m.insert(1, FdVal::Host(old1));
    m.insert(2, FdVal::Host(old2));
}

/// DIAGNOSTIC (SENTRY_SLOTDUMP=1): watchdog that periodically logs every ring slot
/// with a PENDING delegated syscall (`request != response`) to `/tmp/sentry_ipc.log`.
/// A stuck guest thread keeps its slot pending, so its `(nr, dbg_rip, dbg_caller)` —
/// the guest %rip + the first main-exe stack word, i.e. the app call-site of the
/// parked syscall — is logged each tick. With a static (non-component) guest exe,
/// symbolizing `caller` against it pins the exact parked guest function. Pairs with
/// SENTRY_SYSCALLTRACE (which stamps dbg_rip/dbg_caller). Off by default; the
/// production path never spawns this thread.
fn slot_dump_watchdog() {
    let rings = unsafe { RINGS };
    if rings.is_null() {
        return;
    }
    loop {
        // poll(NULL, 0, 3000) — a seccomp-wall-permitted ~3s sleep (clock_nanosleep
        // may be denied; poll is already used by every servicer's blocking wait).
        unsafe { host(SYS_POLL, 0, 0, 3000, 0, 0, 0) };
        let mut any = false;
        for s in 0..MAX_SLOTS {
            let r = unsafe { rings.add(s) };
            let req = unsafe { std::ptr::read_volatile(std::ptr::addr_of!((*r).request)) };
            let resp = unsafe { std::ptr::read_volatile(std::ptr::addr_of!((*r).response)) };
            if req == resp {
                continue;
            }
            any = true;
            let pid = unsafe { std::ptr::read_volatile(std::ptr::addr_of!((*r).pid)) };
            let tid = unsafe { std::ptr::read_volatile(std::ptr::addr_of!((*r).tid)) };
            let nr = unsafe { std::ptr::read_volatile(std::ptr::addr_of!((*r).nr)) };
            let rip = unsafe { std::ptr::read_volatile(std::ptr::addr_of!((*r).dbg_rip)) };
            let caller = unsafe { std::ptr::read_volatile(std::ptr::addr_of!((*r).dbg_caller)) };
            ipc_logf(
                &[
                    (b"SLOTDUMP slot=", s as i64),
                    (b" pid=", pid as i64),
                    (b" tid=", tid as i64),
                    (b" nr=", nr),
                    (b" rip=", rip as i64),
                    (b" caller=", caller as i64),
                ],
                b"",
            );
        }
        if any {
            ipc_logf(&[(b"SLOTDUMP ---tick---", 0)], b"");
        }
    }
}
/// DIAGNOSTIC (SENTRY_HANGDUMP=<delay_s>): after `delay_s` seconds, periodically log
/// EVERY live ring slot's OWNER kernel state — the blocking syscall (`/proc/PID/syscall`:
/// nr + first two args) and signal masks (`/proc/PID/status`: SigBlk/SigPnd/ShdPnd, plus
/// derived SIGCHLD blocked/pending bits). Unlike SENTRY_SLOTDUMP (which only sees slots
/// with a PENDING DELEGATED syscall), this catches a cell wedged in a CELL-LOCAL blocking
/// syscall (select/poll/epoll/read/wait4 run directly on the host) — e.g. a guest postgres
/// postmaster parked in select() whose SIGCHLD-driven latch never wakes. Reads via raw
/// host openat/read/close (the supervisor self-hardens after spawn, so avoid std::fs's
/// stat syscalls). Off by default; never spawned on the production path.
fn hang_dump_watchdog(delay_s: u64) {
    ipc_logf(
        &[
            (b"WATCHDOG-START delay=", delay_s as i64),
            (b" rings_null=", unsafe { RINGS.is_null() } as i64),
        ],
        b"",
    );
    let rings = unsafe { RINGS };
    if rings.is_null() {
        return;
    }
    // Read a small /proc file via raw host syscalls into `buf`; returns bytes read.
    fn read_proc(pid: i32, leaf: &str, buf: &mut [u8]) -> usize {
        let path = format!("/proc/{pid}/{leaf}\0");
        let fd = unsafe {
            host(
                SYS_OPENAT,
                (-100i64) as u64,
                path.as_ptr() as u64,
                libc::O_RDONLY as u64,
                0,
                0,
                0,
            )
        };
        if fd < 0 {
            return 0;
        }
        let n = unsafe {
            host(
                SYS_READ,
                fd as u64,
                buf.as_mut_ptr() as u64,
                buf.len() as u64,
                0,
                0,
                0,
            )
        };
        unsafe { host(SYS_CLOSE, fd as u64, 0, 0, 0, 0, 0) };
        if n > 0 {
            n as usize
        } else {
            0
        }
    }
    let hexu = |s: &str| -> u64 {
        u64::from_str_radix(s.trim().trim_start_matches("0x"), 16).unwrap_or(0)
    };
    for _ in 0..delay_s {
        unsafe { host(SYS_POLL, 0, 0, 1000, 0, 0, 0) };
    }
    loop {
        let mut any = false;
        for s in 0..MAX_SLOTS {
            let r = unsafe { rings.add(s) };
            let pid = unsafe { std::ptr::read_volatile(std::ptr::addr_of!((*r).pid)) };
            if pid <= 1 {
                continue;
            }
            let mut sbuf = [0u8; 256];
            let scn = read_proc(pid, "syscall", &mut sbuf);
            if scn == 0 {
                continue; // dead/unreadable
            }
            let sc = std::str::from_utf8(&sbuf[..scn]).unwrap_or("");
            let mut it = sc.split_whitespace();
            let nr = it.next().and_then(|t| t.parse::<i64>().ok()).unwrap_or(-2);
            let a0 = it.next().map(|t| hexu(t) as i64).unwrap_or(-1);
            let _a1 = it.next().map(|t| hexu(t) as i64).unwrap_or(-1);
            let mut stbuf = [0u8; 4096];
            let stn = read_proc(pid, "status", &mut stbuf);
            let st = std::str::from_utf8(&stbuf[..stn]).unwrap_or("");
            let (mut blk, mut pnd, mut shd) = (0u64, 0u64, 0u64);
            for line in st.lines() {
                if let Some(v) = line.strip_prefix("SigBlk:") {
                    blk = hexu(v);
                } else if let Some(v) = line.strip_prefix("SigPnd:") {
                    pnd = hexu(v);
                } else if let Some(v) = line.strip_prefix("ShdPnd:") {
                    shd = hexu(v);
                }
            }
            // SIGCHLD = signal 17 = bit 16 = 0x10000.
            let chld_blk = ((blk >> 16) & 1) as i64;
            let chld_pnd = (((pnd | shd) >> 16) & 1) as i64;
            // Ring slot fields: `ringnr` is the DELEGATED syscall (what the cell posted
            // to the supervisor and is FUTEX_WAITing on), `pend` = request != response
            // (still awaiting a servicer), `caller` = the stamped guest call-site (only
            // valid with SLOTDUMP/SYSCALLTRACE on). /proc syscall (nr) just shows the
            // futex ring-wait; ringnr is the syscall that's actually stuck.
            let ringnr = unsafe { std::ptr::read_volatile(std::ptr::addr_of!((*r).nr)) };
            let req = unsafe { std::ptr::read_volatile(std::ptr::addr_of!((*r).request)) };
            let resp = unsafe { std::ptr::read_volatile(std::ptr::addr_of!((*r).response)) };
            let caller = unsafe { std::ptr::read_volatile(std::ptr::addr_of!((*r).dbg_caller)) };
            any = true;
            ipc_logf(
                &[
                    (b"HANGDUMP slot=", s as i64),
                    (b" pid=", pid as i64),
                    (b" procnr=", nr),
                    (b" ringnr=", ringnr),
                    (b" pend=", (req != resp) as i64),
                    (b" a0=", a0),
                    (b" sigblk=", blk as i64),
                    (b" shdpnd=", shd as i64),
                    (b" chldBLK=", chld_blk),
                    (b" chldPND=", chld_pnd),
                    (b" caller=", caller as i64),
                ],
                b"",
            );
        }
        if any {
            ipc_logf(&[(b"HANGDUMP ---tick---", 0)], b"");
            // Full proctree topology: which guest vpid is parent of which, + the host
            // pid binding. Reveals who owns a dead child + whether SIGCHLD reached it.
            for (v, pv, hp) in proctree::dump_all() {
                ipc_logf(
                    &[
                        (b"PROCTREE vpid=", v),
                        (b" parent=", pv),
                        (b" hostpid=", hp),
                    ],
                    b"",
                );
            }
        }
        unsafe { host(SYS_POLL, 0, 0, 3000, 0, 0, 0) };
    }
}
fn supervisor_main(pid: i32) -> ! {
    MAIN_CELL_PID.store(pid as u32, Ordering::Relaxed);
    seed_cell_fds(pid);
    // Register the main cell as the sandbox's init (vpid 1) in the owned process
    // tree (native-execution VM, C2a).
    proctree::register_main(pid);
    // Seed the cell's initial guest cwd in OUR (supervisor) table: the supervisor
    // serves `getcwd` and resolves `AT_FDCWD` from `cwds()[pid]`, but the cell-side
    // seed in cell_main lands in the cell's OWN copy of the map (which the supervisor
    // never reads). Without this, a requested starting directory (exec_builder().cwd(),
    // a Dockerfile WORKDIR before a relative-path RUN) is silently ignored — getcwd
    // returns `/` and relative paths resolve against the wrong dir.
    if let Some(c) = guest_cwd_init().lock().unwrap().clone() {
        cwds().lock().unwrap().insert(pid, c);
    }
    supervisor_umasks()
        .lock()
        .unwrap()
        .insert(pid, DEFAULT_UMASK);
    // Shed our OWN inherited stdio (0/1/2). `sandbox_main` forked us from the
    // launcher whose 1/2 are the library's stdout/stderr — for an `exec_capture`
    // that's the PIPE WRITE-END. The supervisor never writes the cell's stdio
    // through its own fds; it services guest reads/writes through the per-pid fd
    // table (the dups `seed_cell_fds` just made, which are independent of these).
    // If we keep 1/2 open and the supervisor outlives the cell tree — e.g. a guest
    // backgrounds a process in a subshell (`( sleep & )`) that orphans and keeps a
    // servicer alive — those inherited write-ends keep the pipe from ever EOFing
    // and `exec_capture`'s reader hangs forever. Redirecting to /dev/null leaves
    // the actual cell processes as the pipe's only write-ends (they close on exit)
    // and also stops stray supervisor diagnostics from corrupting captured output.
    unsafe {
        let null = libc::open(b"/dev/null\0".as_ptr() as *const c_char, libc::O_RDWR);
        if null >= 0 {
            libc::dup2(null, 0);
            libc::dup2(null, 1);
            libc::dup2(null, 2);
            if null > 2 {
                libc::close(null);
            }
        }
    }
    // Seed every slot's owning pid to the main cell (a clone-thread shares the mm,
    // so process_vm via the tgid is correct; a fork()'d child overwrites its slot
    // with its own pid). SA_RESTART so a SIGCHLD doesn't abort a blocking servicer.
    unsafe {
        for i in 0..MAX_SLOTS as u64 {
            set_ring_owner(i, pid, pid);
        }
        let mut sa: libc::sigaction = std::mem::zeroed();
        sa.sa_sigaction = on_sigchld as *const () as usize;
        sa.sa_flags = libc::SA_RESTART | libc::SA_NOCLDSTOP;
        libc::sigemptyset(&mut sa.sa_mask);
        libc::sigaction(libc::SIGCHLD, &sa, std::ptr::null_mut());
    }
    // Deadlock-breaking fd-leak sweeper: closes the adopted pipe write-end of a cell
    // that died WITHOUT CTL_REAP (signal-killed grandchild under a blocked parent),
    // so the blocked delegated reader EOFs. NORMAL context; only closes fds, never
    // waitpid's (see fd_leak_sweeper). Best-effort: a failed spawn just forgoes the
    // sweep (the common CTL_REAP path still delivers EOFs).
    let _ = std::thread::Builder::new().spawn(fd_leak_sweeper);
    // DIAGNOSTIC: pending-slot dump watchdog (SENTRY_SLOTDUMP=1). Spawned before the
    // self-harden so it inherits the same wall as the servicers.
    if std::env::var("SENTRY_SLOTDUMP").is_ok() {
        let _ = std::thread::Builder::new().spawn(slot_dump_watchdog);
    }
    // DIAGNOSTIC: hang dump (SENTRY_HANGDUMP=<delay_s>). Dumps every live cell's blocking
    // syscall + signal masks via /proc — catches cell-local blocks (select/read) that
    // SLOTDUMP (delegated-only) misses. Spawned pre-self-harden so it shares the wall.
    if let Ok(v) = std::env::var("SENTRY_HANGDUMP") {
        let delay = v.parse::<u64>().unwrap_or(8);
        let _ = std::thread::Builder::new().spawn(move || hang_dump_watchdog(delay));
    }
    // Self-harden the trusted tier now that all privileged setup is done: drop the
    // capabilities it never needs, then deny the escape primitives via seccomp.
    // Lazily-spawned servicer threads inherit both; the cell is already forked with
    // its own (stricter) wall.
    drop_supervisor_caps();
    harden_supervisor();
    // Pre-spawn the bounded DELEG-pool servicers (Part A): a nested delegated
    // syscall leases a DELEG slot from the signal handler while the base servicer is
    // busy, so it can't lazily spawn one — these idle servicers stand ready.
    spawn_deleg_servicers();
    // Servicers are spawned LAZILY (CTL_ENSURE_SERVICER, on first use of a slot)
    // so a single-threaded guest needs only this one thread, not MAX_SLOTS.
    servicer_loop(0); // the main thread is slot 0's servicer
}

// ─── ELF loading (static ET_EXEC / static-PIE only; no interpreter) ──────────
fn rd_u16(b: &[u8], o: usize) -> u16 {
    u16::from_le_bytes(b[o..o + 2].try_into().unwrap())
}
fn rd_u32(b: &[u8], o: usize) -> u32 {
    u32::from_le_bytes(b[o..o + 4].try_into().unwrap())
}
fn rd_u64(b: &[u8], o: usize) -> u64 {
    u64::from_le_bytes(b[o..o + 8].try_into().unwrap())
}
const PT_LOAD: u32 = 1;
const PT_INTERP: u32 = 3;
const PF_X: u32 = 1;
const PF_W: u32 = 2;
const PF_R: u32 = 4;

struct Loaded {
    entry: u64,
    phdr_vaddr: u64,
    phent: u16,
    phnum: u16,
    interp: Option<Vec<u8>>,
}

/// Read a guest-absolute file CONFINED to the rootfs: symlinks (including absolute
/// ones such as the interpreter link `/lib64/ld-linux-x86-64.so.2` →
/// `/lib/x86_64-linux-gnu/ld-linux-x86-64.so.2`) resolve WITHIN the image via
/// `openat2(RESOLVE_IN_ROOT)`, never escaping to the host filesystem.
///
/// This is load-time-critical. A plain `std::fs::read(rootfs.join(path))` follows
/// an ABSOLUTE interpreter symlink against the HOST root, loading the host's
/// `ld.so` instead of the image's. On a host whose glibc differs from the image's
/// (e.g. host 2.39 vs image 2.36) the guest then runs the host loader against the
/// image's libc — a version mismatch in which libc's `__tunable_get_val(id)`
/// indexes the loader's differently-shaped `tunable_list`, hits a wider type, and
/// its over-long write clobbers the adjacent stack-canary slot → a spurious
/// "stack smashing detected" abort that kills every glibc (debian/ubuntu/…) guest.
/// (musl images are unaffected — no glibc loader/libc split.) When `root` is None
/// (unconfined), reads the host path directly.
fn read_in_root(root: &Option<std::path::PathBuf>, guest: &str) -> std::io::Result<Vec<u8>> {
    use std::os::unix::ffi::OsStrExt;
    let r = match root {
        Some(r) => r,
        None => return std::fs::read(guest),
    };
    let mut rootb = r.as_os_str().as_bytes().to_vec();
    rootb.push(0);
    let rootfd = unsafe {
        host(
            SYS_OPENAT,
            (-100i64) as u64,
            rootb.as_ptr() as u64,
            O_PATH | O_DIRECTORY,
            0,
            0,
            0,
        )
    };
    if rootfd < 0 {
        return Err(std::io::Error::from_raw_os_error(-rootfd as i32));
    }
    let mut gp = guest.as_bytes().to_vec();
    gp.push(0);
    let how = OpenHow {
        flags: 0, /* O_RDONLY */
        mode: 0,
        resolve: RESOLVE_IN_ROOT,
    };
    let fd = unsafe {
        host(
            SYS_OPENAT2,
            rootfd as u64,
            gp.as_ptr() as u64,
            std::ptr::addr_of!(how) as u64,
            std::mem::size_of::<OpenHow>() as u64,
            0,
            0,
        )
    };
    unsafe { host(SYS_CLOSE, rootfd as u64, 0, 0, 0, 0, 0) };
    if fd < 0 {
        return Err(std::io::Error::from_raw_os_error(-fd as i32));
    }
    let mut buf = Vec::new();
    let mut chunk = [0u8; 65536];
    loop {
        let n = unsafe {
            host(
                SYS_READ,
                fd as u64,
                chunk.as_mut_ptr() as u64,
                chunk.len() as u64,
                0,
                0,
                0,
            )
        };
        if n == 0 {
            break; // EOF
        }
        if n < 0 {
            // A read error must SURFACE, not masquerade as an empty file: reading a
            // DIRECTORY as a file returns -EISDIR here (openat2 O_RDONLY opens a dir
            // fine, but read(2) on it fails) — the old `n <= 0 { break }` swallowed it
            // and returned Ok(empty), so `read_file("/etc")` wrongly "succeeded".
            unsafe { host(SYS_CLOSE, fd as u64, 0, 0, 0, 0, 0) };
            return Err(std::io::Error::from_raw_os_error(-n as i32));
        }
        buf.extend_from_slice(&chunk[..n as usize]);
    }
    unsafe { host(SYS_CLOSE, fd as u64, 0, 0, 0, 0, 0) };
    Ok(buf)
}

/// The default search `PATH` (and the `PATH` seeded into the guest env when none
/// is supplied). Matches the conventional container default and the agent's.
const DEFAULT_PATH: &[u8] = b"/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin";

/// Whether `guest` exists as a REGULAR file confined to `root`: an
/// `openat2(rootfd, guest, RESOLVE_IN_ROOT, O_PATH)` then `fstat` checking
/// `S_IFREG`. `root` is the rootfs path (the host-side servicer owns this — it's
/// the same confinement [`read_in_root`] uses). Used by [`resolve_in_root_path`].
fn is_regular_in_root(root: &std::path::Path, guest: &str) -> bool {
    use std::os::unix::ffi::OsStrExt;
    let mut rootb = root.as_os_str().as_bytes().to_vec();
    rootb.push(0);
    let rootfd = unsafe {
        host(
            SYS_OPENAT,
            (-100i64) as u64,
            rootb.as_ptr() as u64,
            O_PATH | O_DIRECTORY,
            0,
            0,
            0,
        )
    };
    if rootfd < 0 {
        return false;
    }
    let mut gp = guest.as_bytes().to_vec();
    gp.push(0);
    let how = OpenHow {
        flags: O_PATH,
        mode: 0,
        resolve: RESOLVE_IN_ROOT,
    };
    let fd = unsafe {
        host(
            SYS_OPENAT2,
            rootfd as u64,
            gp.as_ptr() as u64,
            std::ptr::addr_of!(how) as u64,
            std::mem::size_of::<OpenHow>() as u64,
            0,
            0,
        )
    };
    unsafe { host(SYS_CLOSE, rootfd as u64, 0, 0, 0, 0, 0) };
    if fd < 0 {
        return false;
    }
    // struct stat: st_mode is a u32 at offset 24 on x86_64 Linux.
    let mut sb = [0u8; 144];
    let n = unsafe { host(SYS_FSTAT, fd as u64, sb.as_mut_ptr() as u64, 0, 0, 0, 0) };
    unsafe { host(SYS_CLOSE, fd as u64, 0, 0, 0, 0, 0) };
    if n != 0 {
        return false;
    }
    let mode = u32::from_le_bytes([sb[24], sb[25], sb[26], sb[27]]);
    (mode & S_IFMT) == S_IFREG
}

/// Return Linux's `/proc/self/exe`-style guest path for an executable: open the
/// guest path confined to `root`, let the kernel resolve symlinks, then map the
/// resulting host path back into guest space. Falls back at callers when any step
/// fails.
fn canonical_exe_in_root(root: &std::path::Path, guest: &str) -> Option<String> {
    use std::os::unix::ffi::OsStrExt;
    let mut rootb = root.as_os_str().as_bytes().to_vec();
    rootb.push(0);
    let rootfd = unsafe {
        host(
            SYS_OPENAT,
            (-100i64) as u64,
            rootb.as_ptr() as u64,
            O_PATH | O_DIRECTORY,
            0,
            0,
            0,
        )
    };
    if rootfd < 0 {
        return None;
    }
    let mut gp = guest.as_bytes().to_vec();
    gp.push(0);
    let how = OpenHow {
        flags: O_PATH,
        mode: 0,
        resolve: RESOLVE_IN_ROOT,
    };
    let fd = unsafe {
        host(
            SYS_OPENAT2,
            rootfd as u64,
            gp.as_ptr() as u64,
            std::ptr::addr_of!(how) as u64,
            std::mem::size_of::<OpenHow>() as u64,
            0,
            0,
        )
    };
    unsafe { host(SYS_CLOSE, rootfd as u64, 0, 0, 0, 0, 0) };
    if fd < 0 {
        return None;
    }
    let host_path = readlink_fd(fd as i32);
    unsafe { host(SYS_CLOSE, fd as u64, 0, 0, 0, 0, 0) };
    let guest_path = host_to_guest_path(&host_path?)?;
    String::from_utf8(guest_path).ok()
}

/// Whether `guest` exists as a DIRECTORY confined to `root` — the no-virt analogue
/// of a successful `chdir` during exec setup. Modeled on [`is_regular_in_root`] but
/// checks `S_IFDIR`. With no rootfs (`None`) it stats the host path directly. Used
/// at exec to make a nonexistent starting `cwd` FAIL the launch (matching the KVM
/// agent, whose pre-exec `chdir` errors out) instead of being silently ignored.
fn dir_exists_in_root(root: &Option<std::path::PathBuf>, guest: &str) -> bool {
    use std::os::unix::ffi::OsStrExt;
    let r = match root {
        Some(r) => r,
        None => return std::path::Path::new(guest).is_dir(),
    };
    let mut rootb = r.as_os_str().as_bytes().to_vec();
    rootb.push(0);
    let rootfd = unsafe {
        host(
            SYS_OPENAT,
            (-100i64) as u64,
            rootb.as_ptr() as u64,
            O_PATH | O_DIRECTORY,
            0,
            0,
            0,
        )
    };
    if rootfd < 0 {
        return false;
    }
    let mut gp = guest.as_bytes().to_vec();
    gp.push(0);
    let how = OpenHow {
        flags: O_PATH,
        mode: 0,
        resolve: RESOLVE_IN_ROOT,
    };
    let fd = unsafe {
        host(
            SYS_OPENAT2,
            rootfd as u64,
            gp.as_ptr() as u64,
            std::ptr::addr_of!(how) as u64,
            std::mem::size_of::<OpenHow>() as u64,
            0,
            0,
        )
    };
    unsafe { host(SYS_CLOSE, rootfd as u64, 0, 0, 0, 0, 0) };
    if fd < 0 {
        return false;
    }
    let mut sb = [0u8; 144];
    let n = unsafe { host(SYS_FSTAT, fd as u64, sb.as_mut_ptr() as u64, 0, 0, 0, 0) };
    unsafe { host(SYS_CLOSE, fd as u64, 0, 0, 0, 0, 0) };
    if n != 0 {
        return false;
    }
    let mode = u32::from_le_bytes([sb[24], sb[25], sb[26], sb[27]]);
    (mode & S_IFMT) == S_IFDIR
}

/// A minimal static x86-64 ELF that checkpoints then exec's its arguments:
/// `syscall(0x5359)` (the SENTINEL warm-zygote checkpoint) then
/// `execve(argv[1], &argv[1], envp)`. Used to run a NON-cooperating service
/// workload (nginx, …) under the warm-daemon [`Pool`]: warm this helper (parks at
/// the checkpoint instantly, BEFORE exec), then `acquire_running` resumes past it
/// and exec's the real CMD — so the workload lives as a detached instance IN the
/// pool supervisor (its own ring slot), sharing the supervisor's loopback +
/// proctree with later execs. Deterministic bytes (no build-time compiler dep);
/// sentry's `load_elf` maps the single R+X PT_LOAD at 0x400000 and jumps to entry.
fn sm_checkpoint_elf() -> Vec<u8> {
    let code: [u8; 53] = [
        0x48, 0xc7, 0xc0, 0x59, 0x53, 0x00, 0x00, // mov rax, 0x5359
        0x0f, 0x05, // syscall  (SENTINEL checkpoint; returns here on resume)
        0x48, 0x8b, 0x7c, 0x24, 0x10, // mov rdi, [rsp+0x10]        ; argv[1]
        0x48, 0x8d, 0x74, 0x24, 0x10, // lea rsi, [rsp+0x10]        ; &argv[1]
        0x48, 0x8b, 0x0c, 0x24, // mov rcx, [rsp]             ; argc
        0x48, 0x8d, 0x54, 0xcc, 0x10, // lea rdx, [rsp+rcx*8+0x10]  ; envp
        0x48, 0xc7, 0xc0, 0x3b, 0x00, 0x00, 0x00, // mov rax, 59   ; execve
        0x0f, 0x05, // syscall
        0x48, 0xc7, 0xc7, 0x7f, 0x00, 0x00, 0x00, // mov rdi, 127  ; (execve failed)
        0x48, 0xc7, 0xc0, 0x3c, 0x00, 0x00, 0x00, // mov rax, 60   ; exit
        0x0f, 0x05, // syscall
    ];
    const BASE: u64 = 0x40_0000;
    const EHSIZE: u64 = 64;
    const PHSIZE: u64 = 56;
    let entry = BASE + EHSIZE + PHSIZE;
    let filesz = EHSIZE + PHSIZE + code.len() as u64;
    let mut e = Vec::with_capacity(filesz as usize);
    e.extend_from_slice(&[0x7f, b'E', b'L', b'F', 2, 1, 1, 0]);
    e.extend_from_slice(&[0u8; 8]);
    e.extend_from_slice(&2u16.to_le_bytes()); // e_type = ET_EXEC
    e.extend_from_slice(&0x3eu16.to_le_bytes()); // e_machine = X86_64
    e.extend_from_slice(&1u32.to_le_bytes()); // e_version
    e.extend_from_slice(&entry.to_le_bytes()); // e_entry
    e.extend_from_slice(&EHSIZE.to_le_bytes()); // e_phoff
    e.extend_from_slice(&0u64.to_le_bytes()); // e_shoff
    e.extend_from_slice(&0u32.to_le_bytes()); // e_flags
    e.extend_from_slice(&(EHSIZE as u16).to_le_bytes()); // e_ehsize
    e.extend_from_slice(&(PHSIZE as u16).to_le_bytes()); // e_phentsize
    e.extend_from_slice(&1u16.to_le_bytes()); // e_phnum
    e.extend_from_slice(&0u16.to_le_bytes()); // e_shentsize
    e.extend_from_slice(&0u16.to_le_bytes()); // e_shnum
    e.extend_from_slice(&0u16.to_le_bytes()); // e_shstrndx
    e.extend_from_slice(&1u32.to_le_bytes()); // p_type = PT_LOAD
    e.extend_from_slice(&5u32.to_le_bytes()); // p_flags = R+X
    e.extend_from_slice(&0u64.to_le_bytes()); // p_offset
    e.extend_from_slice(&BASE.to_le_bytes()); // p_vaddr
    e.extend_from_slice(&BASE.to_le_bytes()); // p_paddr
    e.extend_from_slice(&filesz.to_le_bytes()); // p_filesz
    e.extend_from_slice(&filesz.to_le_bytes()); // p_memsz
    e.extend_from_slice(&0x1000u64.to_le_bytes()); // p_align
    e.extend_from_slice(&code);
    e
}

/// Resolve a command's `argv[0]` to a concrete guest-absolute path, confined to
/// `root`. If `argv0` already contains a `/` it's returned verbatim (a literal
/// path, as today). Otherwise it's a bare name (`node`, `python3`, `env`): each
/// `PATH` directory is tried via the rootfs-confined open and the FIRST entry
/// that's a regular file wins — the no-virt analogue of the agent's
/// `execvpe`-style PATH search, but RESOLVE_IN_ROOT-confined so it can never see a
/// host binary. `path_env` is the merged env's `PATH` (`DEFAULT_PATH` if absent).
/// `None` => no candidate resolved (the caller emits EXIT 127).
fn resolve_in_root_path(root: &std::path::Path, path_env: &[u8], argv0: &str) -> Option<String> {
    if argv0.contains('/') {
        return Some(argv0.to_string());
    }
    if argv0.is_empty() {
        return None;
    }
    let env = if path_env.is_empty() {
        DEFAULT_PATH
    } else {
        path_env
    };
    for dir in env.split(|&b| b == b':') {
        // POSIX: an empty PATH element means the current directory; in a confined
        // rootfs with no meaningful cwd here, treat it as the root.
        let dir = if dir.is_empty() { b"." } else { dir };
        let mut cand = String::from_utf8_lossy(dir).into_owned();
        if !cand.ends_with('/') {
            cand.push('/');
        }
        cand.push_str(argv0);
        if is_regular_in_root(root, &cand) {
            return Some(cand);
        }
    }
    None
}

/// Extract the `PATH` value (without the `PATH=` prefix) from a merged
/// `(KEY,VALUE)` env, as bytes — for [`resolve_in_root_path`]. Empty if unset.
fn path_from_env(env: &[(String, String)]) -> Vec<u8> {
    env.iter()
        .find(|(k, _)| k == "PATH")
        .map(|(_, v)| v.as_bytes().to_vec())
        .unwrap_or_default()
}

fn load_elf(bytes: &[u8], base: u64) -> Loaded {
    if bytes.len() < 64 || &bytes[0..4] != b"\x7fELF" || bytes[4] != 2 || bytes[5] != 1 {
        die(b"not an ELF64-LE\n");
    }
    let e_type = rd_u16(bytes, 16);
    if e_type != 2 && e_type != 3 {
        die(b"not ET_EXEC/ET_DYN\n");
    }
    let entry = rd_u64(bytes, 24) + base;
    let phoff = rd_u64(bytes, 32);
    let phent = rd_u16(bytes, 54);
    let phnum = rd_u16(bytes, 56);
    let mut phdr_vaddr = 0u64;
    let mut max_x = 0u64;
    let mut interp: Option<Vec<u8>> = None;
    let mut loads: Vec<(u64, u64, u64, u64, u32)> = Vec::new();
    let mut lo = u64::MAX;
    let mut hi = 0u64;
    for i in 0..phnum as usize {
        let ph = phoff as usize + i * phent as usize;
        let pt = rd_u32(bytes, ph);
        if pt == PT_INTERP {
            let o = rd_u64(bytes, ph + 8) as usize;
            let sz = rd_u64(bytes, ph + 32) as usize;
            let mut s = bytes[o..o + sz].to_vec();
            while s.last() == Some(&0) {
                s.pop();
            }
            interp = Some(s);
            continue;
        }
        if pt != PT_LOAD {
            continue;
        }
        let p_flags = rd_u32(bytes, ph + 4);
        let p_offset = rd_u64(bytes, ph + 8);
        let p_vaddr = rd_u64(bytes, ph + 16) + base;
        let p_filesz = rd_u64(bytes, ph + 32);
        let p_memsz = rd_u64(bytes, ph + 40);
        if phoff >= p_offset && phoff < p_offset + p_filesz {
            phdr_vaddr = p_vaddr + (phoff - p_offset);
        }
        if (p_flags & PF_X) != 0 {
            max_x = max_x.max(page_up(p_vaddr + p_memsz));
        }
        lo = lo.min(page_down(p_vaddr));
        hi = hi.max(page_up(p_vaddr + p_memsz));
        loads.push((p_vaddr, p_offset, p_filesz, p_memsz, p_flags));
    }
    if max_x == 0 || max_x >= WINDOW_FLOOR {
        die(b"executable segments do not fit below the SUD floor\n");
    }
    // MAP_FIXED (replace), not NOREPLACE: the initial load targets an empty low
    // region (so replace == no-replace there), and execve emulation re-loads OVER
    // the old guest image in place — without munmapping (which would unmap the very
    // stack the handler runs on, since a spawn child's stack is itself low).
    let p = unsafe {
        libc::mmap(
            lo as *mut c_void,
            (hi - lo) as usize,
            libc::PROT_READ | libc::PROT_WRITE,
            libc::MAP_PRIVATE | libc::MAP_ANONYMOUS | libc::MAP_FIXED,
            -1,
            0,
        )
    };
    if p == libc::MAP_FAILED || p as u64 != lo {
        die(b"image reservation failed\n");
    }
    for &(p_vaddr, p_offset, p_filesz, _, _) in &loads {
        unsafe {
            std::ptr::copy_nonoverlapping(
                bytes.as_ptr().add(p_offset as usize),
                p_vaddr as *mut u8,
                p_filesz as usize,
            )
        };
    }
    for &(p_vaddr, _, _, p_memsz, p_flags) in &loads {
        let mut prot = 0;
        if p_flags & PF_R != 0 {
            prot |= libc::PROT_READ;
        }
        if p_flags & PF_W != 0 {
            prot |= libc::PROT_WRITE;
        }
        if p_flags & PF_X != 0 {
            prot |= libc::PROT_EXEC;
        }
        let s = page_down(p_vaddr);
        let ee = page_up(p_vaddr + p_memsz);
        unsafe { libc::mprotect(s as *mut c_void, (ee - s) as usize, prot) };
    }
    // Record the image extent so guest_mmap can never let a colliding address HINT
    // (node/V8's pointer-compression cage) clobber the exe / ld.so via MAP_FIXED.
    record_loader_span(base, lo, hi);
    Loaded {
        entry,
        phdr_vaddr,
        phent,
        phnum,
        interp,
    }
}

// auxv
const AT_NULL: u64 = 0;
const AT_PHDR: u64 = 3;
const AT_PHENT: u64 = 4;
const AT_PHNUM: u64 = 5;
const AT_PAGESZ: u64 = 6;
const AT_BASE: u64 = 7;
const AT_FLAGS: u64 = 8;
const AT_ENTRY: u64 = 9;
const AT_UID: u64 = 11;
const AT_EUID: u64 = 12;
const AT_GID: u64 = 13;
const AT_EGID: u64 = 14;
const AT_PLATFORM: u64 = 15;
const AT_HWCAP: u64 = 16;
const AT_CLKTCK: u64 = 17;
const AT_SECURE: u64 = 23;
const AT_RANDOM: u64 = 25;
const AT_EXECFN: u64 = 31;

fn build_stack(l: &Loaded, interp_base: Option<u64>, argv: &[Vec<u8>], envp: &[Vec<u8>]) -> u64 {
    const SZ: usize = 8 * 1024 * 1024;
    let base = unsafe {
        libc::mmap(
            std::ptr::null_mut(),
            SZ,
            libc::PROT_READ | libc::PROT_WRITE,
            libc::MAP_PRIVATE | libc::MAP_ANONYMOUS,
            -1,
            0,
        )
    };
    if base == libc::MAP_FAILED {
        die(b"stack mmap failed SMARK_RTRIP_7\n");
    }
    guest_vma_note_mapping(base as u64, SZ as u64);
    let top = base as u64 + SZ as u64;
    let mut sp = top;
    macro_rules! push {
        ($bytes:expr) => {{
            let bb: &[u8] = $bytes;
            sp -= bb.len() as u64;
            unsafe { std::ptr::copy_nonoverlapping(bb.as_ptr(), sp as *mut u8, bb.len()) };
            sp
        }};
    }
    // AT_RANDOM (libc stack-canary seed). DELEGATE getrandom — build_stack runs
    // both pre-seal (initial load) and post-seal (execve emulation), and a direct
    // getrandom isn't in the wall's allowlist, so it must go through the supervisor.
    let mut rnd = [0u8; 16];
    delegate(SYS_GETRANDOM, rnd.as_mut_ptr() as u64, 16, 0, 0, 0, 0);
    // ── Make the new program's stack-canary EQUAL the cell's own canary. ──────────
    // emulate_execve / cell_main hand the new program control with %fs still = CELL_FS
    // (the sentry cell's own glibc TLS — kept so the SIGSYS handler's Rust has sound
    // TLS), and it KEEPS that %fs until its crt/ld.so runs arch_prctl(ARCH_SET_FS) to
    // install its real TCB. glibc's __stack_chk_guard comes from AT_RANDOM[0..8] (low
    // byte zeroed). A canary-protected glibc/crt function that straddles that SET_FS
    // saves the canary it reads via CELL_FS in its prologue and checks the one in the
    // new TCB in its epilogue — if they differ → `*** stack smashing detected ***`
    // SIGABRT, DETERMINISTIC on canary-heavy binaries (rustc, gcc's cc1, any modern
    // host-compiled binary) and the LONG-HUNTED "reentrant_under_load" corruption.
    // Seed AT_RANDOM[0..8] with the cell's CURRENT guard (%fs:0x28 — build_stack always
    // runs under CELL_FS) so the guard the program installs in its TCB equals the one it
    // reads through CELL_FS beforehand: the straddling check now matches. Still random
    // (per-cell guard); AT_RANDOM[8..16] stays fresh for the pointer guard.
    {
        let cell_guard: u64;
        unsafe {
            std::arch::asm!("mov {g}, fs:[0x28]", g = out(reg) cell_guard, options(nostack, preserves_flags))
        };
        if cell_guard != 0 {
            rnd[0..8].copy_from_slice(&cell_guard.to_le_bytes());
        }
    }
    let at_random = push!(&rnd);
    let at_platform = push!(b"x86_64\0");
    let execfn = [argv[0].as_slice(), b"\0"].concat();
    let at_execfn = push!(&execfn);
    let mut argv_p = Vec::new();
    for s in argv {
        let z = [s.as_slice(), b"\0"].concat();
        argv_p.push(push!(&z));
    }
    let mut envp_p = Vec::new();
    for s in envp {
        let z = [s.as_slice(), b"\0"].concat();
        let addr = push!(&z);
        envp_p.push(addr);
    }
    let uid = cred_get(&CELL_CREDS.ruid) as u64;
    let euid = cred_get(&CELL_CREDS.euid) as u64;
    let gid = cred_get(&CELL_CREDS.rgid) as u64;
    let egid = cred_get(&CELL_CREDS.egid) as u64;
    let aux: [(u64, u64); 16] = [
        (AT_PHDR, l.phdr_vaddr),
        (AT_PHENT, l.phent as u64),
        (AT_PHNUM, l.phnum as u64),
        (AT_PAGESZ, 4096),
        (AT_BASE, interp_base.unwrap_or(0)),
        (AT_FLAGS, 0),
        (AT_ENTRY, l.entry),
        (AT_UID, uid),
        (AT_EUID, euid),
        (AT_GID, gid),
        (AT_EGID, egid),
        (AT_CLKTCK, 100),
        (AT_HWCAP, 0),
        (AT_SECURE, 0),
        (AT_PLATFORM, at_platform),
        (AT_RANDOM, at_random),
    ];
    let n = 1 + (argv_p.len() + 1) + (envp_p.len() + 1) + (aux.len() + 1 + 1) * 2;
    let mut cur = (sp - (n as u64) * 8) & !0xF;
    let argc_addr = cur;
    let mut put = |v: u64| {
        unsafe { *(cur as *mut u64) = v };
        cur += 8;
    };
    put(argv_p.len() as u64);
    for p in &argv_p {
        put(*p);
    }
    put(0);
    for p in &envp_p {
        put(*p);
    }
    put(0);
    for (t, v) in &aux {
        put(*t);
        put(*v);
    }
    put(AT_EXECFN);
    put(at_execfn);
    put(AT_NULL);
    put(0);
    argc_addr
}

// ─── the seccomp wall: pin the cell to cell-LOCAL syscalls only ──────────────
const AUDIT_ARCH_X86_64: u32 = 0xC000_003E;
const SECCOMP_RET_KILL_PROCESS: u32 = 0x8000_0000;
const SECCOMP_RET_ALLOW: u32 = 0x7fff_0000;
const BPF_LD_W_ABS: u16 = 0x20;
const BPF_JMP_JEQ_K: u16 = 0x15;
const BPF_RET_K: u16 = 0x06;
fn sf(code: u16, jt: u8, jf: u8, k: u32) -> libc::sock_filter {
    libc::sock_filter { code, jt, jf, k }
}
fn install_wall() {
    // Only syscalls Layer 1 itself issues from the cell are permitted; they affect
    // only the cell's own address space / thread state / the ring futex. Everything
    // a hostile guest could use to reach a host resource is absent ⇒ KILL_PROCESS.
    let allow: &[u32] = &[
        SYS_FUTEX as u32,
        SYS_MMAP as u32,
        SYS_MPROTECT as u32,
        SYS_MUNMAP as u32,
        SYS_MREMAP as u32,
        SYS_MADVISE as u32,
        SYS_MINCORE as u32, // cell-local residency probe (see dispatch_simple) — own pages only
        SYS_PKEY_ALLOC as u32, // PKU/MPK: chromium PartitionAlloc/V8 allocate a protection
        SYS_PKEY_FREE as u32, // key at startup and CHECK-fail on ENOSYS. Cell-local (see
        SYS_PKEY_MPROTECT as u32, // dispatch_simple) — a pkey is per-mm and tags only the
        // caller's OWN pages (WRPKRU is unprivileged); reaches no host resource.
        SYS_BRK as u32,
        SYS_ARCH_PRCTL as u32,
        SYS_EXIT as u32,
        SYS_EXIT_GROUP as u32,
        15,                      // rt_sigreturn (returns from the SIGSYS handler + guest handlers)
        SYS_RT_SIGACTION as u32, // cell-local: install guest signal handlers (SIGSYS
        // filtered out in-handler; sa_restorer forced to our above-floor trampoline)
        SYS_RT_SIGPROCMASK as u32, // cell-local: guest signal mask (SIGSYS force-unblocked)
        SYS_RT_SIGPENDING as u32,  // cell-local: used by trapped futex waits to detect
        // pending guest signals without delivering them on sentry's SIGSYS alt-stack.
        SYS_SETITIMER as u32,
        SYS_GETITIMER as u32,
        SYS_ALARM as u32, // cell-local interval timers → SIGALRM to the guest
        SYS_CLONE as u32, // thread creation (cell-local; new thread is sealed too)
        SYS_FORK as u32,  // new process — fork copies SUD+handler+seccomp (still sealed)
        SYS_VFORK as u32,
        SYS_WAIT4 as u32,           // cell-local: reap its own forked children
        SYS_WAITID as u32,          // cell-local: ditto (waitid is the modern reaper)
        SYS_SET_TID_ADDRESS as u32, // cell-local: set this thread's clear_child_tid + return its real tid
        SYS_SET_ROBUST_LIST as u32, // cell-local: register this thread's robust-futex
        // list so the kernel walks it on thread death (FUTEX_OWNER_DIED). Reaches no
        // host resource — only the calling thread's own robust-list head.
        SYS_RSEQ as u32, // cell-local: register this thread's restartable-sequence area
        // (per-cpu rseq). Touches only the caller's own thread state; no host reach.
        SYS_PAUSE as u32, // cell-local signal-wait (forwarded from dispatch_simple, above the floor)
        SYS_RT_SIGTIMEDWAIT as u32, // cell-local signal-wait (synchronously dequeues a signal for THIS thread)
        SYS_RT_SIGSUSPEND as u32, // cell-local signal-wait (a shell's `wait` loop spins on this — see dispatch_simple)
        219, // restart_syscall — KERNEL-INJECTED (never issued by the guest): when an
        // allowlisted restartable syscall running natively above the SUD floor (futex,
        // rt_sigtimedwait, pause, clock_nanosleep, OR sentry's OWN blocking host() calls
        // on the ring/servicer poll) is interrupted by a signal, the kernel re-enters it
        // with rax=__NR_restart_syscall. It can ONLY restart THIS thread's saved restart
        // block (an already-allowed syscall) — it reaches no new host resource. Omitting
        // it made every signal-interrupted wait fault into the wall → KILL_PROCESS: fatal
        // for heavily-threaded, signal-driven workloads (Chromium's ThreadPool +
        // base::WaitableEvent storm restart_syscall constantly). This was THE Chromium
        // "no connection" gate — children/handlers were wall-killed mid-handshake.
        SYS_MEMBARRIER as u32, // cell-local memory barrier (dispatch_simple) — must run
        // IN the cell to fence the cell's OWN threads; the old delegated path no-op'd it
        // in the supervisor, breaking lock-free shared-memory queue visibility (ipcz/mojo
        // store-load ordering on x86). Touches only the caller process's thread ordering;
        // reaches no host resource.
        SYS_EVENTFD2 as u32, // cell-local: sentry's vfork emulation creates a private
        SYS_READ as u32,     // eventfd, then read/write/close it above the SUD floor to
        SYS_WRITE as u32,    // release the parent when the emulated child execs/exits.
        SYS_CLOSE as u32,    // Guest-issued fd syscalls still SUD-dispatch below.
        131, // sigaltstack — every thread registers its own signal alt-stack (so the
        // SUD SIGSYS handler runs on a known-good stack, not a corrupt/exhausted guest
        // RSP). Cell-local: touches only the calling thread's own alt-stack state.
        // NOTE: execve is intentionally NOT here — it's SUD-dispatched and EMULATED
        // in-cell, never issued to the kernel; a raw bypass would only reset SUD and
        // leave the new program walled, so the wall stays meaningful.
        157, // prctl — the clone trampoline re-arms SUD on the child
        SYS_GETPID as u32,
        SYS_GETTID as u32,
        SYS_GETPPID as u32,
        SYS_GETUID as u32,
        SYS_GETEUID as u32,
        SYS_GETGID as u32,
        SYS_GETEGID as u32, // identity: read-only, returns the cell's own ids
        // process-group / session — cell-local (job control). The forwarded
        // host() calls from the SYS_SETSID/SETPGID/GETPGID/GETPGRP/GETSID arms in
        // dispatch_simple run above the SUD floor, so they must be allowlisted to
        // reach the kernel. A raw GUEST call still traps via SUD first (guest code
        // is below the floor) into dispatch_simple, where setpgid's target is
        // validated against this sandbox's pids — so allowing them by nr can't
        // breach the wall (these only touch the caller's own session/pgrp or read
        // a pgid/sid, and the kernel itself confines setpgid to the caller's
        // session). They reach no host resource.
        SYS_SETSID as u32,
        SYS_SETPGID as u32,
        SYS_GETPGID as u32,
        SYS_GETPGRP as u32,
        SYS_GETSID as u32,
        SYS_CLOCK_GETTIME as u32, // read-only clock (benign, like getpid); used by
        // the zygote loop and guest clock reads.
        SYS_GETRANDOM as u32, // fills a caller-owned buffer; no supervisor fd/table context.
                              // NOTE: production narrows these by argument — `clone` to CLONE_VM thread
                              // flags only (reject namespace/fork), and `prctl` to subcommand 59
                              // (PR_SET_SYSCALL_USER_DISPATCH). Both are cell-local-control syscalls that
                              // can't reach a host resource, so allowing them by nr doesn't breach the
                              // wall (a hostile clone/prctl still can't make a forbidden syscall).
    ];
    let n = allow.len();
    let mut prog: Vec<libc::sock_filter> = Vec::new();
    prog.push(sf(BPF_LD_W_ABS, 0, 0, 4)); // A = arch
    prog.push(sf(BPF_JMP_JEQ_K, 1, 0, AUDIT_ARCH_X86_64));
    prog.push(sf(BPF_RET_K, 0, 0, SECCOMP_RET_KILL_PROCESS));
    prog.push(sf(BPF_LD_W_ABS, 0, 0, 0)); // A = nr
    for (p, &nr) in allow.iter().enumerate() {
        prog.push(sf(BPF_JMP_JEQ_K, (n - p) as u8, 0, nr)); // jt → the ALLOW at the end
    }
    prog.push(sf(BPF_RET_K, 0, 0, SECCOMP_RET_KILL_PROCESS)); // default
    prog.push(sf(BPF_RET_K, 0, 0, SECCOMP_RET_ALLOW)); // allow
    let mut fprog = libc::sock_fprog {
        len: prog.len() as u16,
        filter: prog.as_mut_ptr(),
    };
    unsafe {
        if libc::prctl(libc::PR_SET_NO_NEW_PRIVS, 1, 0, 0, 0) != 0 {
            die(b"PR_SET_NO_NEW_PRIVS failed\n");
        }
        if libc::prctl(
            libc::PR_SET_SECCOMP,
            libc::SECCOMP_MODE_FILTER as libc::c_ulong,
            &mut fprog as *mut _ as libc::c_ulong,
            0,
            0,
        ) != 0
        {
            die(b"PR_SET_SECCOMP failed\n");
        }
    }
}

/// Self-harden the TRUSTED tier (the supervisor / future kernel tier) with its
/// OWN seccomp filter — the first slice of the gVisor-style sandboxed supervisor
/// (see no-virt-sentry-max-parity-plan). Today the supervisor is privileged and
/// unwalled; if a guest ever broke OUT of the cell into the supervisor, it would
/// have the full host syscall surface. This denies the escape/escalation
/// primitives the supervisor (and cell startup, which inherits this filter via
/// fork) never legitimately uses — `execve` (the cell EMULATES execve in-process,
/// it never issues a real one), `ptrace`, the mount family, kernel-module loading,
/// `bpf`, `kexec`, `reboot`, swap, and the keyring — so a post-escape attacker
/// can't pivot through them. Default-ALLOW (this is a deny-list, not yet the full
/// allowlist), so the supervisor's normal operation is byte-for-byte unchanged.
///
/// NOTE: `setns` (egress re-home), `setuid`/`setgid`/`setgroups` (the cell's
/// uid-drop, which inherits this filter), `clone`/`fork`, `prctl`/`seccomp`, and
/// the socket family are intentionally NOT denied. Later slices tighten this into
/// a positive allowlist + drop capabilities + split off a privileged broker.
/// Test hook (NOT a public API): fork a child, self-harden it with
/// [`harden_supervisor`], then attempt an escape primitive (`execve`). Returns
/// the signal that killed the child — 31 (`SIGSYS`) means the seccomp deny-list
/// fired — or -1 if the child wasn't signaled (i.e. the primitive was NOT
/// blocked). Lets the test suite guard the hardening against regressions.
#[doc(hidden)]
pub fn harden_self_test_signal() -> i32 {
    let pid = unsafe { libc::fork() };
    if pid == 0 {
        harden_supervisor();
        let p = b"/bin/true\0";
        let argv = [p.as_ptr() as *const libc::c_char, std::ptr::null()];
        unsafe {
            libc::execve(
                p.as_ptr() as *const libc::c_char,
                argv.as_ptr(),
                std::ptr::null(),
            );
            libc::_exit(0); // execve was NOT blocked
        }
    }
    let mut st = 0i32;
    unsafe { libc::waitpid(pid, &mut st, 0) };
    if libc::WIFSIGNALED(st) {
        libc::WTERMSIG(st)
    } else {
        -1
    }
}

/// Test hook (NOT a public API): fork a child, install the per-cell [`install_wall`]
/// seccomp ALLOWLIST on it, then issue a syscall OUTSIDE the cell-local allowlist
/// (`socket` — a host-resource primitive the wall denies). The kernel
/// `SECCOMP_RET_KILL_PROCESS`'s the child; the parent runs the SAME
/// [`classify_status`] the supervisor/pool exit paths use and returns its result.
///
/// Asserts the production classifier maps a wall kill to
/// `Some(SentryError::SeccompViolation { signum: 31 })` — NOT a bare exit `159` —
/// guarding the structured-error surface against regressions. (`None` ⇒ the wall
/// failed to fire, i.e. the child wasn't killed: a hardening regression.)
#[doc(hidden)]
pub fn seccomp_wall_self_test() -> Option<SentryError> {
    let pid = unsafe { libc::fork() };
    if pid == 0 {
        install_wall();
        // `socket(AF_INET, SOCK_STREAM, 0)` is not in the cell allowlist ⇒ KILL.
        unsafe {
            libc::syscall(libc::SYS_socket, libc::AF_INET, libc::SOCK_STREAM, 0);
            libc::_exit(0); // the wall did NOT fire — report a clean exit (no error)
        }
    }
    let mut st = 0i32;
    unsafe { libc::waitpid(pid, &mut st, 0) };
    classify_status(st).1
}

/// Drop the trusted tier's capabilities down to the minimal set it actually needs
/// at runtime — the second slice of the sandboxed supervisor (Phase 1a.2). Today
/// the supervisor runs as full root; this removes the ~25 capabilities it never
/// uses (CAP_SYS_MODULE/BOOT/RAWIO, CAP_NET_RAW, CAP_MKNOD, CAP_SYS_TIME,
/// CAP_BPF, CAP_AUDIT_*, …) from the effective + permitted sets, so they're gone
/// even if the seccomp deny-list were somehow bypassed. Belt-and-suspenders with
/// [`harden_supervisor`]. Best-effort (a no-op if we aren't privileged).
///
/// KEPT (the tier genuinely needs these): CHOWN/DAC_OVERRIDE/DAC_READ_SEARCH/
/// FOWNER (confined fs on the guest's behalf), KILL (signal validated sandbox
/// pids), SETGID/SETUID (the cell's uid-drop, inherited), NET_BIND_SERVICE
/// (published low ports), NET_ADMIN (lo bring-up + SIOCGSKNS), SYS_PTRACE
/// (process_vm to uid-dropped cells), SYS_ADMIN (setns re-home + cgroup/netns).
/// Test hook (NOT a public API): in a child, [`drop_supervisor_caps`] then read
/// back the effective set — returns true iff a dropped cap (CAP_SYS_BOOT) is gone
/// AND a kept cap (CAP_SYS_ADMIN) remains. Guards the cap-drop against regressions.
/// Requires being privileged (the box); unprivileged → no caps to keep → false.
#[doc(hidden)]
pub fn caps_dropped_self_test() -> bool {
    let pid = unsafe { libc::fork() };
    if pid == 0 {
        drop_supervisor_caps();
        #[repr(C)]
        struct Hdr {
            version: u32,
            pid: i32,
        }
        #[repr(C)]
        struct Data {
            effective: u32,
            permitted: u32,
            inheritable: u32,
        }
        let hdr = Hdr {
            version: 0x2008_0522,
            pid: 0,
        };
        let mut data = [
            Data {
                effective: 0,
                permitted: 0,
                inheritable: 0,
            },
            Data {
                effective: 0,
                permitted: 0,
                inheritable: 0,
            },
        ];
        let r = unsafe { libc::syscall(libc::SYS_capget, &hdr as *const _, data.as_mut_ptr()) };
        let eff = data[0].effective;
        let ok = r == 0 && (eff & (1 << 22)) == 0 && (eff & (1 << 21)) != 0; // SYS_BOOT gone, SYS_ADMIN kept
        unsafe { libc::_exit(if ok { 0 } else { 1 }) };
    }
    let mut st = 0i32;
    unsafe { libc::waitpid(pid, &mut st, 0) };
    libc::WIFEXITED(st) && libc::WEXITSTATUS(st) == 0
}

pub(crate) fn drop_supervisor_caps() {
    // bit N = capability N (all kept caps are < 32, so the high word is empty).
    const KEEP: u32 = (1 << 0)  // CHOWN
        | (1 << 1)  // DAC_OVERRIDE
        | (1 << 2)  // DAC_READ_SEARCH
        | (1 << 3)  // FOWNER
        | (1 << 5)  // KILL
        | (1 << 6)  // SETGID
        | (1 << 7)  // SETUID
        | (1 << 10) // NET_BIND_SERVICE
        | (1 << 12) // NET_ADMIN
        | (1 << 19) // SYS_PTRACE
        | (1 << 21); // SYS_ADMIN
    #[repr(C)]
    struct Hdr {
        version: u32,
        pid: i32,
    }
    #[repr(C)]
    struct Data {
        effective: u32,
        permitted: u32,
        inheritable: u32,
    }
    let hdr = Hdr {
        version: 0x2008_0522,
        pid: 0,
    }; // _LINUX_CAPABILITY_VERSION_3
    let data = [
        Data {
            effective: KEEP,
            permitted: KEEP,
            inheritable: 0,
        },
        Data {
            effective: 0,
            permitted: 0,
            inheritable: 0,
        },
    ];
    unsafe { libc::syscall(libc::SYS_capset, &hdr as *const _, data.as_ptr()) };
}

pub(crate) fn harden_supervisor() {
    // x86_64 syscall numbers of escape/escalation primitives.
    let deny: &[u32] = &[
        59,  // execve
        322, // execveat
        101, // ptrace
        165, // mount
        166, // umount2
        155, // pivot_root
        161, // chroot
        175, // init_module
        313, // finit_module
        176, // delete_module
        246, // kexec_load
        320, // kexec_file_load
        169, // reboot
        321, // bpf
        167, // swapon
        168, // swapoff
        250, // keyctl
        248, // add_key
        249, // request_key
        428, // open_tree (new mount API)
        429, // move_mount
        430, // fsopen
        432, // fsmount
        442, // mount_setattr
    ];
    let d = deny.len();
    let mut prog: Vec<libc::sock_filter> = Vec::new();
    prog.push(sf(BPF_LD_W_ABS, 0, 0, 4)); // A = arch
    prog.push(sf(BPF_JMP_JEQ_K, 1, 0, AUDIT_ARCH_X86_64)); // x86_64 → skip the kill
    prog.push(sf(BPF_RET_K, 0, 0, SECCOMP_RET_KILL_PROCESS)); // other arch → kill
    prog.push(sf(BPF_LD_W_ABS, 0, 0, 0)); // A = nr
    for (i, &nr) in deny.iter().enumerate() {
        // jump forward to the KILL at the very end (skip the ALLOW)
        prog.push(sf(BPF_JMP_JEQ_K, (d - i) as u8, 0, nr));
    }
    prog.push(sf(BPF_RET_K, 0, 0, SECCOMP_RET_ALLOW)); // default: allow
    prog.push(sf(BPF_RET_K, 0, 0, SECCOMP_RET_KILL_PROCESS)); // a denied syscall
    let mut fprog = libc::sock_fprog {
        len: prog.len() as u16,
        filter: prog.as_mut_ptr(),
    };
    unsafe {
        // NO_NEW_PRIVS is required for the seccomp filter (and for the eventual
        // privilege drop). Best-effort: never let hardening break a working sandbox.
        libc::prctl(libc::PR_SET_NO_NEW_PRIVS, 1, 0, 0, 0);
        libc::prctl(
            libc::PR_SET_SECCOMP,
            libc::SECCOMP_MODE_FILTER as libc::c_ulong,
            &mut fprog as *mut _ as libc::c_ulong,
            0,
            0,
        );
    }
}

// Sentry-reserved signal used to terminate a sibling thread on `execve` (de_thread).
// A real execve(2) from a multi-threaded process makes the kernel kill every OTHER
// thread in the group (zap_other_threads) so the new image owns the address space
// alone. sentry emulates execve in-process, so the supervisor performs this kernel
// bookkeeping through CTL_DETHREAD_FOR_EXEC: send each sibling SIG_DETHREAD, whose
// sentry-owned process-wide handler exits that thread. SIGRTMAX is the realtime
// signal least likely to be used by a guest; the guest can neither install a handler
// for it (rt_sigaction refuses it, like SIGSYS) nor block it, so it is always
// deliverable and always runs OUR handler.
const SIG_DETHREAD: i32 = 64; // SIGRTMAX

// Handler for SIG_DETHREAD: exit THIS thread. The supervisor owns ring-slot
// cleanup for de_thread because the process survives and its fd table must not be
// dropped. The handler must not return to the about-to-be-overwritten guest image.
// Async-signal-safe: one raw syscall.
extern "C" fn dethread_handler(_sig: c_int, _info: *mut libc::siginfo_t, _ctx: *mut c_void) {
    unsafe {
        host(SYS_EXIT, 0, 0, 0, 0, 0, 0);
    }
}

fn install_handler() {
    unsafe {
        // Give the main cell thread (slot 0) its signal alt-stack BEFORE arming the
        // SIGSYS handler, so the handler always lands on a known-good stack.
        register_altstack();
        let mut sa: libc::sigaction = std::mem::zeroed();
        sa.sa_sigaction = cell_layer1 as *const () as usize;
        // SA_ONSTACK: run the SUD trap handler on the per-thread alt-stack, not the
        // guest's RSP (which may be exhausted/corrupted under concurrency).
        sa.sa_flags = libc::SA_SIGINFO | libc::SA_NODEFER | libc::SA_ONSTACK;
        libc::sigemptyset(&mut sa.sa_mask);
        if libc::sigaction(libc::SIGSYS, &sa, std::ptr::null_mut()) != 0 {
            die(b"sigaction(SIGSYS) failed\n");
        }
        // SIG_DETHREAD: the supervisor-triggered de_thread teardown handler. sigaction is
        // process-wide, so installing it once covers every present and future thread;
        // SA_ONSTACK keeps it off a possibly-trashed guest RSP. Best-effort.
        let mut sd: libc::sigaction = std::mem::zeroed();
        sd.sa_sigaction = dethread_handler as *const () as usize;
        sd.sa_flags = libc::SA_SIGINFO | libc::SA_ONSTACK;
        libc::sigemptyset(&mut sd.sa_mask);
        libc::sigaction(SIG_DETHREAD, &sd, std::ptr::null_mut());
        let mut reserved = sentry_reserved_signal_mask();
        host(
            SYS_RT_SIGPROCMASK,
            1,
            std::ptr::addr_of_mut!(reserved) as u64,
            0,
            8,
            0,
            0,
        );
    }
}
fn arm_sud() {
    let rc = unsafe {
        libc::prctl(
            PR_SET_SYSCALL_USER_DISPATCH,
            PR_SYS_DISPATCH_ON,
            WINDOW_FLOOR as libc::c_ulong,
            (USER_TOP - WINDOW_FLOOR) as libc::c_ulong,
            std::ptr::addr_of!(SELECTOR) as libc::c_ulong,
        )
    };
    if rc != 0 {
        die(b"PR_SET_SYSCALL_USER_DISPATCH failed\n");
    }
}
#[inline(never)]
unsafe fn enter_guest(entry: u64, sp: u64) -> ! {
    std::arch::asm!(
        "mov rsp, {sp}",
        "xor rdx, rdx",
        "xor rbp, rbp",
        "jmp {entry}",
        sp = in(reg) sp, entry = in(reg) entry,
        options(noreturn, nostack),
    );
}

/// Resume a checkpointed guest from its captured register file `g` (18 gregs in
/// glibc order: R8,R9,R10,R11,R12,R13,R14,R15,RDI,RSI,RBP,RBX,RDX,RAX,RCX,RSP,
/// RIP,…). Restores every integer register + the stack and jumps to RIP. SENTINEL
/// captures that need a zero return value normalize `REG_RAX` at capture time;
/// live C4 captures keep the interrupted register file intact.
#[inline(never)]
unsafe fn resume(g: *const i64) -> ! {
    std::arch::asm!(
        "mov rbx, [rax + 16*8]", // rbx = captured RIP (temp)
        "mov rsp, [rax + 15*8]", // rsp = captured RSP
        "push rbx",              // [rsp] = RIP for the final `ret`
        "push qword ptr [rax + 13*8]", // guest RAX, restored after using RAX as pointer
        "mov r8,  [rax + 0*8]",
        "mov r9,  [rax + 1*8]",
        "mov r10, [rax + 2*8]",
        "mov r11, [rax + 3*8]",
        "mov r12, [rax + 4*8]",
        "mov r13, [rax + 5*8]",
        "mov r14, [rax + 6*8]",
        "mov r15, [rax + 7*8]",
        "mov rdi, [rax + 8*8]",
        "mov rsi, [rax + 9*8]",
        "mov rbp, [rax + 10*8]",
        "mov rdx, [rax + 12*8]",
        "mov rcx, [rax + 14*8]",
        "mov rbx, [rax + 11*8]", // guest RBX (after using it for RIP)
        "pop rax",
        "ret",                   // pop RIP, jump
        in("rax") g,
        options(noreturn),
    );
}

/// Called in a freshly-forked instance: re-arm SUD (not inherited across fork),
/// restore the guest's TLS (`%fs`), and resume the guest at the checkpoint regs.
/// Never returns.
unsafe fn resume_pending_syscall() -> ! {
    RESTORE_PENDING_SYSCALL.store(false, Ordering::Release);
    host(SYS_ARCH_PRCTL, ARCH_SET_FS, CELL_FS, 0, 0, 0, 0);
    let nr = RESTORE_PENDING_NR;
    let args = RESTORE_PENDING_ARGS;
    let ret = dispatch_simple(
        nr, args[0], args[1], args[2], args[3], args[4], args[5], None,
    );
    CAPTURED[REG_RAX] = ret;
    host(SYS_ARCH_PRCTL, ARCH_SET_FS, GUEST_FS, 0, 0, 0, 0);
    resume(std::ptr::addr_of!(CAPTURED) as *const i64);
}

unsafe fn resume_instance() -> ! {
    host(
        157,
        PR_SET_SYSCALL_USER_DISPATCH as u64,
        PR_SYS_DISPATCH_ON,
        WINDOW_FLOOR,
        USER_TOP - WINDOW_FLOOR,
        std::ptr::addr_of!(SELECTOR) as u64,
        0,
    );
    if RESTORE_PENDING_SYSCALL.load(Ordering::Acquire) {
        resume_pending_syscall();
    }
    host(SYS_ARCH_PRCTL, ARCH_SET_FS, GUEST_FS, 0, 0, 0, 0);
    resume(std::ptr::addr_of!(CAPTURED) as *const i64);
}
fn decode_exit(st: c_int) -> i32 {
    if st & 0x7f == 0 {
        (st >> 8) & 0xff
    } else {
        128 + (st & 0x7f)
    }
}
fn now_ns() -> u64 {
    let mut ts = [0i64; 2];
    unsafe {
        host(
            SYS_CLOCK_GETTIME,
            1, /*MONOTONIC*/
            ts.as_mut_ptr() as u64,
            0,
            0,
            0,
            0,
        )
    };
    ts[0] as u64 * 1_000_000_000 + ts[1] as u64
}
/// The zygote loop after the checkpoint longjmp: driven (`Pool`) or self-contained
/// bench (`zygote_bench`).
fn zygote_loop() -> ! {
    if ZYG_DRIVEN.load(Ordering::Relaxed) {
        driven_zygote_loop()
    } else {
        bench_zygote_loop()
    }
}

fn allow_ptrace_from(pid: i32) {
    if pid > 1 {
        unsafe {
            // Best-effort Yama grant for live C4 capture: the single-threaded
            // launcher is the trusted process that later ptrace-stops this
            // zygote-forked child to read memory/register state.
            let _ = host(SYS_PRCTL, PR_SET_PTRACER, pid as u64, 0, 0, 0, 0);
        }
    }
}

const ZYG_RESTORES: u32 = 2000;
/// Self-contained bench: fork-from-warm restore N times, measure latency, stash
/// the result in `ZYG` for the supervisor to report, then exit.
fn bench_zygote_loop() -> ! {
    let mut total = 0u64;
    let mut min = u64::MAX;
    let mut first_code = -1i32;
    for i in 0..ZYG_RESTORES {
        let t0 = now_ns();
        let pid = unsafe { host(SYS_FORK, 0, 0, 0, 0, 0, 0) };
        if pid == 0 {
            unsafe { resume_instance() };
        }
        let mut st: c_int = 0;
        unsafe {
            host(
                SYS_WAIT4,
                pid as u64,
                &mut st as *mut c_int as u64,
                0,
                0,
                0,
                0,
            )
        };
        if i == 0 {
            first_code = decode_exit(st);
        }
        let dt = now_ns() - t0;
        total += dt;
        min = min.min(dt);
    }
    unsafe {
        if !ZYG.is_null() {
            (*ZYG).exit = first_code;
            (*ZYG).mean_ns = total / ZYG_RESTORES as u64;
            (*ZYG).done = min as u32; // reuse: stash min (ns) for reporting
            (*ZYG).ready = 1;
        }
        host(SYS_EXIT_GROUP, 0, 0, 0, 0, 0, 0);
    }
    unreachable!()
}

/// Driven loop (the parked warm cell): signal `ready`, then per acquire fork an
/// INSTANCE with its own ring slot (so it can do servicer-backed I/O), resume the
/// guest in it, reap it, and report the exit code via `ZYG`. The instance's own
/// `exit_group`→`CTL_REAP` frees its slot supervisor-side.
fn driven_zygote_loop() -> ! {
    let warm = unsafe { host(SYS_GETPID, 0, 0, 0, 0, 0, 0) as i32 };
    let launcher = unsafe { host(SYS_GETPPID, 0, 0, 0, 0, 0, 0) as i32 };
    let req = ring_word(unsafe { std::ptr::addr_of_mut!((*ZYG).req) });
    let done = ring_word(unsafe { std::ptr::addr_of_mut!((*ZYG).done) });
    // Signal "checkpointed/ready" to the controller.
    unsafe {
        let r = ring_word(std::ptr::addr_of_mut!((*ZYG).ready));
        r.store(1, Ordering::Release);
        host(SYS_FUTEX, r.as_ptr() as u64, FUTEX_WAKE, 1, 0, 0, 0);
    }
    let mut last = 0u32;
    loop {
        // Wait for an acquire (req bump); a sentinel value asks us to quit.
        loop {
            let cur = req.load(Ordering::Acquire);
            if cur != last {
                break;
            }
            unsafe {
                host(
                    SYS_FUTEX,
                    req.as_ptr() as u64,
                    FUTEX_WAIT,
                    last as u64,
                    0,
                    0,
                    0,
                )
            };
        }
        let r = req.load(Ordering::Acquire);
        last = r;
        if r == u32::MAX {
            unsafe { host(SYS_EXIT_GROUP, 0, 0, 0, 0, 0, 0) };
        }
        // The supervisor published `run_mode` BEFORE its `req` store-release (in
        // zygote_acquire), so we observe it correctly after our `req` load-acquire
        // above. 0 = run-to-completion (the original path), 1 = detached running
        // instance (L0b warm daemon), 2 = release (kill a detached instance).
        let mode = unsafe { std::ptr::read_volatile(std::ptr::addr_of!((*ZYG).run_mode)) };

        // Mode 2 — RELEASE: reap the detached instance named in `inst_pid`.
        // No fork. (CTL_REAP closes its host fds + recycles its ring slot.)
        if mode == 2 {
            // RELEASE. The SUPERVISOR (trusted, not seccomp-walled) has already
            // SIGKILL'd the instance — a CELL cannot issue `kill` (it isn't in the
            // seccomp allowlist ⇒ SECCOMP_RET_KILL_PROCESS would kill the warm cell
            // itself). We are the instance's PARENT (we forked it in mode 1), so our
            // job is only to REAP its zombie; `wait4` IS allowlisted ("reap its own
            // forked children"). The supervisor frees the ring slot + host fds
            // (reap_dead_pid) after we report done.
            let victim = unsafe { std::ptr::read_volatile(std::ptr::addr_of!((*ZYG).inst_pid)) };
            if victim > 1 {
                let mut st: c_int = 0;
                unsafe {
                    host(
                        SYS_WAIT4,
                        victim as u64,
                        &mut st as *mut c_int as u64,
                        0,
                        0,
                        0,
                        0,
                    )
                };
            }
            unsafe {
                (*ZYG).exit = 0;
                done.store(r, Ordering::Release);
                host(SYS_FUTEX, done.as_ptr() as u64, FUTEX_WAKE, 1, 0, 0, 0);
            }
            continue;
        }

        // Fork an instance with its own slot (like the SYS_FORK process path), so
        // its post-checkpoint syscalls are serviced.
        let slot = alloc_slot();
        let code: i32 = if slot as usize >= MAX_SLOTS {
            -11
        } else {
            delegate(CTL_ENSURE_SERVICER, slot as u64, 0, 0, 0, 0, 0);
            delegate(CTL_FORK_TABLE, slot as u64, SYS_FORK as u64, 0, 0, 0, 0);
            unsafe { (*ring_at(slot as u64)).fork_parent = warm };
            let pid = unsafe { host(SYS_FORK, 0, 0, 0, 0, 0, 0) };
            if pid == 0 {
                set_slot(slot as u64);
                unsafe {
                    allow_ptrace_from(launcher);
                    set_ring_owner(
                        slot as u64,
                        host(SYS_GETPID, 0, 0, 0, 0, 0, 0) as i32,
                        host(SYS_GETTID, 0, 0, 0, 0, 0, 0) as i32,
                    );
                    resume_instance();
                }
            } else if pid < 0 {
                unsafe { (*ring_at(slot as u64)).fork_parent = 0 };
                delegate(CTL_FORK_CANCEL, slot as u64, 0, 0, 0, 0, 0);
                -11
            } else if mode == 1 {
                // Mode 1 — DETACHED running instance (a warm daemon): record its pid
                // and report `done` NOW; do NOT WAIT4/REAP. It stays alive — owning
                // its ring slot + servicer (so its serviced loop-listener I/O keeps
                // working) — until a later RELEASE (mode 2) kills + reaps it. The
                // supervisor adds it to running_instances() so sweep_stragglers spares
                // it across client execs.
                delegate(CTL_BIND_SLOT, slot as u64, pid as u64, 0, 0, 0, 0);
                unsafe {
                    (*ZYG).inst_pid = pid as i32;
                    (*ZYG).exit = 0;
                    done.store(r, Ordering::Release);
                    host(SYS_FUTEX, done.as_ptr() as u64, FUTEX_WAKE, 1, 0, 0, 0);
                }
                continue; // skip the shared run-to-completion exit-reporting tail
            } else {
                delegate(CTL_BIND_SLOT, slot as u64, pid as u64, 0, 0, 0, 0);
                let mut st: c_int = 0;
                unsafe {
                    host(
                        SYS_WAIT4,
                        pid as u64,
                        &mut st as *mut c_int as u64,
                        0,
                        0,
                        0,
                        0,
                    )
                };
                // Reap the instance: close its host fds (delivers a capture pipe's
                // EOF) and recycle its ring slot. Without this each acquire leaks
                // the instance's fd table + slot (slot exhaustion after MAX_SLOTS).
                delegate(CTL_REAP, pid as u64, 0, 0, 0, 0, 0);
                decode_exit(st)
            }
        };
        unsafe {
            (*ZYG).exit = code;
            (*ZYG).inst_pid = 0; // mode 0 leaves no live instance
            done.store(r, Ordering::Release);
            host(SYS_FUTEX, done.as_ptr() as u64, FUTEX_WAKE, 1, 0, 0, 0);
        }
    }
}

/// The CELL: load + seal + run. `path` is the guest-visible ELF path; when `root`
/// is set, the exe AND its interpreter are loaded from within the rootfs (the
/// guest's `/lib/ld-...` is in the image, not on the host).
/// binfmt_script: a `#!` script can't be `load_elf`'d. Resolve it like the kernel —
/// read the interpreter line, and return the INTERPRETER as the program to load with
/// argv rebuilt to `[interp, optarg?, script, original_args[1..]]`. Loops (bounded at
/// 4, like the kernel) for an interpreter that is itself a script. Returns the final
/// `(path, file_bytes, argv)` ready for `load_elf` + `build_stack`. A plain ELF passes
/// straight through. Used by the initial cell load AND execve emulation, so an exec'd
/// shell/python/entrypoint script (e.g. a Docker `/docker-entrypoint.sh`, or a staged
/// `#!/bin/sh` program) runs instead of dying with "not an ELF64-LE".
fn resolve_shebang(
    path: &str,
    args: &[String],
    read: impl Fn(&str) -> Option<Vec<u8>>,
) -> Option<(String, Vec<u8>, Vec<String>)> {
    let mut path = path.to_string();
    let mut argv: Vec<String> = args.to_vec();
    let mut bytes = read(&path)?;
    for _ in 0..4 {
        if bytes.len() < 2 || &bytes[0..2] != b"#!" {
            break;
        }
        // The interpreter line: up to the first newline, capped (kernel reads one
        // BINPRM_BUF_SIZE block). Split into the interpreter and a SINGLE optional arg
        // (everything after the first whitespace, trimmed — kernel binfmt_script).
        let end = bytes
            .iter()
            .take(256)
            .position(|&b| b == b'\n')
            .unwrap_or_else(|| bytes.len().min(255));
        let line = String::from_utf8_lossy(&bytes[2..end]);
        let line = line.trim();
        let (interp, optarg) = match line.find([' ', '\t']) {
            Some(i) => {
                let a = line[i..].trim();
                (
                    line[..i].to_string(),
                    (!a.is_empty()).then(|| a.to_string()),
                )
            }
            None => (line.to_string(), None),
        };
        if interp.is_empty() {
            die_code(b"bad shebang interpreter\n", 127);
        }
        let mut next = Vec::with_capacity(argv.len() + 2);
        next.push(interp.clone());
        if let Some(a) = optarg {
            next.push(a);
        }
        next.push(path.clone()); // the script becomes the interpreter's file argument
        next.extend(argv.iter().skip(1).cloned());
        argv = next;
        path = interp;
        bytes = read(&path)?;
    }
    Some((path, bytes, argv))
}

fn cell_main(path: &str, args: &[String], root: Option<std::path::PathBuf>) -> ! {
    // Stash for execve emulation: the rootfs, and the cell's own TLS (captured now,
    // while %fs is still the cell's glibc TLS — before the guest sets its own).
    unsafe {
        CELL_ROOT = root.clone();
        host(
            SYS_ARCH_PRCTL,
            ARCH_GET_FS,
            std::ptr::addr_of_mut!(CELL_FS) as u64,
            0,
            0,
            0,
            0,
        );
    }
    // Load the guest ELF + its interpreter CONFINED to the rootfs (symlinks resolve
    // within the image, never escaping to the host — see `read_in_root`). An
    // unreadable exe exits 127 (the shell's "not executable / not found"
    // convention) so the exec server's EXIT frame carries 127 — the host decodes
    // it cleanly instead of seeing a generic failure. The exec/spawn path resolves
    // a bare argv[0] against PATH (host-side, in `run_one`) before we get here, so
    // `path` is normally a concrete in-root path by now.
    // Resolve `#!` scripts to their interpreter (binfmt_script) BEFORE the ELF load,
    // rebuilding argv accordingly — so an exec'd shell/python/entrypoint script runs
    // instead of dying in load_elf. A plain ELF passes through unchanged. Pre-seal, so
    // reads go directly through the rootfs.
    let (path, bytes, args) = resolve_shebang(path, args, |p| read_in_root(&root, p).ok())
        .unwrap_or_else(|| die_code(b"cannot read guest exe\n", 127));
    let (_path, args) = (path.as_str(), args.as_slice());
    // ET_DYN (PIE) main exe loads at a base; ET_EXEC at its fixed vaddrs. These
    // loads + the interpreter happen PRE-seal (direct fs), which is fine — only the
    // guest's own shared libraries (loaded by ld.so post-seal) go via delegation.
    let exe_base = if rd_u16(&bytes, 16) == 3 {
        EXE_DYN_BASE
    } else {
        0
    };
    let exe = load_elf(&bytes, exe_base);

    unsafe {
        let h = libc::mmap(
            std::ptr::null_mut(),
            HEAP_SIZE as usize,
            libc::PROT_READ | libc::PROT_WRITE,
            libc::MAP_PRIVATE | libc::MAP_ANONYMOUS,
            -1,
            0,
        );
        HEAP_BASE = h as u64;
        HEAP_CUR.store(h as u64, std::sync::atomic::Ordering::Release);
        HEAP_END = h as u64 + HEAP_SIZE;
    }

    // Dynamic: load the real ld.so as interpreter and jump to IT; it relocates the
    // exe and loads shared libs (via delegated open + decomposed file mmap).
    let (jump_to, interp_base) = if let Some(ref ip) = exe.interp {
        let ipath = String::from_utf8_lossy(ip).to_string();
        let ibytes =
            read_in_root(&root, &ipath).unwrap_or_else(|_| die_code(b"cannot read ld.so\n", 127));
        let interp = load_elf(&ibytes, INTERP_BASE);
        (interp.entry, Some(INTERP_BASE))
    } else {
        (exe.entry, None)
    };

    let argv: Vec<Vec<u8>> = args.iter().map(|s| s.as_bytes().to_vec()).collect();
    // Guest env: the merged base (image `Env` + per-request env, already overlaid by
    // the exec server) if any, else the built-in default. Either way we GUARANTEE a
    // `PATH` and `HOME` so a caller that passes just one var (e.g. `X=1`) doesn't
    // lose them — mirroring the in-guest agent's `build_env`, which starts from the
    // agent's own env (PATH/HOME present) and layers the request on top. The empty
    // case still gets the full `PATH`/`PWD`/`LANG` default.
    let envp: Vec<Vec<u8>> = {
        let e = guest_env().lock().unwrap();
        if e.is_empty() {
            vec![
                {
                    let mut v = b"PATH=".to_vec();
                    v.extend_from_slice(DEFAULT_PATH);
                    v
                },
                b"PWD=/".to_vec(),
                b"LANG=C".to_vec(),
            ]
        } else {
            let mut out = e.clone();
            fn has(out: &[Vec<u8>], k: &[u8]) -> bool {
                out.iter()
                    .any(|kv| kv.starts_with(k) && kv.get(k.len()) == Some(&b'='))
            }
            if !has(&out, b"PATH") {
                let mut v = b"PATH=".to_vec();
                v.extend_from_slice(DEFAULT_PATH);
                out.push(v);
            }
            if !has(&out, b"HOME") {
                out.push(b"HOME=/".to_vec());
            }
            out
        }
    };
    // Seed this cell's guest cwd (supervisor resolves AT_FDCWD + serves getcwd from
    // it) if the caller requested a starting directory. A requested cwd that doesn't
    // exist as a directory in the rootfs FAILS the launch (exit 127) — the no-virt
    // analogue of the KVM agent's pre-exec `chdir`, which errors out rather than
    // silently running in `/`. (Done pre-seal, while we can still openat the rootfs.)
    if let Some(c) = guest_cwd_init().lock().unwrap().clone() {
        let cwd_s = String::from_utf8_lossy(&c).into_owned();
        if !dir_exists_in_root(&root, &cwd_s) {
            die_code(b"sentry: requested working directory does not exist\n", 127);
        }
        let me = unsafe { host(SYS_GETPID, 0, 0, 0, 0, 0, 0) } as i32;
        cwds().lock().unwrap().insert(me, c);
    }
    let initial_creds = run_uid().lock().unwrap().unwrap_or((0, 0));
    cred_reset(initial_creds.0, initial_creds.1);
    cell_umask_reset();

    let sp = build_stack(&exe, interp_base, &argv, &envp);

    // Join the guest cgroup (if any) BEFORE sealing: writing our pid to
    // cgroup.procs moves the cell (and every later fork/thread) under the
    // memory/pids/cpu caps. Done here, pre-seal, while we can still open host
    // paths directly. Best-effort — a failure leaves the cell uncapped, not dead.
    if let Some(dir) = cgroup_path().lock().unwrap().clone() {
        let me = unsafe { host(SYS_GETPID, 0, 0, 0, 0, 0, 0) };
        let _ = std::fs::write(dir.join("cgroup.procs"), me.to_string());
    }

    // Drop the guest to an unprivileged host uid/gid if requested (defense in
    // depth: a hostile guest is host-unprivileged even though it's sealed). Done
    // pre-seal, AFTER every root-needed step (the ELF/interp reads + cgroup join)
    // — post-seal all fs/io is the SUPERVISOR's (still root, confined), so the
    // dropped cell loses no capability. The root supervisor can still
    // `process_vm_*` the dropped cell (CAP_SYS_PTRACE), validated on the box.
    // Order: supplementary groups, then gid, then uid (uid last — once dropped we
    // can't change gid). The guest's `getuid` (cell-local) reports this uid.
    if let Some((uid, gid)) = *run_uid().lock().unwrap() {
        unsafe {
            host(SYS_SETGROUPS, 0, 0, 0, 0, 0, 0);
            host(SYS_SETGID, gid as u64, 0, 0, 0, 0, 0);
            host(SYS_SETUID, uid as u64, 0, 0, 0, 0, 0);
        }
    }

    unregister_host_libc_rseq();
    unsafe { reset_exec_signal_dispositions() };
    install_handler();
    arm_sud();
    install_wall(); // ← the cell is now SEALED; only cell-local syscalls survive

    // Warm/zygote mode: checkpoint at the SENTINEL. `sigsetjmp` returns 0 on the
    // warming run (enter the guest; it hits the SENTINEL → the handler captures +
    // longjmps back here with 1) and 1 from the checkpoint → become the zygote.
    if WARM_MODE.load(Ordering::Relaxed) {
        // savesigs=0: don't save/restore the signal mask across the checkpoint
        // longjmp — the sealed cell can't call `rt_sigprocmask` (not allowlisted),
        // and we never block signals, so the mask needs no restoring.
        let from_checkpoint =
            unsafe { __sigsetjmp(std::ptr::addr_of_mut!(JMPBUF) as *mut c_void, 0) };
        if from_checkpoint == 0 {
            unsafe { enter_guest(jump_to, sp) };
        } else {
            zygote_loop();
        }
    }

    unsafe { enter_guest(jump_to, sp) };
}

#[cfg(all(target_os = "linux", target_arch = "x86_64"))]
fn cell_restore_main(
    snapshot_dir: &str,
    restore_vpid: state_snap::Vpid,
    restore_warm: bool,
    stop_before_resume: bool,
    root: Option<std::path::PathBuf>,
) -> ! {
    unsafe {
        CELL_ROOT = root;
        host(
            SYS_ARCH_PRCTL,
            ARCH_GET_FS,
            std::ptr::addr_of_mut!(CELL_FS) as u64,
            0,
            0,
            0,
            0,
        );
    }

    let dir = std::path::Path::new(snapshot_dir);
    let state = read_state_snapshot_dir(dir).unwrap_or_else(|e| {
        let msg = format!("cannot read sentry state snapshot: {e}\n");
        die_code(msg.as_bytes(), 127)
    });
    if !state
        .cpu_feature_baseline
        .satisfied_by(&state_snap::CpuFeatures::current())
    {
        die_code(b"sentry restore: CPU feature baseline not satisfied\n", 127);
    }
    let ram = std::fs::File::open(dir.join("mem.blob"))
        .unwrap_or_else(|_| die_code(b"cannot read sentry mem.blob\n", 127));
    let image = state
        .mem
        .proc_image(restore_vpid)
        .unwrap_or_else(|| die_code(b"sentry restore: missing requested memory image\n", 127));
    for rec in image.regions {
        unsafe {
            memimage::restore_region(rec, &ram, image.blob_off)
                .unwrap_or_else(|_| die_code(b"sentry restore: map memory failed\n", 127));
        }
    }
    let thread = image
        .threads
        .first()
        .unwrap_or_else(|| die_code(b"sentry restore: missing thread regs\n", 127));
    unsafe {
        CAPTURED = thread.gregs;
        GUEST_FS = thread.fs;
        if let Some((nr, args)) = decode_pending_syscall(&thread.xsave) {
            RESTORE_PENDING_NR = nr;
            RESTORE_PENDING_ARGS = args;
            RESTORE_PENDING_SYSCALL.store(true, Ordering::Release);
        } else {
            RESTORE_PENDING_SYSCALL.store(false, Ordering::Release);
        }
    }

    if let Some(dir) = cgroup_path().lock().unwrap().clone() {
        let me = unsafe { host(SYS_GETPID, 0, 0, 0, 0, 0, 0) };
        let _ = std::fs::write(dir.join("cgroup.procs"), me.to_string());
    }
    let initial_creds = run_uid().lock().unwrap().unwrap_or((0, 0));
    cred_reset(initial_creds.0, initial_creds.1);
    cell_umask_reset();
    if let Some((uid, gid)) = *run_uid().lock().unwrap() {
        unsafe {
            host(SYS_SETGROUPS, 0, 0, 0, 0, 0, 0);
            host(SYS_SETGID, gid as u64, 0, 0, 0, 0, 0);
            host(SYS_SETUID, uid as u64, 0, 0, 0, 0, 0);
        }
    }

    unregister_host_libc_rseq();
    unsafe { reset_exec_signal_dispositions() };
    install_handler();
    arm_sud();
    if !restore_warm && stop_before_resume {
        let me = unsafe { host(SYS_GETPID, 0, 0, 0, 0, 0, 0) };
        unsafe { host(SYS_KILL, me as u64, libc::SIGSTOP as u64, 0, 0, 0, 0) };
    }
    install_wall();
    if restore_warm {
        zygote_loop();
    }
    unsafe { resume_instance() };
}

/// Run a guest ELF under the sentry, confined to `root` if given, and return the
/// guest's exit code. Forks a sandbox launcher (which itself forks the per-tenant
/// supervisor + the sealed cell); the CALLER's process is untouched (no globals
/// are set in it), so this is safe to call repeatedly / from a service.
///
/// `args` are the guest's arguments *after* argv[0] (argv[0] is set to `elf`). The
/// supervisor mediates fs/io/net. With `root` set, `elf` is a **guest-visible**
/// path (e.g. `/bin/busybox`) — the exe, its interpreter, and every runtime path
/// are loaded/resolved from within the rootfs (`openat2(RESOLVE_IN_ROOT)`); the
/// host fs is unreachable. With `root` None, `elf` is a host path.
pub fn run(
    elf: &std::path::Path,
    args: &[String],
    root: Option<&std::path::Path>,
) -> std::io::Result<i32> {
    run_in(elf, args, root, &SandboxCfg::default())
}

/// As [`run`], but also returns the structured [`SentryError`] reason (e.g. a
/// forbidden-syscall [`SentryError::SeccompViolation`]) when the guest ended other
/// than a plain exit — so a sandbox-policy kill is distinguishable from a
/// workload's own non-zero exit (both flatten to the same `128+sig` code). The
/// integer code is identical to [`run`].
pub fn run_classified(
    elf: &std::path::Path,
    args: &[String],
    root: Option<&std::path::Path>,
) -> std::io::Result<(i32, Option<SentryError>)> {
    run_in_classified(elf, args, root, &SandboxCfg::default())
}

/// Per-sandbox configuration applied by the supervisor: outbound-TCP egress
/// policy + bind-mounts/volumes. Built by [`Sandbox`] and passed down to
/// [`sandbox_main`], which sets the egress global and opens the mount dirfds.
///
/// `netns` defaults to `true` (see the manual [`Default`] impl below): a fresh
/// sandbox/cell is network-ISOLATED by default — its own loopback + port space —
/// so a guest can't reach the host's (or another tenant's) `127.0.0.1`. This is
/// the isolation-safe default; opt OUT (a host-netns sandbox, e.g. a workload
/// meant to be host-reachable via `expose_tcp`) with [`Sandbox::without_netns`].
#[derive(Clone, serde::Serialize, serde::Deserialize)]
struct SandboxCfg {
    /// `allow_all` (default) / `deny_private` / `allowlist:<cidrs>` /
    /// `denylist:<cidrs>` (see [`crate::vmm::egress_policy`]).
    egress: Option<String>,
    /// `(guest_path, host_path, readonly)` bind-mounts.
    mounts: Vec<(std::path::PathBuf, std::path::PathBuf, bool)>,
    /// cgroup-v2 resource limits applied to the GUEST (cell) process tree.
    limits: Limits,
    /// `(uid, gid)` to drop the guest to before sealing (`None` = no drop).
    run_uid: Option<(u32, u32)>,
    /// A STABLE cgroup dir name shared by all of a `Sandbox`'s execs (`None` =
    /// per-call `sentry-<pid>`). Lets concurrent execs share one resource cap.
    cgroup_name: Option<String>,
    /// Warm/zygote mode: the cell checkpoints at the SENTINEL syscall and the
    /// supervisor benches fork-from-warm restore latency.
    warm: bool,
    /// Isolate the sandbox in its own network namespace (own port space + own
    /// loopback). DEFAULT (`true`): the guest's `127.0.0.1` is private, so it
    /// can't reach the host's or another tenant's loopback services. Egress to
    /// non-loopback destinations and published-port ingress are re-homed into the
    /// host netns (see [`rehome_to_host_netns`]). Opt out (shared host netns) only
    /// for a workload that must `bind` a host-reachable listener directly (the
    /// `expose_tcp` ingress path).
    netns: bool,
    /// Ports a netns sandbox publishes to the host (a guest bind to one is
    /// re-homed into the host netns). Empty = nothing published.
    published_ports: Vec<u16>,
    /// Guest environment as `(KEY, VALUE)` pairs. Empty = the built-in default
    /// (`PATH`/`PWD`/`LANG`); non-empty replaces it wholesale.
    env: Vec<(String, String)>,
    /// Initial guest working directory (guest-absolute). `None` = `/`.
    cwd: Option<String>,
    /// Bounded-execution watchdog: max wall-time the supervisor waits for an
    /// `exec`/`exec_capture` cell before SIGKILLing the whole cell tree and
    /// returning [`SentryError::Timeout`]. `0` = unlimited (default; matches the VM
    /// backend — no behavior change). Production single-tenant sets a ceiling so a
    /// wedged workload (a stuck guest, or the deferred C1 reentrancy hang) can never
    /// hang the Pool: the exec is bounded + reported, and the Pool serves the next.
    exec_timeout_ms: u64,
    /// Bake-time warmup mode: preserve orphaned/background children of an exec as
    /// owned live instances so a subsequent C4 snapshot captures them. Normal execs
    /// keep this false and sweep stragglers to guarantee capture-pipe EOF.
    preserve_stragglers: bool,
}

/// `netns` defaults to `true` (network-isolated): every cell spawned through the
/// low-level [`run`]/[`run_in`]/[`spawn`]/[`zygote_bench`] surface — and every
/// [`Sandbox`] (which seeds its own `netns` from here) — is isolation-safe by
/// default. The only fields that differ from `bool::default()`/`Default::default`
/// are `netns`; everything else is the type's zero value.
impl Default for SandboxCfg {
    fn default() -> Self {
        SandboxCfg {
            egress: None,
            mounts: Vec::new(),
            limits: Limits::default(),
            run_uid: None,
            cgroup_name: None,
            warm: false,
            netns: true,
            published_ports: Vec::new(),
            env: Vec::new(),
            cwd: None,
            exec_timeout_ms: 0,
            preserve_stragglers: false,
        }
    }
}

/// Where a spawned sandbox's guest stdio is wired. Each `Some(fd)` is `dup2`'d
/// onto the guest's 0/1/2 (the supervisor seeds the guest fds from its own
/// stdio); `None` inherits the caller's. Used by the exec server (three pipes)
/// and `exec_capture` (one pipe on stdout+stderr).
#[derive(Default, Clone, Copy)]
struct Stdio {
    stdin: Option<c_int>,
    stdout: Option<c_int>,
    stderr: Option<c_int>,
    /// tty mode: after wiring 0/1/2 to a pty slave, make the sandbox tree a new
    /// session leader (`setsid`) and acquire the slave as its controlling
    /// terminal (`TIOCSCTTY`) — so the guest's `isatty`, job control, and
    /// terminal signal delivery (^C → SIGINT) work. Mutually exclusive with the
    /// `new_group` `setpgid` (setsid already starts a fresh process group whose
    /// leader pid == the tree pid, so `killpg(-pid)` still tears the tree down).
    ctty: bool,
    /// Per-exec STRUCTURED-ERROR channel (write-end of a `O_CLOEXEC` pipe): the
    /// supervisor writes a one-byte [`SentryError`] tag here just before `_exit`
    /// when the cell ended for a structured reason (e.g. a seccomp-wall kill), so
    /// the library can recover the reason the supervisor's `_exit(128+sig)` flattens
    /// away. `None` = no reason channel (the integer code is enough). It is NOT a
    /// guest fd: it's threaded into [`supervisor_main`] and deliberately spared by
    /// the close_range fd-sweep (see [`spawn_sandbox`]); the cell never sees it.
    status_wr: Option<c_int>,
}

/// cgroup-v2 resource caps for the guest. Each is optional; only set fields are
/// written. Applied to the CELL tree (cell + its forks/threads), NOT the trusted
/// supervisor — so they bound guest memory/processes/CPU as a unit. Best-effort:
/// if cgroup v2 isn't available/writable (an unprivileged host), the sandbox
/// still runs, just uncapped (the wall + page-table isolation are unaffected).
#[derive(Default, Clone, serde::Serialize, serde::Deserialize)]
struct Limits {
    memory_max: Option<u64>, // bytes → memory.max
    pids_max: Option<u64>,   // count → pids.max
    cpu_max: Option<String>, // "QUOTA PERIOD" microseconds → cpu.max
}
impl Limits {
    fn any(&self) -> bool {
        self.memory_max.is_some() || self.pids_max.is_some() || self.cpu_max.is_some()
    }
}

/// Why a sentry cell ended other than a plain non-zero `exit()`. Surfaced
/// alongside the exit code so a caller can tell a **sandbox-policy kill** apart
/// from a workload's own non-zero status — both of which today collapse to the
/// same `128+signal` integer (e.g. exit `159` is ambiguous: a forbidden-syscall
/// SECCOMP kill *or* a guest that raised `SIGSYS`/exited `159` itself).
///
/// `#[non_exhaustive]`: more variants (`Oom`, `Timeout`, …) land as the limits
/// surface grows the signals it can attribute; match with a `_ =>` arm.
#[non_exhaustive]
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum SentryError {
    /// The cell hit the per-cell **seccomp wall** ([`install_wall`]) on a syscall
    /// outside the cell-local allowlist and the kernel `SECCOMP_RET_KILL_PROCESS`'d
    /// it — i.e. a sandbox-policy denial, NOT a workload error. `signum` is the
    /// terminating signal (always `SIGSYS` = 31). Reported as exit `128+signum`.
    SeccompViolation { signum: i32 },
    /// The cell was terminated by a signal other than the seccomp wall (e.g.
    /// `SIGKILL` from a cgroup OOM / `cpu.max` throttle, or a host `kill`). Reported
    /// as exit `128+signum`.
    Signaled { signum: i32 },
    /// The exec exceeded the Pool/Sandbox `exec_timeout` and the supervisor SIGKILL'd
    /// the cell tree (bounded-execution watchdog). Reported as exit `137` (128+SIGKILL).
    /// Distinguishes a deadline kill from a workload that exited 137 on its own.
    Timeout,
}
impl SentryError {
    /// The exit code this error is reported as (`128 + signum`), so a caller that
    /// only has the integer code still sees the conventional value.
    pub fn exit_code(self) -> i32 {
        match self {
            SentryError::SeccompViolation { signum } | SentryError::Signaled { signum } => {
                128 + signum
            }
            SentryError::Timeout => 128 + libc::SIGKILL,
        }
    }
}
impl std::fmt::Display for SentryError {
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
        match self {
            SentryError::SeccompViolation { signum } => {
                write!(
                    f,
                    "cell killed by the seccomp wall on a forbidden syscall (SIG{signum})"
                )
            }
            SentryError::Signaled { signum } => write!(f, "cell terminated by signal {signum}"),
            SentryError::Timeout => write!(f, "cell SIGKILL'd after exceeding the exec timeout"),
        }
    }
}
impl std::error::Error for SentryError {}

// Wire tags for the structured-error reason, carried out of the supervisor
// alongside the exit code: over the [`Pool`] control socket (the 8-byte reply,
// see [`reply_status`]) and over the per-exec status pipe (one byte, see
// [`spawn_sandbox`]/[`supervisor_main`]). 0 = no error (plain exit).
const ERR_NONE: u32 = 0;
const ERR_SECCOMP: u32 = 1;
const ERR_SIGNALED: u32 = 2;
const ERR_TIMEOUT: u32 = 3;
fn err_to_tag(e: Option<SentryError>) -> u32 {
    match e {
        None => ERR_NONE,
        Some(SentryError::SeccompViolation { .. }) => ERR_SECCOMP,
        Some(SentryError::Signaled { .. }) => ERR_SIGNALED,
        Some(SentryError::Timeout) => ERR_TIMEOUT,
    }
}
/// Reconstruct the error from a `(code, tag)` pair (the only place `signum` is
/// recovered, from `code - 128`). `code < 128` with a non-`NONE` tag can't happen
/// (the producer only tags signaled deaths), so a defensive `signum` floor of 0.
fn tag_to_err(code: i32, tag: u32) -> Option<SentryError> {
    let signum = (code - 128).max(0);
    match tag {
        ERR_SECCOMP => Some(SentryError::SeccompViolation { signum }),
        ERR_SIGNALED => Some(SentryError::Signaled { signum }),
        ERR_TIMEOUT => Some(SentryError::Timeout),
        _ => None,
    }
}
/// Classify a `waitpid` raw status into `(exit_code, reason)`: a normal exit has
/// no reason; a SIGSYS kill of a (walled) cell is a [`SentryError::SeccompViolation`]
/// (the supervisor installs the wall on every cell, so a cell's SIGSYS death IS a
/// wall kill — the guest's *own* SIGSYS is SUD-trapped, never delivered fatally);
/// any other signal is [`SentryError::Signaled`]. Mirrors [`wait_exit`]'s integer
/// mapping (`128+sig`) so callers that ignore the reason are byte-for-byte unchanged.
fn classify_status(st: c_int) -> (i32, Option<SentryError>) {
    if libc::WIFSIGNALED(st) {
        let signum = libc::WTERMSIG(st);
        let err = if signum == libc::SIGSYS {
            SentryError::SeccompViolation { signum }
        } else {
            SentryError::Signaled { signum }
        };
        (128 + signum, Some(err))
    } else if libc::WIFEXITED(st) {
        (libc::WEXITSTATUS(st), None)
    } else {
        (-1, None)
    }
}

/// As [`run`], but with a [`SandboxCfg`] (egress policy + mounts).
fn run_in(
    elf: &std::path::Path,
    args: &[String],
    root: Option<&std::path::Path>,
    cfg: &SandboxCfg,
) -> std::io::Result<i32> {
    run_in_classified(elf, args, root, cfg).map(|(c, _)| c)
}

/// As [`run_in`], but also returns the structured [`SentryError`] reason (e.g. a
/// seccomp-wall kill) when the cell ended other than a plain exit. The per-exec
/// supervisor reports the reason over a status pipe (see [`spawn_sandbox`]); the
/// integer code is identical to [`run_in`].
fn run_in_classified(
    elf: &std::path::Path,
    args: &[String],
    root: Option<&std::path::Path>,
    cfg: &SandboxCfg,
) -> std::io::Result<(i32, Option<SentryError>)> {
    let mut stp = [0 as c_int; 2];
    if unsafe { libc::pipe2(stp.as_mut_ptr(), libc::O_CLOEXEC) } < 0 {
        return Err(std::io::Error::last_os_error());
    }
    let (st_r, st_w) = (stp[0], stp[1]);
    let stdio = Stdio {
        status_wr: Some(st_w),
        ..Stdio::default()
    };
    let pid = match spawn_sandbox(elf, args, root, stdio, false, cfg) {
        Ok(p) => p,
        Err(e) => {
            unsafe {
                libc::close(st_r);
                libc::close(st_w);
            }
            return Err(e);
        }
    };
    // Our copy of the write-end is closed in the parent so the read EOFs once the
    // supervisor exits (the supervisor holds the only other write-end).
    unsafe { libc::close(st_w) };
    let code = wait_exit(pid)?;
    let err = read_status_reason(st_r, code);
    Ok((code, err))
}

/// Read the one-byte error tag the supervisor wrote to the per-exec status pipe
/// (`st_r`) before `_exit`, reconstructing the [`SentryError`]. Called ONLY after
/// the supervisor has been `wait`'d, so the tag (if any) is already in the pipe
/// buffer (write happens-before `_exit` happens-before our `waitpid` return); a
/// NON-BLOCKING read therefore can't miss it AND can't hang on a straggler that
/// still holds a write-end (`EAGAIN`/EOF/short read all ⇒ no structured reason).
/// Closes `st_r`.
fn read_status_reason(st_r: c_int, code: i32) -> Option<SentryError> {
    // The byte is already buffered; never block (a backgrounded guest fork could
    // otherwise hold the write-end open and wedge a blocking read).
    unsafe {
        let fl = libc::fcntl(st_r, libc::F_GETFL);
        if fl >= 0 {
            libc::fcntl(st_r, libc::F_SETFL, fl | libc::O_NONBLOCK);
        }
    }
    let mut tag = [0u8; 1];
    let n = unsafe { libc::read(st_r, tag.as_mut_ptr() as *mut c_void, 1) };
    unsafe { libc::close(st_r) };
    if n == 1 {
        tag_to_err(code, tag[0] as u32)
    } else {
        None
    }
}

/// Warm a guest to its SENTINEL checkpoint, then fork-from-warm restore it many
/// times, measuring the restore latency (printed by the supervisor as a
/// `[zygote] …` line on stderr). `elf` must issue `syscall(0x5359)` when warmed.
/// This is the no-virt fast-spin-up primitive (the spike measured ~0.2–0.5 ms vs
/// the ~4.6 ms KVM reset); proves the mechanism in the production cell.
pub fn zygote_bench(
    elf: &std::path::Path,
    args: &[String],
    root: Option<&std::path::Path>,
) -> std::io::Result<i32> {
    let cfg = SandboxCfg {
        warm: true,
        ..SandboxCfg::default()
    };
    let pid = spawn_sandbox(elf, args, root, Stdio::default(), false, &cfg)?;
    wait_exit(pid)
}

/// Decode `waitpid` status into an exit code (128+signal if signaled). Also
/// reaps the sandbox's cgroup (named by `pid`) — a no-op if none was created.
fn wait_exit(pid: c_int) -> std::io::Result<i32> {
    wait_exit_classified(pid).map(|(c, _)| c)
}

/// As [`wait_exit`], but also returns the structured [`SentryError`] reason when
/// `pid` was signaled (a seccomp-wall kill, OOM/limit `SIGKILL`, …). Used where
/// the WAITED pid is the CELL itself ([`Pool`]'s per-cell wait), so its
/// `WIFSIGNALED`/`WTERMSIG` directly classify the death; the supervisor-waiting
/// paths instead get the reason over the status pipe (the supervisor `_exit`s with
/// `128+sig`, which hides the `WIFSIGNALED`-ness from the library's `waitpid`).
fn wait_exit_classified(pid: c_int) -> std::io::Result<(i32, Option<SentryError>)> {
    let mut st: c_int = 0;
    if unsafe { libc::waitpid(pid, &mut st, 0) } < 0 {
        return Err(std::io::Error::last_os_error());
    }
    remove_cgroup(pid);
    Ok(classify_status(st))
}

const SYS_PIDFD_OPEN: i64 = 434;
/// CLOCK_MONOTONIC milliseconds (for deadline arithmetic across EINTR).
fn now_ms() -> i64 {
    let mut ts = [0i64; 2];
    unsafe {
        host(
            SYS_CLOCK_GETTIME,
            libc::CLOCK_MONOTONIC as u64,
            ts.as_mut_ptr() as u64,
            0,
            0,
            0,
            0,
        )
    };
    ts[0].saturating_mul(1000) + ts[1] / 1_000_000
}

/// As [`wait_exit_classified`], but bounds the wait to `timeout_ms` of wall-time —
/// the Pool/Sandbox bounded-execution watchdog. `0` ⇒ unlimited (byte-identical to
/// `wait_exit_classified`, the default). On timeout, SIGKILL `pid` (the foreground
/// cell; its straggler tree is reclaimed by the caller's `sweep_stragglers`) and
/// return `(137, Some(SentryError::Timeout))`. Uses a pidfd + `poll(2)` so a normal
/// exit returns with zero added latency; degrades to an unbounded wait if
/// `pidfd_open` is unavailable (never wrong, just un-bounded on that host).
fn wait_exit_deadline(pid: c_int, timeout_ms: u64) -> std::io::Result<(i32, Option<SentryError>)> {
    if timeout_ms == 0 {
        return wait_exit_classified(pid);
    }
    let pidfd = unsafe { host(SYS_PIDFD_OPEN, pid as u64, 0, 0, 0, 0, 0) };
    if pidfd < 0 {
        return wait_exit_classified(pid);
    }
    let deadline = now_ms().saturating_add(timeout_ms as i64);
    let timed_out = loop {
        let remaining = deadline - now_ms();
        if remaining <= 0 {
            break true;
        }
        let mut pf = pollfd_bytes(pidfd as i32, POLLIN_BIT);
        let r = unsafe {
            host(
                SYS_POLL,
                pf.as_mut_ptr() as u64,
                1,
                remaining as u64,
                0,
                0,
                0,
            )
        };
        if r > 0 {
            break false; // pidfd readable ⇒ the cell exited ⇒ reap below
        }
        if r == 0 {
            break true; // poll timeout
        }
        // -EINTR (a stray supervisor signal): re-poll the remaining budget.
    };
    unsafe { host(SYS_CLOSE, pidfd as u64, 0, 0, 0, 0, 0) };
    if timed_out {
        unsafe {
            // Tear down the WHOLE cell tree, not just the leader. spawn_sandbox forks
            // a supervisor that forks the sealed cell, so SIGKILL'ing only `pid` (the
            // supervisor) leaves the cell alive (reparented to init) STILL HOLDING the
            // exec stdout/stderr pipe write-ends — so the caller's pump-join (run_one /
            // run_one_tty) would hang forever despite the "timeout", which is exactly
            // the bug that made a wedged production exec hang. The tree is its own
            // process group (spawn_sandbox `new_group`), so killpg(leader) nukes the
            // supervisor + cell + any grandchildren in one shot; the dying cell drops
            // its pipe ends, the pumps EOF, and run_one drains. Guard on group
            // leadership so a (hypothetical) non-grouped caller still does the safe
            // single-pid kill rather than signalling some unrelated group.
            if libc::getpgid(pid) == pid {
                libc::kill(-pid, libc::SIGKILL);
            } else {
                libc::kill(pid, libc::SIGKILL);
            }
            let mut st: c_int = 0;
            libc::waitpid(pid, &mut st, 0); // SIGKILL'd ⇒ reaps the leader promptly
        }
        remove_cgroup(pid);
        return Ok((128 + libc::SIGKILL, Some(SentryError::Timeout)));
    }
    wait_exit_classified(pid)
}

/// Wait for a warm/driven cell to signal its `ready` word (the SENTINEL checkpoint),
/// bounded by `timeout_ms` (`0` = unlimited). Returns `true` iff `ready` fired;
/// `false` if the cell died before signalling or the deadline elapsed (in which case
/// its whole tree is SIGKILL'd + reaped, like `wait_exit_deadline`). The pre-redesign
/// loop was an UNBOUNDED `while ready==0 { futex_wait }` with no liveness check, so a
/// warm cell that wedged or was OOM/throttle-killed before the checkpoint hung the
/// supervisor forever — wedging `Pool::warm`/`SentryPool::build` and poisoning a
/// serial session. This converts that into a diagnosable build/acquire failure. With
/// `timeout_ms == 0` the deadline is disabled but the liveness check still fires, so a
/// crashed pre-checkpoint cell fails fast instead of hanging.
fn wait_ready_deadline(ready: &AtomicU32, pid: c_int, timeout_ms: u64) -> bool {
    let deadline = if timeout_ms == 0 {
        None
    } else {
        Some(now_ms().saturating_add(timeout_ms as i64))
    };
    loop {
        if ready.load(Ordering::Acquire) != 0 {
            return true;
        }
        // Did the cell exit before reaching the checkpoint (crash / OOM / throttle
        // kill)? The warm cell is this supervisor's direct child, so reap it here; a
        // store of `ready` can land right before exit, so re-check the word after.
        let mut st: c_int = 0;
        if unsafe { libc::waitpid(pid, &mut st, libc::WNOHANG) } == pid {
            return ready.load(Ordering::Acquire) != 0;
        }
        let mut slice_ms: i64 = 50;
        if let Some(dl) = deadline {
            let rem = dl - now_ms();
            if rem <= 0 {
                // Deadline: tear down the whole wedged cell tree (own process group ⇒
                // killpg the leader) and report failure to the caller.
                unsafe {
                    if libc::getpgid(pid) == pid {
                        libc::kill(-pid, libc::SIGKILL);
                    } else {
                        libc::kill(pid, libc::SIGKILL);
                    }
                    let mut s: c_int = 0;
                    libc::waitpid(pid, &mut s, 0);
                }
                remove_cgroup(pid);
                return false;
            }
            slice_ms = slice_ms.min(rem);
        }
        // Relative CLOCK_MONOTONIC timeout for FUTEX_WAIT: `[tv_sec, tv_nsec]`.
        let ts: [i64; 2] = [slice_ms / 1000, (slice_ms % 1000) * 1_000_000];
        unsafe {
            host(
                SYS_FUTEX,
                ready.as_ptr() as u64,
                FUTEX_WAIT,
                0,
                ts.as_ptr() as u64,
                0,
                0,
            )
        };
    }
}

/// Fork a sandbox tree (supervisor + sealed cell) and return its pid WITHOUT
/// waiting. `stdout_to`, when set, becomes the guest's stdout+stderr (used by
/// [`Sandbox::exec_capture`]). `new_group` puts the tree in its own process
/// group so [`Child::kill`] can tear down the whole tree with one `killpg`.
fn spawn_sandbox(
    elf: &std::path::Path,
    args: &[String],
    root: Option<&std::path::Path>,
    stdio: Stdio,
    new_group: bool,
    cfg: &SandboxCfg,
) -> std::io::Result<c_int> {
    // Serialize the supervisor config into an anonymous memfd (NOT cloexec — it
    // must survive the child's execve). The fork child relocates it to
    // REEXEC_CONFIG_FD and re-execs THIS binary into a pristine, single-threaded
    // address space, where the `.init_array` ctor reads it and runs sandbox_main.
    // This is the fork-in-MT fix: the non-async-signal-safe supervisor never runs
    // in the fork-inherited image of a multithreaded caller (libtest, napi/Node).
    let rc = ReexecConfig::Sandbox {
        path: elf.to_path_buf(),
        args: args.to_vec(),
        root: root.map(|p| p.to_path_buf()),
        cfg: cfg.clone(),
        has_status: stdio.status_wr.is_some(),
    };
    let json = serde_json::to_vec(&rc)
        .map_err(|e| std::io::Error::new(std::io::ErrorKind::InvalidInput, e))?;
    let cfg_mfd =
        unsafe { libc::memfd_create(b"sentry_reexec_cfg\0".as_ptr() as *const c_char, 0) };
    if cfg_mfd < 0 {
        return Err(std::io::Error::last_os_error());
    }
    {
        // Write the whole blob (loop for short writes), then the child/ctor rewinds.
        let mut off = 0usize;
        while off < json.len() {
            let n = unsafe {
                libc::write(
                    cfg_mfd,
                    json[off..].as_ptr() as *const c_void,
                    json.len() - off,
                )
            };
            if n <= 0 {
                unsafe { libc::close(cfg_mfd) };
                return Err(std::io::Error::last_os_error());
            }
            off += n as usize;
        }
    }
    let reexec_exe = reexec_exe_cstring()?;
    let pid = unsafe { libc::fork() };
    if pid < 0 {
        unsafe { libc::close(cfg_mfd) };
        return Err(std::io::Error::last_os_error());
    }
    if pid == 0 {
        // In the ctty (pty) case, `setsid` below starts a fresh session AND a
        // fresh process group (leader pid == this pid), so the explicit
        // `setpgid` is both redundant and HARMFUL — `setpgid(0,0)` makes us a
        // group leader, after which `setsid` returns EPERM. Skip it then.
        if new_group && !stdio.ctty {
            unsafe { libc::setpgid(0, 0) };
        }
        // Redirect this sandbox process's 0/1/2 BEFORE the supervisor seeds the
        // guest fds from them. dup2 each requested fd onto its target, then close
        // the originals (>2) exactly once (a fd reused across targets, e.g.
        // stdout==stderr in exec_capture, must not be double-closed).
        unsafe {
            let mut to_close = [false; 3]; // index by stdio slot
            let targets = [(stdio.stdin, 0), (stdio.stdout, 1), (stdio.stderr, 2)];
            for (i, (src, tgt)) in targets.iter().enumerate() {
                if let Some(fd) = *src {
                    libc::dup2(fd, *tgt);
                    if fd > 2 {
                        to_close[i] = true;
                    }
                }
            }
            // Close each distinct original >2 once.
            let mut closed: [c_int; 3] = [-1, -1, -1];
            for (i, (src, _)) in targets.iter().enumerate() {
                if let (true, Some(fd)) = (to_close[i], *src) {
                    if !closed.contains(&fd) {
                        libc::close(fd);
                        closed[i] = fd;
                    }
                }
            }
            // tty mode: fd 0 is now the pty slave. Become a session leader and
            // claim it as the controlling terminal so the guest sees a real tty
            // (isatty, raw mode, ^C → SIGINT, SIGWINCH on RESIZE). Must run AFTER
            // the dup2 (so fd 0 is the slave) and only when NOT already a group
            // leader (see the `setpgid` skip above). Best-effort: a failure just
            // means no ctty, not a broken sandbox.
            if stdio.ctty {
                libc::setsid();
                libc::ioctl(0, libc::TIOCSCTTY as _, 0);
            }
            // Relocate the structured-error STATUS pipe (if any) onto the reserved
            // fd 3 BEFORE the sweep, so it survives the fd-shedding below and lands
            // at a number `supervisor_main` can find without inheriting it as a stray
            // fd. It stays open (occupying fd 3) for the supervisor's lifetime, so
            // its fresh fds (rootfs dirfd, ring shm) open at >= 4 — the cell never
            // sees it (it's the trusted tier's reporting channel, not a guest fd).
            // Relocate the STATUS pipe (→ fd 3) and the config memfd (→ fd 4)
            // COLLISION-SAFELY. Both must survive the fd sweep below; the config
            // must also survive the execve into the re-exec'd ctor. The naive
            // "dup2 status→3, then config→4" is UNSOUND under concurrency: a peer
            // thread can free a low fd just before THIS thread's `memfd_create`, so
            // `cfg_mfd` itself lands on fd 3 (the status target). Then `dup2(status→3)`
            // clobbers the config memfd and `dup2(cfg_mfd=3→4)` copies the (still
            // empty) status pipe to fd 4 — the ctor reads an empty fd 4 and dies
            // ("config memfd parse failed", exit 127; observed as a rare empty-output
            // 127 verify failure under the concurrent layer-cache stress). Fix: copy
            // BOTH originals to HIGH scratch fds (≥16, clear of the 0..=4 targets)
            // FIRST, so a later target write can't corrupt either source, THEN place
            // them. The scratch copies are swept below; dup2's destination is always
            // non-cloexec, so fds 3/4 survive the execve as required.
            let cfg_scratch = libc::fcntl(cfg_mfd, libc::F_DUPFD, 16);
            let status_scratch = match stdio.status_wr {
                Some(w) => libc::fcntl(w, libc::F_DUPFD, 16),
                None => -1,
            };
            if cfg_scratch >= 0 {
                libc::dup2(cfg_scratch, REEXEC_CONFIG_FD);
            } else if cfg_mfd != REEXEC_CONFIG_FD {
                libc::dup2(cfg_mfd, REEXEC_CONFIG_FD); // fallback: no free scratch fd
            }
            let _status_fd: c_int = if status_scratch >= 0 {
                libc::dup2(status_scratch, STATUS_PIPE_FD);
                STATUS_PIPE_FD
            } else if let Some(w) = stdio.status_wr {
                if w != STATUS_PIPE_FD {
                    libc::dup2(w, STATUS_PIPE_FD); // fallback: no free scratch fd
                }
                STATUS_PIPE_FD
            } else {
                // No status pipe: drop a stray inherited fd 3 so it can't masquerade
                // as one.
                libc::close(STATUS_PIPE_FD);
                -1
            };
            // Shed EVERY other inherited fd (fd >= 3, but SPARE the status fd). The
            // forked sandbox tree only needs the stdio it just wired onto 0/1/2 (plus
            // the status fd); it opens its own fds fresh post-fork (rootfs dirfd, ring
            // shm) in `setup_sandbox_env`. Without this the tree inherits the caller's
            // fds — listener/connection sockets AND the parent-side pipe ends.
            // Critically, the inherited STDIN WRITE end keeps a guest reading fd 0
            // (e.g. `cat`) from ever seeing EOF, so the cell never exits and the exec
            // hangs. `close_range` (fast); on older kernels fall back to a bounded
            // loop to the fd soft limit. The sweep floor skips the reserved status fd.
            // Spare 0/1/2 + status(3) + config(REEXEC_CONFIG_FD=4); sweep everything ≥5.
            let sweep_lo = REEXEC_CONFIG_FD as u64 + 1;
            if host(SYS_CLOSE_RANGE, sweep_lo, u32::MAX as u64, 0, 0, 0, 0) < 0 {
                let mut lim: libc::rlimit = std::mem::zeroed();
                let max = if libc::getrlimit(libc::RLIMIT_NOFILE, &mut lim) == 0 {
                    (lim.rlim_cur as c_int).clamp(64, 4096)
                } else {
                    1024
                };
                for fd in sweep_lo as c_int..max {
                    libc::close(fd);
                }
            }
        }
        // Re-exec THIS binary into a PRISTINE single-threaded address space; the
        // `.init_array` ctor (`sentry_reexec_ctor`) reads REEXEC_CONFIG_FD and runs
        // `sandbox_main` there. argv=[exe, MARKER]; inherit the live envp. No
        // allocation here (async-signal-safe): all pointers are 'static / stack /
        // the global `environ`. execve only returns on failure.
        unsafe {
            let argv: [*const c_char; 3] = [
                reexec_exe.as_ptr(),
                REEXEC_MARKER.as_ptr() as *const c_char,
                std::ptr::null(),
            ];
            libc::execve(reexec_exe.as_ptr(), argv.as_ptr(), current_environ());
            libc::_exit(127); // execve failed (e.g. /proc not mounted) — fail closed
        }
    }
    unsafe { libc::close(cfg_mfd) }; // parent drops its copy of the config memfd
    Ok(pid)
}

/// Reserved fd the structured-error status pipe is relocated onto in the sandbox
/// tree (see [`spawn_sandbox`]) so it survives the fd-shedding sweep; the cell
/// never opens it (it stays the trusted supervisor's reporting channel). 3 is the
/// first free fd after stdio.
const STATUS_PIPE_FD: c_int = 3;
/// The live status-pipe fd in THIS sandbox tree (`-1` = none), set by
/// [`spawn_sandbox`] before `sandbox_main`; read by [`supervisor_main`]/[`report_status`]
/// to write the one-byte error tag. CoW-inherited by the supervisor/cell fork; only
/// the supervisor ever writes it.
static STATUS_PIPE_FD_ACTIVE: std::sync::atomic::AtomicI32 = std::sync::atomic::AtomicI32::new(-1);

// ─── Clean-supervisor re-exec (fork-in-multithreaded-process fix) ─────────────
// The supervisor (`sandbox_main`: mmap/alloc/file-IO/threads — all NON-async-
// signal-safe) MUST run in a PRISTINE address space. `spawn_sandbox` forks from a
// caller that is ALWAYS multithreaded (a libtest harness; a napi/Node host), and
// POSIX forbids non-async-signal-safe code in such a fork child — it corrupts once
// the parent heap/locks are perturbed (the "stack smashing"/parallel flakiness).
// So the fork child does ONLY async-signal-safe setup, then re-execs THIS binary
// into a clean single-threaded image where the `.init_array` ctor below runs the
// supervisor. (Mirrors how the KVM backend execs a fresh worker; the supervisor
// stays a process distinct from both host and cell.)
//
// The config travels in an inherited (non-cloexec) memfd relocated to this fd.
const REEXEC_CONFIG_FD: c_int = 4; // STATUS_PIPE_FD is 3
const REEXEC_CTRL_FD: c_int = 5; // persistent-supervisor control socket (Pool path)
                                 // NUL-terminated so it's both the execve argv[1] (as_ptr) and the ctor's match key
                                 // (compared via CStr::to_bytes_with_nul) — ONE source of truth.
const REEXEC_MARKER: &[u8] = b"--__sentry_reexec_supervisor__\0";

/// What the re-exec'd (pristine, single-threaded) process should become. Both
/// fork-and-run-supervisor entry points (`spawn_sandbox` → `sandbox_main` and
/// `Pool::start` → `persistent_supervisor_main`) serialize this into the config
/// memfd; the `.init_array` ctor reads it and dispatches. fds (status @3, config
/// @4, ctrl @5) are relocated by the fork child and survive the execve.
#[derive(serde::Serialize, serde::Deserialize)]
enum ReexecConfig {
    /// One-shot sandbox (`sandbox_main`): run `path args` under `root`.
    Sandbox {
        path: std::path::PathBuf,
        args: Vec<String>,
        root: Option<std::path::PathBuf>,
        cfg: SandboxCfg,
        /// A status pipe was relocated onto STATUS_PIPE_FD (the static is reset by
        /// execve, so the re-exec'd supervisor re-learns it from here).
        has_status: bool,
    },
    /// Persistent pool supervisor (`persistent_supervisor_main`): serve exec
    /// requests on the control socket relocated to [`REEXEC_CTRL_FD`]. When
    /// `exec_sock` is set, ALSO bind a streaming exec socket there (the unified
    /// workload+exec backend) whose cells fork in-process via the launcher.
    Persistent {
        root: Option<std::path::PathBuf>,
        cfg: SandboxCfg,
        #[serde(default)]
        exec_sock: Option<std::path::PathBuf>,
    },
}

/// The current process environment pointer for execve (no allocation — passes the
/// live `environ`, so SENTRY_* flags and everything else carry across the re-exec).
unsafe fn current_environ() -> *const *const c_char {
    extern "C" {
        static mut environ: *mut *mut c_char;
    }
    environ as *const *const c_char
}

/// `.init_array` constructor — runs in EVERY process linking this lib, BEFORE
/// `main` (so it intercepts even the libtest binary whose `main` we don't own).
/// Normal start: argv[1] != MARKER → return instantly. Re-exec'd supervisor:
/// read the JSON config from the inherited [`REEXEC_CONFIG_FD`] memfd and run
/// `sandbox_main` (never returns) in this pristine, single-threaded address space.
extern "C" fn sentry_reexec_ctor(argc: c_int, argv: *const *const c_char) {
    unsafe {
        if argc < 2 || argv.is_null() {
            return;
        }
        let a1 = *argv.add(1);
        if a1.is_null() || std::ffi::CStr::from_ptr(a1).to_bytes_with_nul() != REEXEC_MARKER {
            return; // not a re-exec — ordinary program startup
        }
        // We ARE the re-exec'd supervisor. Rewind + slurp the config memfd, parse,
        // dispatch. Raw read into a growable buffer (libc malloc is up post-init).
        libc::lseek(REEXEC_CONFIG_FD, 0, libc::SEEK_SET);
        let mut data = Vec::with_capacity(8192);
        let mut tmp = [0u8; 8192];
        loop {
            let n = libc::read(REEXEC_CONFIG_FD, tmp.as_mut_ptr() as *mut c_void, tmp.len());
            if n <= 0 {
                break;
            }
            data.extend_from_slice(&tmp[..n as usize]);
        }
        libc::close(REEXEC_CONFIG_FD);
        let rc: ReexecConfig = match serde_json::from_slice(&data) {
            Ok(r) => r,
            Err(_) => libc::_exit(127),
        };
        match rc {
            ReexecConfig::Sandbox {
                path,
                args,
                root,
                cfg,
                has_status,
            } => {
                if has_status {
                    STATUS_PIPE_FD_ACTIVE.store(STATUS_PIPE_FD, Ordering::Relaxed);
                }
                sandbox_main(&path, &args, root.as_deref(), &cfg);
            }
            ReexecConfig::Persistent {
                root,
                cfg,
                exec_sock,
            } => {
                persistent_supervisor_main(REEXEC_CTRL_FD, root.as_deref(), &cfg, exec_sock);
            }
        }
    }
}

// glibc invokes .init_array entries with (argc, argv, envp); we read argc/argv.
#[used]
#[cfg_attr(
    all(target_os = "linux", target_arch = "x86_64"),
    link_section = ".init_array"
)]
static SENTRY_REEXEC_CTOR: extern "C" fn(c_int, *const *const c_char) = sentry_reexec_ctor;

/// Serialize a [`ReexecConfig`] into a fresh anonymous memfd (NOT cloexec — it must
/// survive the child's execve). Returns the fd; the fork child relocates it to
/// [`REEXEC_CONFIG_FD`] and the re-exec'd ctor reads it. Caller closes its copy.
fn write_reexec_config(rc: &ReexecConfig) -> std::io::Result<c_int> {
    let json = serde_json::to_vec(rc)
        .map_err(|e| std::io::Error::new(std::io::ErrorKind::InvalidInput, e))?;
    let mfd = unsafe { libc::memfd_create(b"sentry_reexec_cfg\0".as_ptr() as *const c_char, 0) };
    if mfd < 0 {
        return Err(std::io::Error::last_os_error());
    }
    let mut off = 0usize;
    while off < json.len() {
        let n =
            unsafe { libc::write(mfd, json[off..].as_ptr() as *const c_void, json.len() - off) };
        if n <= 0 {
            unsafe { libc::close(mfd) };
            return Err(std::io::Error::last_os_error());
        }
        off += n as usize;
    }
    Ok(mfd)
}

/// Executable used for sentry clean-supervisor reexec.
///
/// Rust binaries/tests link the sentry directly, so `/proc/self/exe` contains the
/// `.init_array` reexec constructor. Embedders such as `@supermachine/core` are
/// loaded into another host executable (`node`), so `/proc/self/exe` would reexec
/// Node, which does not know `REEXEC_MARKER`. They set
/// `SUPERMACHINE_SENTRY_REEXEC_BIN` to a bundled helper binary that links this
/// crate and therefore has the constructor.
fn reexec_exe_cstring() -> std::io::Result<std::ffi::CString> {
    let exe = std::env::var("SUPERMACHINE_SENTRY_REEXEC_BIN")
        .unwrap_or_else(|_| "/proc/self/exe".to_string());
    std::ffi::CString::new(exe).map_err(|_| {
        std::io::Error::new(std::io::ErrorKind::InvalidInput, "reexec path contains NUL")
    })
}
/// Write the one-byte [`SentryError`] tag for `err` to this tree's status pipe (if
/// any), so the library can recover the reason a `_exit(128+sig)` flattens away.
/// Signal-safe (a single `write`); a no-op when there's no channel or no reason.
fn report_status(err: Option<SentryError>) {
    let fd = STATUS_PIPE_FD_ACTIVE.load(Ordering::Relaxed);
    if fd < 0 {
        return;
    }
    let tag = err_to_tag(err);
    if tag == ERR_NONE {
        return;
    }
    let b = [tag as u8];
    unsafe { libc::write(fd, b.as_ptr() as *const c_void, 1) };
}

/// A backgrounded sandbox tree (its own process group). Returned by
/// [`spawn`]; held by [`Sandbox`] for the long-running workload. `wait` returns
/// the workload's exit code; `kill` SIGKILLs the whole tree.
pub struct Child {
    pid: c_int,
}
impl Child {
    /// The sandbox's process-group-leader pid.
    pub fn id(&self) -> i32 {
        self.pid
    }
    /// Block until the sandbox exits; returns its exit code (128+sig if killed).
    pub fn wait(&self) -> std::io::Result<i32> {
        wait_exit(self.pid)
    }
    /// Non-blocking: `Some(code)` if the sandbox has exited, else `None`.
    pub fn try_wait(&self) -> std::io::Result<Option<i32>> {
        let mut st: c_int = 0;
        let r = unsafe { libc::waitpid(self.pid, &mut st, libc::WNOHANG) };
        if r < 0 {
            Err(std::io::Error::last_os_error())
        } else if r == 0 {
            Ok(None)
        } else if libc::WIFEXITED(st) {
            Ok(Some(libc::WEXITSTATUS(st)))
        } else if libc::WIFSIGNALED(st) {
            Ok(Some(128 + libc::WTERMSIG(st)))
        } else {
            Ok(None)
        }
    }
    /// SIGKILL the whole sandbox process group (supervisor + cell + forks).
    pub fn kill(&self) {
        unsafe { libc::kill(-self.pid, libc::SIGKILL) };
    }
}

/// Run a guest ELF under the sentry in the BACKGROUND, confined to `root`, and
/// return a [`Child`] handle (the caller's process is untouched). The sandbox
/// runs in its own process group; use [`Child::wait`]/[`Child::kill`].
pub fn spawn(
    elf: &std::path::Path,
    args: &[String],
    root: Option<&std::path::Path>,
) -> std::io::Result<Child> {
    Ok(Child {
        pid: spawn_sandbox(
            elf,
            args,
            root,
            Stdio::default(),
            true,
            &SandboxCfg::default(),
        )?,
    })
}

/// A handle to a sentry sandbox bound to an extracted rootfs directory: a
/// long-running workload can run in the background while you [`exec`](Sandbox::exec)
/// additional confined commands against the SAME rootfs.
///
/// Each `exec` is its own sealed, confined sandbox sharing the rootfs directory,
/// so it sees the workload's filesystem writes — the no-virt analogue of
/// `docker exec` for the filesystem. It does NOT (yet) share the workload's PID
/// or network namespace (so it can't see the workload's processes or reach its
/// `localhost` ports); that is the M5 namespacing work. The rootfs is served by
/// `openat2(RESOLVE_IN_ROOT)`, so every exec is confined to it.
pub struct Sandbox {
    root: std::path::PathBuf,
    egress: Option<String>,
    mounts: Vec<(std::path::PathBuf, std::path::PathBuf, bool)>,
    limits: Limits,
    run_uid: Option<(u32, u32)>,
    netns: bool,
    published_ports: Vec<u16>,
    /// The image environment (`(KEY, VALUE)` pairs) used as the BASE env for every
    /// `exec`/`serve_exec` request: the exec server overlays the per-request env on
    /// top (request keys win), so a command sees the image's `PATH`/`LANG`/etc. The
    /// `Sandbox::exec`/`exec_capture` direct path uses it as the cell env wholesale.
    env: Vec<(String, String)>,
    cgroup_name: String,
    /// Bounded-execution watchdog (ms; `0` = unlimited). See [`SandboxCfg::exec_timeout_ms`].
    exec_timeout_ms: u64,
    preserve_stragglers: bool,
    workload: std::sync::Mutex<Option<Child>>,
}
impl Sandbox {
    /// Bind a sandbox to an extracted rootfs directory.
    ///
    /// Network-ISOLATED by default (`netns: true`): the guest gets its own
    /// loopback + port space, so it can't reach the host's or another tenant's
    /// `127.0.0.1` (the cross-tenant `localhost` leak). Egress to non-loopback
    /// destinations is still proxied through the host netns. Call
    /// [`without_netns`](Sandbox::without_netns) to share the host netns (a
    /// workload that must `bind` a host-reachable listener directly).
    pub fn new(root: impl Into<std::path::PathBuf>) -> Sandbox {
        // A stable, process-unique cgroup name so all of THIS sandbox's execs
        // share one cgroup (and one resource cap), distinct from other sandboxes.
        static N: AtomicU32 = AtomicU32::new(0);
        let cgroup_name = format!(
            "sentry-sb-{}-{}",
            unsafe { libc::getpid() },
            N.fetch_add(1, Ordering::Relaxed)
        );
        Sandbox {
            root: root.into(),
            egress: None,
            mounts: Vec::new(),
            limits: Limits::default(),
            run_uid: None,
            // Isolation-safe default: a fresh sandbox is network-isolated. Opt out
            // with `without_netns()` for a host-reachable workload (`expose_tcp`).
            netns: true,
            published_ports: Vec::new(),
            env: Vec::new(),
            cgroup_name,
            exec_timeout_ms: 0,
            preserve_stragglers: false,
            workload: std::sync::Mutex::new(None),
        }
    }

    /// Repoint this sandbox's confined ROOT to `root`, leaving EVERY other field
    /// (egress / uid / mounts / cgroup limits / env / netns / published ports)
    /// untouched. Used by the builder path to serve a Dockerfile stage from a
    /// PER-BUILD private reflink clone of the warm rootfs while keeping an
    /// otherwise-identical, equally-confined sandbox: the new root is still a
    /// single real directory opened as the `O_PATH` `ROOT_FD` and every guest path
    /// still resolves via `openat2(RESOLVE_IN_ROOT)` — same confinement, no new
    /// path-resolution route, no namespace, no mount. Prefer this explicit field
    /// override to post-hoc mutating the sandbox so confinement/policy can't be
    /// silently lost.
    pub fn with_root(mut self, root: impl Into<std::path::PathBuf>) -> Sandbox {
        self.root = root.into();
        self
    }

    /// Clone the sandbox policy into a fresh handle with a new cgroup name and no
    /// live workload child. Used when one image snapshot must produce independent
    /// restored supervisors, matching native VM pool semantics.
    pub fn clone_template(&self) -> Sandbox {
        let mut next = Sandbox::new(self.root.clone());
        next.egress = self.egress.clone();
        next.mounts = self.mounts.clone();
        next.limits = self.limits.clone();
        next.run_uid = self.run_uid;
        next.netns = self.netns;
        next.published_ports = self.published_ports.clone();
        next.env = self.env.clone();
        next.exec_timeout_ms = self.exec_timeout_ms;
        next.preserve_stragglers = self.preserve_stragglers;
        next
    }

    /// Run the guest as an unprivileged host `uid`/`gid` (defense in depth: a
    /// hostile guest is host-unprivileged even though it's sealed). The cell
    /// drops to it before sealing; the supervisor stays privileged (it does the
    /// confined fs/io as the sentry's own uid), so the guest loses no capability.
    /// The guest's `getuid`/`getgid` then report `uid`/`gid`. Use distinct ids
    /// per tenant for inter-tenant uid isolation.
    pub fn with_uid(mut self, uid: u32, gid: u32) -> Sandbox {
        self.run_uid = Some((uid, gid));
        self
    }

    /// Set the base guest environment (`(KEY, VALUE)` pairs) for this sandbox —
    /// typically the baked image's `Env`. It seeds every cell: the direct
    /// [`exec`](Sandbox::exec)/[`exec_capture`](Sandbox::exec_capture) path runs
    /// with exactly this env, and the [`serve_exec`](Sandbox::serve_exec) path uses
    /// it as the base under each per-request env (the request's keys win). Empty
    /// (the default) keeps the built-in `PATH`/`PWD`/`LANG` fallback.
    pub fn with_env(mut self, env: Vec<(String, String)>) -> Sandbox {
        self.env = env;
        self
    }

    /// Isolate the sandbox in its own network namespace: it gets its own port
    /// space (binds never collide with another tenant's) and its own loopback
    /// (its `127.0.0.1` is private — it can't reach the host's or another
    /// tenant's loopback services). Requires root.
    ///
    /// This is now the DEFAULT for a fresh [`Sandbox`] (see [`Sandbox::new`]), so
    /// this call is normally redundant — kept as an explicit, idempotent enabler
    /// (and to re-enable isolation after a [`without_netns`](Sandbox::without_netns)).
    /// Egress to non-loopback destinations and published ports are re-homed into
    /// the host netns ([`rehome_to_host_netns`]); only loopback stays isolated.
    pub fn with_netns(mut self) -> Sandbox {
        self.netns = true;
        self
    }

    /// Share the HOST network namespace instead of isolating (the explicit opt-out
    /// of the isolation-safe default). The guest's `127.0.0.1` is then the HOST's
    /// loopback and its binds land directly in the host netns — so a workload
    /// listener is host-reachable (e.g. via `expose_tcp`) WITHOUT publishing a
    /// port. Use only for trusted, host-reachable workloads: a shared netns means
    /// the guest can reach the host's (and other shared-netns tenants') loopback
    /// services, so it forgoes the cross-tenant `localhost` isolation.
    pub fn without_netns(mut self) -> Sandbox {
        self.netns = false;
        self
    }

    /// Publish a port from a netns sandbox to the host: when the guest `bind`s
    /// `port`, the listening socket is re-homed into the host netns so the host
    /// (and, if the guest binds `0.0.0.0`, external clients) can reach it on the
    /// same port — the ingress counterpart to the egress proxy. Only meaningful in
    /// a netns sandbox (the default); a [`without_netns`](Sandbox::without_netns)
    /// sandbox already binds in the host netns. Re-asserts netns isolation (so it
    /// composes safely after `without_netns`). Other binds stay isolated. The host
    /// port is the same as the guest's; collisions on the host are the operator's
    /// responsibility (like `docker -p`).
    pub fn with_published_port(mut self, port: u16) -> Sandbox {
        self.netns = true;
        self.published_ports.push(port);
        self
    }

    /// Cap the guest's total memory (cgroup-v2 `memory.max`, bytes). When the
    /// guest tree exceeds it, the kernel OOM-kills a process in the cgroup — the
    /// host and other tenants are protected. Best-effort (needs cgroup v2).
    pub fn with_memory_limit(mut self, bytes: u64) -> Sandbox {
        self.limits.memory_max = Some(bytes);
        self
    }
    /// Bounded-execution watchdog: SIGKILL an `exec`/`exec_capture` cell tree (and
    /// return [`SentryError::Timeout`]) if it runs longer than `dur`. `0`/unset =
    /// unlimited (default). A production single-tenant ceiling so a wedged workload
    /// (stuck guest, or the deferred C1 reentrancy hang) can never hang the Pool —
    /// the exec is bounded + reported and the Pool serves the next request.
    pub fn with_exec_timeout(mut self, dur: std::time::Duration) -> Sandbox {
        self.exec_timeout_ms = dur.as_millis().min(u64::MAX as u128) as u64;
        self
    }
    /// Cap the guest's total processes+threads (cgroup-v2 `pids.max`) — fork-bomb
    /// protection. Note the guest's own tasks only (the supervisor isn't counted).
    pub fn with_pids_limit(mut self, n: u64) -> Sandbox {
        self.limits.pids_max = Some(n);
        self
    }
    /// Cap the guest's CPU (cgroup-v2 `cpu.max`): `quota_us` microseconds of CPU
    /// per `period_us` microseconds (e.g. `50_000, 100_000` = 50% of one core).
    pub fn with_cpu_limit(mut self, quota_us: u64, period_us: u64) -> Sandbox {
        self.limits.cpu_max = Some(format!("{quota_us} {period_us}"));
        self
    }

    /// Set the outbound-TCP egress policy enforced on this sandbox's workload
    /// AND every `exec` (`allow_all` / `deny_private` / `allowlist:<cidrs>` /
    /// `denylist:<cidrs>` — see [`crate::vmm::egress_policy`]). The supervisor
    /// is the only thing that opens a real host socket, so this is the single
    /// egress control point: a denied connect never reaches the network.
    pub fn with_egress(mut self, policy: impl Into<String>) -> Sandbox {
        self.egress = Some(policy.into());
        self
    }

    /// Bind-mount a host directory at `guest_path` (a writable volume, or
    /// `readonly`). The guest sees `host_path`'s contents at `guest_path`, served
    /// by the same `openat2(RESOLVE_IN_ROOT)` confinement (it's its own confined
    /// root — `..` can't escape it). Applies to the workload and every `exec`.
    /// `guest_path` must be absolute; a trailing slash is trimmed.
    pub fn with_mount(
        mut self,
        guest_path: impl Into<std::path::PathBuf>,
        host_path: impl Into<std::path::PathBuf>,
        readonly: bool,
    ) -> Sandbox {
        self.mounts
            .push((guest_path.into(), host_path.into(), readonly));
        self
    }

    /// Preserve background children after foreground execs so bake warmup can
    /// snapshot live daemons. This is intentionally not the default: normal execs
    /// sweep stragglers to keep output capture from hanging on inherited fds.
    pub(crate) fn with_preserve_stragglers(mut self, on: bool) -> Sandbox {
        self.preserve_stragglers = on;
        self
    }

    /// The config passed down to each spawned sandbox tree.
    fn cfg(&self) -> SandboxCfg {
        SandboxCfg {
            egress: self.egress.clone(),
            mounts: self.mounts.clone(),
            limits: self.limits.clone(),
            run_uid: self.run_uid,
            netns: self.netns,
            published_ports: self.published_ports.clone(),
            // Only materialize the shared cgroup when there are limits to apply.
            cgroup_name: self.limits.any().then(|| self.cgroup_name.clone()),
            warm: false,
            env: self.env.clone(),
            cwd: None,
            exec_timeout_ms: self.exec_timeout_ms,
            preserve_stragglers: self.preserve_stragglers,
        }
    }

    /// The rootfs directory this sandbox is confined to.
    pub fn root(&self) -> &std::path::Path {
        &self.root
    }

    /// Start a persistent-supervisor [`Pool`] carrying THIS sandbox's config
    /// (rootfs + egress / mounts / cgroup limits / uid-drop). The supervisor sets
    /// the config up once and applies it to every cell it forks — pooled
    /// [`exec`](Pool::exec)s and fork-from-warm [`acquire`](Pool::acquire)
    /// instances alike. When limits are set, all of the pool's cells share ONE
    /// cgroup (so the cap bounds the tenant as a whole, not per-exec).
    pub fn pool(&self) -> std::io::Result<Pool> {
        Pool::start(Some(&self.root), &self.cfg(), None)
    }

    /// Like [`pool`](Self::pool) but the supervisor ALSO binds a streaming exec
    /// socket at `exec_sock` (the unified workload+exec backend) — a `Vm` dials it
    /// with the full `ExecBuilder` protocol so its cells fork in-process and share
    /// the supervisor's loopback + proctree with the backgrounded workload.
    pub fn pool_with_exec(&self, exec_sock: std::path::PathBuf) -> std::io::Result<Pool> {
        Pool::start(Some(&self.root), &self.cfg(), Some(exec_sock))
    }

    /// Run a command to completion, confined to the rootfs, returning its exit
    /// code. `argv[0]` is the guest-visible executable path (e.g. `/bin/sh`);
    /// the sentry has no `$PATH` search of its own, so for a shell command use
    /// `["/bin/sh", "-c", "…"]`. Stdio is inherited from the caller.
    pub fn exec<I, S>(&self, argv: I) -> std::io::Result<i32>
    where
        I: IntoIterator<Item = S>,
        S: Into<String>,
    {
        self.exec_classified(argv).map(|(c, _)| c)
    }

    /// As [`exec`](Sandbox::exec), but also returns the structured [`SentryError`]
    /// reason (`Some(SeccompViolation { .. })` for a forbidden-syscall kill, etc.)
    /// so a sandbox-policy kill is distinguishable from the workload's own non-zero
    /// exit — both of which flatten to the same `128+sig` integer code.
    pub fn exec_classified<I, S>(&self, argv: I) -> std::io::Result<(i32, Option<SentryError>)>
    where
        I: IntoIterator<Item = S>,
        S: Into<String>,
    {
        let v: Vec<String> = argv.into_iter().map(Into::into).collect();
        if v.is_empty() {
            return Err(std::io::Error::from(std::io::ErrorKind::InvalidInput));
        }
        run_in_classified(
            std::path::Path::new(&v[0]),
            &v[1..],
            Some(&self.root),
            &self.cfg(),
        )
    }

    /// Like [`exec`](Sandbox::exec) but captures the command's stdout+stderr.
    /// Returns `(exit_code, output)`.
    pub fn exec_capture<I, S>(&self, argv: I) -> std::io::Result<(i32, Vec<u8>)>
    where
        I: IntoIterator<Item = S>,
        S: Into<String>,
    {
        self.exec_capture_classified(argv).map(|(c, o, _)| (c, o))
    }

    /// As [`exec_capture`](Sandbox::exec_capture), but also returns the structured
    /// [`SentryError`] reason (`(exit_code, output, reason)`) so a seccomp-wall kill
    /// is distinguishable from a workload's own non-zero exit.
    pub fn exec_capture_classified<I, S>(
        &self,
        argv: I,
    ) -> std::io::Result<(i32, Vec<u8>, Option<SentryError>)>
    where
        I: IntoIterator<Item = S>,
        S: Into<String>,
    {
        let v: Vec<String> = argv.into_iter().map(Into::into).collect();
        if v.is_empty() {
            return Err(std::io::Error::from(std::io::ErrorKind::InvalidInput));
        }
        let mut fds = [0 as c_int; 2];
        if unsafe { libc::pipe(fds.as_mut_ptr()) } < 0 {
            return Err(std::io::Error::last_os_error());
        }
        let (rd, wr) = (fds[0], fds[1]);
        // A second, O_CLOEXEC pipe carries the structured-error tag out of the
        // supervisor (which `_exit`s 128+sig, flattening the seccomp distinction).
        let mut stp = [0 as c_int; 2];
        if unsafe { libc::pipe2(stp.as_mut_ptr(), libc::O_CLOEXEC) } < 0 {
            let e = std::io::Error::last_os_error();
            unsafe {
                libc::close(rd);
                libc::close(wr);
            }
            return Err(e);
        }
        let (st_r, st_w) = (stp[0], stp[1]);
        let elf = std::path::PathBuf::from(&v[0]);
        let cap = Stdio {
            stdout: Some(wr),
            stderr: Some(wr),
            status_wr: Some(st_w),
            ..Stdio::default()
        };
        let pid = match spawn_sandbox(&elf, &v[1..], Some(&self.root), cap, true, &self.cfg()) {
            Ok(p) => p,
            Err(e) => {
                unsafe {
                    libc::close(rd);
                    libc::close(wr);
                    libc::close(st_r);
                    libc::close(st_w);
                }
                return Err(e);
            }
        };
        // Close our write ends so the reads see EOF when the sandbox exits.
        unsafe {
            libc::close(wr);
            libc::close(st_w);
        }
        let mut out = Vec::new();
        let mut buf = [0u8; 16 * 1024];
        loop {
            let n = unsafe { libc::read(rd, buf.as_mut_ptr() as *mut c_void, buf.len()) };
            if n <= 0 {
                break;
            }
            out.extend_from_slice(&buf[..n as usize]);
        }
        unsafe { libc::close(rd) };
        let code = wait_exit(pid)?;
        let err = read_status_reason(st_r, code);
        Ok((code, out, err))
    }

    /// Start the image's baked workload (`/.supermachine/run-workload`) in the
    /// background, replacing any previous one. Errors if no workload is staged.
    pub fn start_workload(&self) -> std::io::Result<()> {
        let script = self.root.join(".supermachine/run-workload");
        if !script.is_file() {
            return Err(std::io::Error::new(
                std::io::ErrorKind::NotFound,
                "no /.supermachine/run-workload staged in the rootfs",
            ));
        }
        let child = Child {
            pid: spawn_sandbox(
                std::path::Path::new("/bin/sh"),
                &["/.supermachine/run-workload".to_string()],
                Some(&self.root),
                Stdio::default(),
                true,
                &self.cfg(),
            )?,
        };
        let mut g = self.workload.lock().unwrap();
        if let Some(old) = g.take() {
            old.kill();
            let _ = old.wait();
        }
        *g = Some(child);
        Ok(())
    }

    /// Whether the background workload is still running.
    pub fn workload_running(&self) -> bool {
        let g = self.workload.lock().unwrap();
        match g.as_ref() {
            Some(c) => matches!(c.try_wait(), Ok(None)),
            None => false,
        }
    }

    /// Stop the background workload (SIGKILL its process group) and reap it, then
    /// reap this sandbox's shared cgroup (no-op if it was never created / if other
    /// execs are still using it — those reap it when they finish + Drop runs).
    pub fn stop(&self) -> std::io::Result<()> {
        if let Some(c) = self.workload.lock().unwrap().take() {
            c.kill();
            let _ = c.wait();
        }
        remove_cgroup_named(&self.cgroup_name);
        Ok(())
    }
}
impl Drop for Sandbox {
    fn drop(&mut self) {
        let _ = self.stop();
    }
}

// ─── exec server: speak the guest-agent wire protocol over a unix socket ──────
//
// The product drives execs/file-ops through `crate::Vm`, which talks the framed
// binary protocol of the in-guest exec agent (`crate::exec` <-> the guest agent)
// over a unix socket. The sentry has no guest kernel and no agent, so this server
// IMPERSONATES the agent on a unix socket: every REQUEST forks a sealed sentry
// cell against the image rootfs and pumps stdio over the socket; every CONTROL
// serves probe / file-ops. That lets `Vm::start` dispatch the entire exec/file
// surface to the sentry with NO protocol change on the library side.
//
// First slice: pipe mode (no pty/TTY), single command + sequential `chain`,
// stdin/stdout/stderr streaming, SIGNAL forwarding, EXIT with exit code + peak
// RSS. CONTROL: probe, write_file, read_file, signal (to the workload), and a
// no-op ok=true for the snapshot-lifecycle actions (sync_time/sync/drop_vfs_caches
// /smpark_*) that don't apply without a guest kernel.

// Wire constants — MUST match `crate::exec` and the guest agent.
const EW_REQUEST: u8 = 0xff;
const EW_CONTROL: u8 = 0xfe;
const EW_STDIN: u8 = 0;
const EW_STDOUT: u8 = 1;
const EW_STDERR: u8 = 2;
/// tty mode only: u16 BE cols, u16 BE rows → `TIOCSWINSZ` on the pty master.
const EW_RESIZE: u8 = 3;
const EW_SIGNAL: u8 = 4;
const EW_EXIT: u8 = 5;
const EW_ERROR: u8 = 6;
/// Host ingress bridge: body is a u16 BE guest port (0 = first listener). After
/// a one-byte success ack the connection becomes an unframed byte stream.
const EW_BRIDGE: u8 = 0xfd;
/// Wire-protocol version the server advertises on `probe`. Keep == the guest
/// agent's `AGENT_PROTOCOL` and the lib's `HOST_AGENT_PROTOCOL_MIN`.
const EW_PROTOCOL: u32 = 3;
const EW_MAX_FRAME: u32 = 16 * 1024 * 1024;

/// A running sentry exec server. Holds the accept thread + a background workload
/// (the image's baked CMD, started at [`ExecServer::start`] if present). Drop or
/// [`stop`](ExecServer::stop) tears everything down and unlinks the socket.
pub struct ExecServer {
    path: std::path::PathBuf,
    stop: std::sync::Arc<AtomicBool>,
    join: Mutex<Option<std::thread::JoinHandle<()>>>,
    workload: std::sync::Arc<Mutex<Option<Child>>>,
}

impl ExecServer {
    /// Bind a unix socket at `sock_path` and serve the exec protocol against
    /// `root`, applying `cfg` (egress / mounts / limits / uid) to every exec.
    /// If `start_workload` is set, the image's `/.supermachine/run-workload` is
    /// launched in the background first (the analogue of a VM booting its CMD).
    fn start(
        root: std::path::PathBuf,
        cfg: SandboxCfg,
        sock_path: std::path::PathBuf,
        start_workload: bool,
    ) -> std::io::Result<ExecServer> {
        use std::os::unix::net::UnixListener;
        let _ = std::fs::remove_file(&sock_path);
        let listener = UnixListener::bind(&sock_path)?;
        listener.set_nonblocking(true)?;

        let workload: std::sync::Arc<Mutex<Option<Child>>> = std::sync::Arc::new(Mutex::new(None));
        if start_workload {
            let script = root.join(".supermachine/run-workload");
            if script.is_file() {
                let pid = spawn_sandbox(
                    std::path::Path::new("/bin/sh"),
                    &["/.supermachine/run-workload".to_string()],
                    Some(&root),
                    Stdio::default(),
                    true,
                    &cfg,
                )?;
                *workload.lock().unwrap() = Some(Child { pid });
            }
        }

        let stop = std::sync::Arc::new(AtomicBool::new(false));
        let join = {
            let (stop, root, cfg, workload) =
                (stop.clone(), root.clone(), cfg.clone(), workload.clone());
            std::thread::Builder::new()
                .name("sentry-exec-accept".into())
                .spawn(move || accept_loop(listener, stop, root, cfg, workload))?
        };
        Ok(ExecServer {
            path: sock_path,
            stop,
            join: Mutex::new(Some(join)),
            workload,
        })
    }

    /// The unix socket path the server listens on (the `Vm`'s exec socket).
    pub fn path(&self) -> &std::path::Path {
        &self.path
    }

    /// Stop accepting, kill the workload, and unlink the socket. Idempotent.
    pub fn stop(&self) {
        self.stop.store(true, Ordering::Relaxed);
        let join = self.join.lock().unwrap_or_else(|e| e.into_inner()).take();
        if let Some(j) = join {
            let _ = j.join();
        }
        if let Some(c) = self.workload.lock().unwrap().take() {
            c.kill();
            let _ = c.wait();
        }
        let _ = std::fs::remove_file(&self.path);
    }
}
impl Drop for ExecServer {
    fn drop(&mut self) {
        self.stop();
    }
}

/// Accept connections until `stop`; one handler thread per connection.
fn accept_loop(
    listener: std::os::unix::net::UnixListener,
    stop: std::sync::Arc<AtomicBool>,
    root: std::path::PathBuf,
    cfg: SandboxCfg,
    workload: std::sync::Arc<Mutex<Option<Child>>>,
) {
    while !stop.load(Ordering::Relaxed) {
        match listener.accept() {
            Ok((stream, _)) => {
                let (root, cfg, workload) = (root.clone(), cfg.clone(), workload.clone());
                let _ = std::thread::Builder::new()
                    .name("sentry-exec-conn".into())
                    .spawn(move || {
                        let _ = handle_conn(stream, &root, &cfg, &workload, None);
                    });
            }
            Err(e) if e.kind() == std::io::ErrorKind::WouldBlock => {
                std::thread::sleep(std::time::Duration::from_millis(20));
            }
            Err(_) => break,
        }
    }
}

/// Read one framed message header+body from `s`. Returns `(kind, body)` or an
/// error (EOF maps to `UnexpectedEof`). Caps the body at `EW_MAX_FRAME`.
fn ew_read_frame(s: &mut std::os::unix::net::UnixStream) -> std::io::Result<(u8, Vec<u8>)> {
    use std::io::Read;
    let mut hdr = [0u8; 5];
    s.read_exact(&mut hdr)?;
    let len = u32::from_be_bytes([hdr[1], hdr[2], hdr[3], hdr[4]]);
    if len > EW_MAX_FRAME {
        return Err(std::io::Error::new(
            std::io::ErrorKind::InvalidData,
            "frame too large",
        ));
    }
    let mut body = vec![0u8; len as usize];
    if len > 0 {
        s.read_exact(&mut body)?;
    }
    Ok((hdr[0], body))
}

/// Send one framed message. `s` is shared (multiple pump threads write), so the
/// header+body go out under the caller's lock.
fn ew_send_frame(
    s: &Mutex<std::os::unix::net::UnixStream>,
    kind: u8,
    payload: &[u8],
) -> std::io::Result<()> {
    use std::io::Write;
    let mut g = s.lock().unwrap();
    let mut hdr = [0u8; 5];
    hdr[0] = kind;
    hdr[1..5].copy_from_slice(&(payload.len() as u32).to_be_bytes());
    g.write_all(&hdr)?;
    if !payload.is_empty() {
        g.write_all(payload)?;
    }
    g.flush()
}

/// Handle one connection: first frame is REQUEST (exec) or CONTROL (probe/file/…).
fn handle_conn(
    mut stream: std::os::unix::net::UnixStream,
    root: &std::path::Path,
    cfg: &SandboxCfg,
    workload: &std::sync::Arc<Mutex<Option<Child>>>,
    launch: Option<std::sync::Arc<Mutex<c_int>>>,
) -> std::io::Result<()> {
    let (kind, body) = ew_read_frame(&mut stream)?;
    match kind {
        EW_CONTROL => handle_control(&mut stream, root, &body, workload),
        EW_REQUEST => handle_request(stream, root, cfg, &body, launch),
        EW_BRIDGE if launch.is_some() => {
            std::thread::Builder::new()
                .name("sentry-loop-bridge".into())
                .spawn(move || {
                    let _ = handle_bridge(stream, &body);
                })?;
            Ok(())
        }
        _ => Ok(()),
    }
}

/// Connect a raw host-side stream to a listener in this supervisor's owned
/// LoopNet. The listener may appear shortly after pool acquisition, so retry for
/// the same bounded startup window used by the workload tests.
fn handle_bridge(mut stream: std::os::unix::net::UnixStream, body: &[u8]) -> std::io::Result<()> {
    use std::io::{Read, Write};
    if body.len() != 2 {
        stream.write_all(&[0])?;
        return Ok(());
    }
    let port = u16::from_be_bytes([body[0], body[1]]);
    let sid = loop_state().lock().unwrap().net.socket();
    let efd = loop_new_efd();
    loop_state().lock().unwrap().efds.insert(sid, efd);
    let deadline = std::time::Instant::now() + std::time::Duration::from_secs(15);
    let connected = loop {
        let result = {
            let mut ls = loop_state().lock().unwrap();
            let result = if port == 0 {
                ls.net.connect_first(sid)
            } else {
                ls.net
                    .connect(sid, netstack::Endpoint::v4([127, 0, 0, 1], port))
            };
            if let Ok(listener) = result {
                loop_wake(&ls, listener);
            }
            result
        };
        match result {
            Ok(_) => break true,
            Err(netstack::ECONNREFUSED) if std::time::Instant::now() < deadline => {
                std::thread::sleep(std::time::Duration::from_millis(10));
            }
            Err(_) => break false,
        }
    };
    stream.write_all(&[u8::from(connected)])?;
    if !connected {
        loop_close(sid);
        return Ok(());
    }

    let mut reader = stream.try_clone()?;
    let writer = std::sync::Arc::new(Mutex::new(stream));
    let writer2 = writer.clone();
    let up = std::thread::Builder::new()
        .name("sentry-bridge-host-to-loop".into())
        .spawn(move || {
            let mut buf = [0u8; 16 * 1024];
            loop {
                match reader.read(&mut buf) {
                    Ok(0) | Err(_) => break,
                    Ok(n) if loop_send_bytes(sid, &buf[..n]) < 0 => break,
                    Ok(_) => {}
                }
            }
            let _ = loop_state()
                .lock()
                .unwrap()
                .net
                .shutdown(sid, netstack::SHUT_WR);
            let _ = writer.lock().unwrap().shutdown(std::net::Shutdown::Write);
        })?;
    let down = std::thread::Builder::new()
        .name("sentry-bridge-loop-to-host".into())
        .spawn(move || {
            loop {
                let (n, data) = loop_recv_bytes(sid, 16 * 1024, false);
                if n <= 0 {
                    break;
                }
                if writer2.lock().unwrap().write_all(&data).is_err() {
                    break;
                }
            }
            let _ = writer2.lock().unwrap().shutdown(std::net::Shutdown::Both);
        })?;
    let _ = up.join();
    let _ = down.join();
    loop_close(sid);
    Ok(())
}

/// Serve a CONTROL action (probe / write_file / read_file / signal / lifecycle
/// no-ops). Replies with a single CONTROL ack frame `{ok, …}`.
fn handle_control(
    stream: &mut std::os::unix::net::UnixStream,
    root: &std::path::Path,
    body: &[u8],
    workload: &std::sync::Arc<Mutex<Option<Child>>>,
) -> std::io::Result<()> {
    let v: serde_json::Value = serde_json::from_slice(body).unwrap_or(serde_json::Value::Null);
    let action = v.get("action").and_then(|a| a.as_str()).unwrap_or("");
    let ack: serde_json::Value = match action {
        "probe" => serde_json::json!({ "ok": true, "protocol": EW_PROTOCOL }),
        "write_file" => {
            let path = v.get("path").and_then(|p| p.as_str()).unwrap_or("");
            let data = v.get("data_b64").and_then(|d| d.as_str()).unwrap_or("");
            let mode = v.get("mode").and_then(|m| m.as_u64());
            match crate::api::b64_decode(data) {
                Ok(bytes) => match write_in_root(root, path, &bytes, mode) {
                    Ok(()) => serde_json::json!({ "ok": true }),
                    Err(e) => serde_json::json!({ "ok": false, "error": e.to_string() }),
                },
                Err(e) => serde_json::json!({ "ok": false, "error": e }),
            }
        }
        "read_file" => {
            let path = v.get("path").and_then(|p| p.as_str()).unwrap_or("");
            let max = v
                .get("max_bytes")
                .and_then(|m| m.as_u64())
                .unwrap_or(4 * 1024 * 1024);
            match read_in_root(&Some(root.to_path_buf()), path) {
                Ok(bytes) if bytes.len() as u64 <= max => {
                    serde_json::json!({ "ok": true, "data_b64": crate::api::b64_encode(&bytes) })
                }
                Ok(_) => serde_json::json!({ "ok": false, "error": "file exceeds max_bytes" }),
                Err(e) => serde_json::json!({ "ok": false, "error": e.to_string() }),
            }
        }
        "signal" => {
            let signum = v.get("signum").and_then(|s| s.as_i64()).unwrap_or(15) as i32;
            if let Some(c) = workload.lock().unwrap().as_ref() {
                unsafe { libc::kill(-c.pid, signum) };
            }
            serde_json::json!({ "ok": true })
        }
        // Snapshot-lifecycle actions: no guest kernel to act on — succeed as no-ops
        // so the host's restore path doesn't error.
        "sync_time" | "sync" | "drop_vfs_caches" | "smpark_park" | "smpark_unpark" => {
            serde_json::json!({ "ok": true })
        }
        other => serde_json::json!({ "ok": false, "error": format!("unknown action: {other}") }),
    };
    let m = Mutex::new(stream.try_clone()?);
    let body = serde_json::to_vec(&ack).unwrap_or_default();
    ew_send_frame(&m, EW_CONTROL, &body)
}

/// Confined host-side write into the rootfs for the `write_file` CONTROL action:
/// `openat2(rootfd, path, RESOLVE_IN_ROOT, O_WRONLY|O_CREAT|O_TRUNC)` so the path
/// can't escape the image. `mode` defaults to 0o644.
fn write_in_root(
    root: &std::path::Path,
    guest: &str,
    bytes: &[u8],
    mode: Option<u64>,
) -> std::io::Result<()> {
    use std::os::unix::ffi::OsStrExt;
    let mut rootb = root.as_os_str().as_bytes().to_vec();
    rootb.push(0);
    let rootfd = unsafe {
        host(
            SYS_OPENAT,
            (-100i64) as u64,
            rootb.as_ptr() as u64,
            O_PATH | O_DIRECTORY,
            0,
            0,
            0,
        )
    };
    if rootfd < 0 {
        return Err(std::io::Error::from_raw_os_error(-rootfd as i32));
    }
    let mut gp = guest.as_bytes().to_vec();
    gp.push(0);
    const O_WRONLY: u64 = 1;
    const O_CREAT: u64 = 0o100;
    const O_TRUNC: u64 = 0o1000;
    let how = OpenHow {
        flags: O_WRONLY | O_CREAT | O_TRUNC,
        mode: mode.unwrap_or(0o644),
        resolve: RESOLVE_IN_ROOT,
    };
    let fd = unsafe {
        host(
            SYS_OPENAT2,
            rootfd as u64,
            gp.as_ptr() as u64,
            std::ptr::addr_of!(how) as u64,
            std::mem::size_of::<OpenHow>() as u64,
            0,
            0,
        )
    };
    unsafe { host(SYS_CLOSE, rootfd as u64, 0, 0, 0, 0, 0) };
    if fd < 0 {
        return Err(std::io::Error::from_raw_os_error(-fd as i32));
    }
    // Force the EXACT mode: openat2's create-mode is umask-masked, so a 0o755 stage
    // could lose its +x bits and an exec'd staged script (stage_file_mode) would
    // fail. fchmod is umask-independent.
    if let Some(m) = mode {
        unsafe { host(SYS_FCHMOD, fd as u64, m, 0, 0, 0, 0) };
    }
    let mut off = 0usize;
    while off < bytes.len() {
        let n = unsafe {
            host(
                SYS_WRITE,
                fd as u64,
                bytes[off..].as_ptr() as u64,
                (bytes.len() - off) as u64,
                0,
                0,
                0,
            )
        };
        if n <= 0 {
            unsafe { host(SYS_CLOSE, fd as u64, 0, 0, 0, 0, 0) };
            return Err(std::io::Error::last_os_error());
        }
        off += n as usize;
    }
    unsafe { host(SYS_CLOSE, fd as u64, 0, 0, 0, 0, 0) };
    Ok(())
}

/// Serve a REQUEST: parse `{argv, env, cwd, stage_files, chain}`, run argv (then
/// each `chain` argv while the previous exited 0) as sealed cells, pump stdio over
/// the socket, and send the final EXIT frame.
fn handle_request(
    stream: std::os::unix::net::UnixStream,
    root: &std::path::Path,
    base_cfg: &SandboxCfg,
    body: &[u8],
    // When `Some(launch_fd)`, fork cells IN-PROCESS via the persistent supervisor's
    // launcher (the unified workload+exec backend) so they share its loop_state +
    // proctree; when `None`, the legacy ExecServer path (spawn_sandbox per cell).
    // The guard is intentionally held for the whole request: the launcher replies
    // with both pid and final wait status on one socket, so concurrent exec launches
    // must not interleave. Bridge/control connections do not take this lock.
    launch: Option<std::sync::Arc<Mutex<c_int>>>,
) -> std::io::Result<()> {
    let _launch_guard = launch.as_ref().map(|l| l.lock().unwrap());
    let launch = _launch_guard.as_ref().map(|g| **g);

    let v: serde_json::Value = serde_json::from_slice(body)
        .map_err(|e| std::io::Error::new(std::io::ErrorKind::InvalidData, e))?;
    let argv = json_str_vec(v.get("argv"));
    let writer = std::sync::Arc::new(Mutex::new(stream.try_clone()?));
    if argv.is_empty() {
        let _ = ew_send_frame(&writer, EW_ERROR, b"REQUEST.argv is empty");
        return Ok(());
    }
    // Build a per-request cfg carrying the guest env + cwd. The request env
    // OVERLAYS the sandbox base env (the image's `Env`, seeded via `with_env`):
    // keep every base key the request didn't set, and let request keys win on
    // collision — so an `exec` sees the image's `PATH`/`LANG`/etc unless it
    // overrides them, matching what a command sees inside a booted VM.
    let mut cfg = base_cfg.clone();
    let req_env = json_env(v.get("env"));
    cfg.env = merge_env(&base_cfg.env, &req_env);
    cfg.cwd = v.get("cwd").and_then(|c| c.as_str()).map(|s| s.to_string());

    // tty mode: run the (single) command on a pty instead of three pipes, so the
    // guest sees a real controlling terminal (isatty, raw mode, ^C, SIGWINCH).
    // `cols`/`rows` seed the initial window size; the host can resize later via
    // RESIZE frames. tty is per-request and applies to the FIRST command only —
    // a `chain` after it (rare under tty) falls back to pipe mode.
    let tty = v.get("tty").and_then(|t| t.as_bool()).unwrap_or(false);
    let cols = v.get("cols").and_then(|c| c.as_u64()).map(|c| c as u16);
    let rows = v.get("rows").and_then(|r| r.as_u64()).map(|r| r as u16);

    // stage_files: write each into the rootfs before running (confined). A failed
    // write is fatal — like the in-guest agent, surface it as an ERROR frame and a
    // 127-style EXIT so the host's wait() returns instead of running the command
    // against missing inputs (or hanging). Matches `agent::spawn_pipes`.
    if let Some(sf) = v.get("stage_files").and_then(|s| s.as_array()) {
        for f in sf {
            let path = f.get("path").and_then(|p| p.as_str()).unwrap_or("");
            let data = f.get("data_b64").and_then(|d| d.as_str()).unwrap_or("");
            let mode = f.get("mode").and_then(|m| m.as_u64());
            let res = match crate::api::b64_decode(data) {
                Ok(bytes) => write_in_root(root, path, &bytes, mode),
                Err(e) => Err(std::io::Error::new(std::io::ErrorKind::InvalidData, e)),
            };
            if let Err(e) = res {
                let _ = ew_send_frame(
                    &writer,
                    EW_ERROR,
                    format!("stage_file({path}): {e}").as_bytes(),
                );
                send_exit_with_rss(&writer, 127);
                return Ok(());
            }
        }
    }

    // argv + chain run sequentially; stop on the first non-zero exit.
    let mut argvs: Vec<Vec<String>> = vec![argv];
    for c in v
        .get("chain")
        .and_then(|c| c.as_array())
        .into_iter()
        .flatten()
    {
        argvs.push(json_str_vec(Some(c)));
    }
    let mut last = 0i32;
    for (i, av) in argvs.iter().enumerate() {
        if av.is_empty() {
            continue;
        }
        // Only the FIRST command consumes host stdin; chained commands get EOF.
        // tty mode applies to the first command only.
        last = match (launch, tty && i == 0) {
            // Unified in-process backend.
            (Some(lfd), true) => {
                run_one_tty_inproc(&stream, &writer, root, &cfg, av, cols, rows, lfd)?
            }
            (Some(lfd), false) => run_one_inproc(&stream, &writer, root, &cfg, av, i == 0, lfd)?,
            // Legacy ExecServer (separate supervisor per cell).
            (None, true) => run_one_tty(&stream, &writer, root, &cfg, av, cols, rows)?,
            (None, false) => run_one(&stream, &writer, root, &cfg, av, i == 0)?,
        };
        if last != 0 {
            break;
        }
    }
    // ExecServer: the cells are this process's children, so getrusage(RUSAGE_CHILDREN)
    // measures their peak RSS. Unified: the cells are the LAUNCHER's children (a
    // separate process), so our getrusage would report a bogus 0 — send a code-only
    // EXIT (peak_rss = None) instead. (Real per-cell RSS via a launcher relay is a
    // follow-on.)
    if launch.is_some() {
        let _ = ew_send_frame(&writer, EW_EXIT, &(last as u32).to_be_bytes());
    } else {
        send_exit_with_rss(&writer, last);
    }
    Ok(())
}

/// Send the final EXIT frame: a 12-byte payload of the exit `code` (u32 BE)
/// followed by the peak RSS in KiB (u64 BE), read from
/// `getrusage(RUSAGE_CHILDREN)` — the high-water mark of every cell this server
/// process has reaped. The host's `decode_exit_payload` reads the optional
/// 8-byte tail into [`crate::exec::ExecOutcome::peak_rss_kib`]; mirrors the
/// in-guest agent's EXIT layout exactly. RSS-less callers (resolution/spawn
/// failures that never forked) still get a well-formed frame — the rss is just
/// whatever prior children left in the accumulator (0 if none).
fn send_exit_with_rss(writer: &std::sync::Arc<Mutex<std::os::unix::net::UnixStream>>, code: i32) {
    let mut ru: libc::rusage = unsafe { std::mem::zeroed() };
    let peak_rss_kib: u64 = unsafe {
        if libc::getrusage(libc::RUSAGE_CHILDREN, &mut ru) == 0 {
            ru.ru_maxrss as u64
        } else {
            0
        }
    };
    let mut payload = [0u8; 12];
    payload[0..4].copy_from_slice(&(code as u32).to_be_bytes());
    payload[4..12].copy_from_slice(&peak_rss_kib.to_be_bytes());
    let _ = ew_send_frame(writer, EW_EXIT, &payload);
}

/// Serializes the fork + fd-table critical section of every cell spawn. The host
/// fd table is PROCESS-GLOBAL and fd numbers are reused on close, so two threads
/// each doing `pipe()` … `fork()` … `close()` concurrently (e.g. two in-process
/// `Pool`s / two `build_linear`s racing — sentry_build_cow_isolation) interleave fd
/// allocation: one thread's `close(in_r)` can hit a number a sibling's `pipe()` just
/// handed out, so the sibling's later read/write/dup2 sees EBADF (or a fork inherits
/// a half-wired fd → hang). Holding this lock across [create pipes → spawn_sandbox →
/// parent closes the cell's ends] makes each spawn's fd churn atomic. The forked
/// child inherits the locked mutex (CoW) but NEVER touches it (it runs sandbox_main),
/// so there is no child-side deadlock; the parent holds it only for the brief
/// fork+close window, then the pump/reader threads run lock-free.
fn spawn_lock() -> &'static Mutex<()> {
    static L: OnceLock<Mutex<()>> = OnceLock::new();
    L.get_or_init(|| Mutex::new(()))
}

/// Run ONE argv as a sealed cell, pumping stdin (if `consume_stdin`), stdout, and
/// stderr over the socket, and forwarding SIGNAL frames. Returns the exit code
/// (128+sig if signaled).
fn run_one(
    sock: &std::os::unix::net::UnixStream,
    writer: &std::sync::Arc<Mutex<std::os::unix::net::UnixStream>>,
    root: &std::path::Path,
    cfg: &SandboxCfg,
    argv: &[String],
    consume_stdin: bool,
) -> std::io::Result<i32> {
    // Three pipes: stdin (host -> cell), stdout/stderr (cell -> host). Hold the
    // global spawn lock across pipe-create → fork → parent-close so concurrent
    // spawns don't interleave fd-table churn (see spawn_lock).
    let spawn_guard = spawn_lock().lock().unwrap();
    let mut p_in = [0 as c_int; 2];
    let mut p_out = [0 as c_int; 2];
    let mut p_err = [0 as c_int; 2];
    unsafe {
        if libc::pipe(p_in.as_mut_ptr()) < 0
            || libc::pipe(p_out.as_mut_ptr()) < 0
            || libc::pipe(p_err.as_mut_ptr()) < 0
        {
            return Err(std::io::Error::last_os_error());
        }
    }
    let (in_r, in_w) = (p_in[0], p_in[1]);
    let (out_r, out_w) = (p_out[0], p_out[1]);
    let (err_r, err_w) = (p_err[0], p_err[1]);
    let stdio = Stdio {
        stdin: Some(in_r),
        stdout: Some(out_w),
        stderr: Some(err_w),
        ctty: false,
        ..Stdio::default()
    };
    // Resolve a bare `argv[0]` (no slash) against the merged env's PATH, confined
    // to the rootfs. Unresolved → the command doesn't exist: return 127 (the
    // shell's "command not found" convention; matches the agent's execvpe ENOENT)
    // so the caller's single EXIT frame carries 127 — NOT a torn stream / `Err`.
    let elf = match resolve_in_root_path(root, &path_from_env(&cfg.env), &argv[0]) {
        Some(p) => std::path::PathBuf::from(p),
        None => {
            for fd in [in_r, in_w, out_r, out_w, err_r, err_w] {
                unsafe { libc::close(fd) };
            }
            return Ok(127);
        }
    };
    let pid = match spawn_sandbox(&elf, &argv[1..], Some(root), stdio, true, cfg) {
        Ok(p) => p,
        Err(_e) => {
            // A spawn failure (e.g. fork EAGAIN) is the host's, not the guest's —
            // report it as exit 127 (the caller sends one clean EXIT frame) rather
            // than returning an `Err` the host decodes as a torn stream.
            for fd in [in_r, in_w, out_r, out_w, err_r, err_w] {
                unsafe { libc::close(fd) };
            }
            return Ok(127);
        }
    };
    // Parent drops the cell's ends; keeps in_w (to feed stdin), out_r/err_r (to read).
    unsafe {
        libc::close(in_r);
        libc::close(out_w);
        libc::close(err_w);
    }
    // A command that does NOT consume host stdin (every CHAINED command — only the
    // first gets the host's stdin) must see EOF on fd 0 IMMEDIATELY, else a reader
    // like `cat` blocks on read(0) forever and the chain times out. The lib closes
    // host stdin once for the whole connection, so the reader thread below never
    // gets an empty-EW_STDIN frame for these — so close in_w here. (When
    // consume_stdin, the reader owns in_w and closes it on the EOF frame.)
    if !consume_stdin {
        unsafe { libc::close(in_w) };
    }
    // fd-table churn for this spawn is done — let a sibling spawn proceed while our
    // pump/reader threads run lock-free.
    drop(spawn_guard);

    // stdout/stderr pump threads: pipe -> framed STDOUT/STDERR.
    let pump = |fd: c_int, kind: u8, w: std::sync::Arc<Mutex<std::os::unix::net::UnixStream>>| {
        std::thread::spawn(move || {
            let mut buf = [0u8; 32 * 1024];
            loop {
                let n = unsafe { libc::read(fd, buf.as_mut_ptr() as *mut c_void, buf.len()) };
                if n <= 0 {
                    break;
                }
                if ew_send_frame(&w, kind, &buf[..n as usize]).is_err() {
                    break;
                }
            }
            unsafe { libc::close(fd) };
        })
    };
    let t_out = pump(out_r, EW_STDOUT, writer.clone());
    let t_err = pump(err_r, EW_STDERR, writer.clone());

    // Reader thread: host STDIN -> cell stdin pipe; SIGNAL -> killpg. Unblocked
    // when the cell exits via `shutdown(Read)` below.
    let reader_done = std::sync::Arc::new(AtomicBool::new(false));
    let t_in = {
        let mut rs = sock.try_clone()?;
        let reader_done = reader_done.clone();
        std::thread::spawn(move || {
            loop {
                match ew_read_frame(&mut rs) {
                    // Only the stdin-consuming command owns in_w here; a non-consuming
                    // (chained) command already closed in_w above, so ignore its stdin
                    // frames (handling them would double-close in_w → wrong fd).
                    Ok((EW_STDIN, data)) if consume_stdin => {
                        if data.is_empty() {
                            unsafe { libc::close(in_w) }; // stdin EOF
                            continue;
                        }
                        let mut off = 0usize;
                        while off < data.len() {
                            let n = unsafe {
                                libc::write(
                                    in_w,
                                    data[off..].as_ptr() as *const c_void,
                                    data.len() - off,
                                )
                            };
                            if n <= 0 {
                                break;
                            }
                            off += n as usize;
                        }
                    }
                    Ok((EW_SIGNAL, data)) if !data.is_empty() => {
                        signal_cell_tree(pid, data[0] as c_int);
                    }
                    Ok(_) => {}
                    Err(_) => break,
                }
                if reader_done.load(Ordering::Relaxed) {
                    break;
                }
            }
        })
    };

    // Wait for the cell tree to exit (drains because the pump threads are live).
    // BOUNDED by the Pool/Sandbox exec watchdog (cfg.exec_timeout_ms; 0 = unlimited):
    // a wedged or fork+signal-trapping RUN is SIGKILL'd at the deadline and reported
    // as 137 — so a PRODUCTION Vm/Pool exec (this is the ExecServer path that
    // Vm::start_sentry + SentryPool::acquire().exec() use) can never hang forever.
    let (code, _) = wait_exit_deadline(pid, cfg.exec_timeout_ms)?;
    // Unblock + join the reader, close the stdin write end, join the pumps.
    reader_done.store(true, Ordering::Relaxed);
    let _ = sock.shutdown(std::net::Shutdown::Read);
    unsafe { libc::close(in_w) };
    let _ = t_in.join();
    let _ = t_out.join();
    let _ = t_err.join();
    Ok(code)
}

/// Signal a launched cell's whole tree, but ONLY via `killpg` when the cell is the
/// LEADER of its own process group (the launcher child `setpgid(0,0)`s, so once
/// that has run `getpgid(pid)==pid`). Before that race resolves — or for any cell
/// that never led a group — fall back to a single-pid `kill(pid)`. This is the
/// guard that keeps `kill(-pid)` from ever landing on the supervisor's / harness's
/// own group (a stray `kill(-pid)` whose `pid` is still in the parent group would
/// otherwise nuke unrelated processes). Mirrors `await_launched_exit`'s guard.
fn signal_cell_tree(pid: i32, sig: c_int) {
    unsafe {
        if libc::getpgid(pid) == pid {
            libc::kill(-pid, sig);
        } else {
            libc::kill(pid, sig);
        }
    }
}
/// Like [`run_one`] but forks the cell IN-PROCESS via the persistent supervisor's
/// single-threaded launcher (`launch_cell_env`) instead of `spawn_sandbox` — so the
/// cell shares this supervisor's `loop_state` (owned loopback) + proctree with the
/// backgrounded workload and with sibling execs (the unified workload+exec model).
/// stdin/stdout/stderr are three pipes wired into the cell's fd table
/// ([`seed_cell_fds_streaming`]) and serviced over the ring; the same pump/reader
/// threads frame them to/from the connection. `launch_fd` is the launcher socket.
#[cfg(all(target_os = "linux", target_arch = "x86_64"))]
fn run_one_inproc(
    sock: &std::os::unix::net::UnixStream,
    writer: &std::sync::Arc<Mutex<std::os::unix::net::UnixStream>>,
    root: &std::path::Path,
    cfg: &SandboxCfg,
    argv: &[String],
    consume_stdin: bool,
    launch_fd: c_int,
) -> std::io::Result<i32> {
    // Resolve a bare argv[0] via PATH, rootfs-confined (unresolved ⇒ 127, like run_one).
    let elf = match resolve_in_root_path(root, &path_from_env(&cfg.env), &argv[0]) {
        Some(p) => p,
        None => return Ok(127),
    };
    let elf = canonical_exe_in_root(root, &elf).unwrap_or(elf);
    let env_kv: Vec<String> = cfg.env.iter().map(|(k, v)| format!("{k}={v}")).collect();

    // Three pipes: stdin (host→cell), stdout/stderr (cell→host).
    let spawn_guard = spawn_lock().lock().unwrap();
    let mut p_in = [0 as c_int; 2];
    let mut p_out = [0 as c_int; 2];
    let mut p_err = [0 as c_int; 2];
    unsafe {
        if libc::pipe(p_in.as_mut_ptr()) < 0
            || libc::pipe(p_out.as_mut_ptr()) < 0
            || libc::pipe(p_err.as_mut_ptr()) < 0
        {
            return Err(std::io::Error::last_os_error());
        }
    }
    let (in_r, in_w) = (p_in[0], p_in[1]);
    let (out_r, out_w) = (p_out[0], p_out[1]);
    let (err_r, err_w) = (p_err[0], p_err[1]);
    let pid = launch_cell_env(
        launch_fd,
        Some(&elf),
        argv,
        false,
        &env_kv,
        cfg.cwd.as_deref(),
    );
    if pid <= 0 {
        for fd in [in_r, in_w, out_r, out_w, err_r, err_w] {
            unsafe { libc::close(fd) };
        }
        return Ok(127);
    }
    // Wire the cell's fd 0/1/2 to our pipes (dups), seed the SUPERVISOR cwd (getcwd
    // is supervisor-serviced — the cell's own seed is private), then drop OUR copies
    // of the cell-side ends so EOF propagates when the cell exits.
    seed_cell_fds_streaming(pid, in_r, out_w, err_w);
    if let Some(c) = &cfg.cwd {
        cwds().lock().unwrap().insert(pid, c.as_bytes().to_vec());
    }
    unsafe { set_ring_owner(0, pid, pid) };
    continue_launched_cell(pid);
    unsafe {
        libc::close(in_r);
        libc::close(out_w);
        libc::close(err_w);
    }
    if !consume_stdin {
        unsafe { libc::close(in_w) };
    }
    drop(spawn_guard);

    let pump = |fd: c_int, kind: u8, w: std::sync::Arc<Mutex<std::os::unix::net::UnixStream>>| {
        std::thread::spawn(move || {
            let mut buf = [0u8; 32 * 1024];
            loop {
                let n = unsafe { libc::read(fd, buf.as_mut_ptr() as *mut c_void, buf.len()) };
                if n <= 0 {
                    break;
                }
                if ew_send_frame(&w, kind, &buf[..n as usize]).is_err() {
                    break;
                }
            }
            unsafe { libc::close(fd) };
        })
    };
    let t_out = pump(out_r, EW_STDOUT, writer.clone());
    let t_err = pump(err_r, EW_STDERR, writer.clone());
    let reader_done = std::sync::Arc::new(AtomicBool::new(false));
    let t_in = {
        let mut rs = sock.try_clone()?;
        // Read with a TIMEOUT (not shutdown(Read)) so the reader notices this
        // command's exit (`reader_done`) WITHOUT closing the connection's read half —
        // chained commands share one connection, so a per-command shutdown(Read)
        // would make every LATER command's reader see EOF and (via the close-kill
        // below) abort the chain. A genuine client close returns EOF (a kind other
        // than WouldBlock/TimedOut).
        let _ = rs.set_read_timeout(Some(std::time::Duration::from_millis(200)));
        let reader_done = reader_done.clone();
        std::thread::spawn(move || loop {
            match ew_read_frame(&mut rs) {
                Ok((EW_STDIN, data)) if consume_stdin => {
                    if data.is_empty() {
                        unsafe { libc::close(in_w) };
                        continue;
                    }
                    let mut off = 0usize;
                    while off < data.len() {
                        let n = unsafe {
                            libc::write(
                                in_w,
                                data[off..].as_ptr() as *const c_void,
                                data.len() - off,
                            )
                        };
                        if n <= 0 {
                            break;
                        }
                        off += n as usize;
                    }
                }
                Ok((EW_SIGNAL, data)) if !data.is_empty() => {
                    signal_cell_tree(pid, data[0] as c_int);
                }
                Ok(_) => {}
                // Read timeout: not a close — poll `reader_done`, else keep reading so
                // the connection stays usable for the next chained command.
                Err(ref e)
                    if e.kind() == std::io::ErrorKind::WouldBlock
                        || e.kind() == std::io::ErrorKind::TimedOut =>
                {
                    if reader_done.load(Ordering::Relaxed) {
                        break;
                    }
                }
                // Genuine EOF/error = the client closed mid-command (per-request
                // `.timeout()`, cancel, or drop). Unlike ExecServer (each exec its own
                // throwaway supervisor), a leaked runaway here would block this serial
                // accept thread (await waits the long cfg watchdog) AND clobber ring
                // slot 0 for the next exec — so SIGKILL the cell tree.
                Err(_) => {
                    if !reader_done.load(Ordering::Relaxed) {
                        signal_cell_tree(pid, libc::SIGKILL);
                    }
                    break;
                }
            }
        })
    };
    // Wait via the launcher's status relay, bounded by the exec watchdog.
    let (code, _) = await_launched_exit(launch_fd, pid, cfg.exec_timeout_ms);
    shm_reap_pid(pid);
    fd_drop(pid);
    free_slots_of(pid);
    // PARITY with KVM/HVF: a pool VM is a PERSISTENT process space (this unified
    // in-process supervisor IS the VM — it shares loop_state + proctree across all
    // execs). So a detached daemon started by one exec (postgres, an app server)
    // MUST survive into later execs, exactly as a real VM's kernel keeps it — only
    // an explicit pool release / VM-destroy reaps it (release kills the
    // running_instances set). The foreground's own slots were just freed
    // (free_slots_of(pid) above), so ring slot 0 is clear for the next exec, and
    // surviving daemons sit on their own slots 1+. Well-behaved daemons
    // redirect/close stdio (postgres → -l logfile) so the exec_capture pipe still
    // EOFs; one that holds the pipe blocks until the exec watchdog, same as a real
    // VM's agent reading the foreground's pipe. Previously normal execs swept
    // (SIGKILL'd) every straggler — which killed e.g. a postgres the next exec
    // needed (the cross-backend conformance starts postgres in one exec and uses it
    // from another). Bake-time warmups already preserved (to capture daemons into
    // the C4 snapshot); pool execs now match. The Err/abort path above still
    // SIGKILLs a runaway when the client drops mid-command.
    // PARITY with KVM/HVF: this unified in-process supervisor IS the persistent VM
    // (shared loop_state + proctree across execs), so a detached daemon started by one
    // exec (postgres, an app server) MUST survive into later execs — exactly as a real
    // VM's kernel keeps it; only pool release / VM-destroy reaps it. The foreground's
    // own slot 0 was freed (free_slots_of above) so the next exec is clear; surviving
    // daemons sit on slots 1+. (Confirmed NOT the postgres-start hang — that was the
    // loop-unaware select/pselect6 servicing, fixed in poll_merge_entries. Bake-time
    // already preserved to capture daemons into the C4 snapshot; pool execs now match.)
    let _ = cfg.preserve_stragglers;
    preserve_stragglers_as_running(0);
    reader_done.store(true, Ordering::Relaxed);
    unsafe { libc::close(in_w) };
    let _ = t_in.join();
    let _ = t_out.join();
    let _ = t_err.join();
    Ok(code)
}

/// In-process tty exec. A real ctty pty needs the cell to `setsid`+`TIOCSCTTY`
/// (which `spawn_sandbox`'s `Stdio::ctty` does but the in-process launcher path
/// doesn't yet), so for now the unified backend serves a tty request as pipe-mode
/// (`run_one_inproc`) — correct output, just no controlling terminal. No current
/// test exercises tty on a pool-backed Vm; the ExecServer + warm-daemon paths keep
/// the real pty. (Follow-on: pty alloc + cell-side setsid for full ctty parity.)
#[cfg(all(target_os = "linux", target_arch = "x86_64"))]
#[allow(clippy::too_many_arguments)]
fn run_one_tty_inproc(
    sock: &std::os::unix::net::UnixStream,
    writer: &std::sync::Arc<Mutex<std::os::unix::net::UnixStream>>,
    root: &std::path::Path,
    cfg: &SandboxCfg,
    argv: &[String],
    _cols: Option<u16>,
    _rows: Option<u16>,
    launch_fd: c_int,
) -> std::io::Result<i32> {
    run_one_inproc(sock, writer, root, cfg, argv, true, launch_fd)
}

/// Run ONE argv as a sealed cell on a **pty** (tty mode): the cell's
/// stdin/stdout/stderr are all the pty slave, and the cell becomes a session
/// leader owning it as the controlling terminal (`Stdio::ctty`). The host holds
/// the master: STDIN frames are written to it, master output is framed back as
/// STDOUT (stderr is merged into the pty, so no STDERR frames), RESIZE frames
/// drive `TIOCSWINSZ`, and SIGNAL frames `killpg` the tree. Returns the exit code
/// (128+sig if signaled). Mirrors the in-guest agent's `spawn_tty`.
fn run_one_tty(
    sock: &std::os::unix::net::UnixStream,
    writer: &std::sync::Arc<Mutex<std::os::unix::net::UnixStream>>,
    root: &std::path::Path,
    cfg: &SandboxCfg,
    argv: &[String],
    cols: Option<u16>,
    rows: Option<u16>,
) -> std::io::Result<i32> {
    // Hold the global spawn lock across pty-alloc → fork → parent-close so
    // concurrent spawns don't interleave fd-table churn (see spawn_lock / run_one).
    let spawn_guard = spawn_lock().lock().unwrap();
    // Allocate a pty. The host keeps the master; the cell opens the slave (by
    // path) after `setsid` so it becomes the controlling terminal (done in
    // `spawn_sandbox` via `Stdio::ctty`).
    let pty = open_pty()?;
    let master = pty.master;
    if let (Some(c), Some(r)) = (cols, rows) {
        let _ = set_winsize(master, c, r);
    }
    // Open a host-side slave fd to seed the cell's 0/1/2. The cell dup2's it onto
    // 0/1/2 and (for fd 0) acquires it as ctty; `spawn_sandbox` closes this
    // original copy in the child after the dup2.
    let slave_c = match std::ffi::CString::new(pty.slave_path.clone()) {
        Ok(c) => c,
        Err(_) => {
            cleanup_private_devpts(pty.private_devpts.as_deref());
            unsafe { libc::close(master) };
            return Err(std::io::Error::new(
                std::io::ErrorKind::InvalidData,
                "pty slave path NUL",
            ));
        }
    };
    let slave = unsafe { libc::open(slave_c.as_ptr(), libc::O_RDWR) };
    if slave < 0 {
        let e = std::io::Error::last_os_error();
        cleanup_private_devpts(pty.private_devpts.as_deref());
        unsafe { libc::close(master) };
        return Err(e);
    }
    cleanup_private_devpts(pty.private_devpts.as_deref());
    let stdio = Stdio {
        stdin: Some(slave),
        stdout: Some(slave),
        stderr: Some(slave),
        ctty: true,
        ..Stdio::default()
    };
    // Resolve a bare `argv[0]` against the merged env's PATH, confined to the
    // rootfs (see `run_one`). Unresolved / spawn failure → exit 127 cleanly.
    let elf = match resolve_in_root_path(root, &path_from_env(&cfg.env), &argv[0]) {
        Some(p) => std::path::PathBuf::from(p),
        None => {
            unsafe {
                libc::close(master);
                libc::close(slave);
            }
            return Ok(127);
        }
    };
    let pid = match spawn_sandbox(&elf, &argv[1..], Some(root), stdio, true, cfg) {
        Ok(p) => p,
        Err(_e) => {
            unsafe {
                libc::close(master);
                libc::close(slave);
            }
            return Ok(127);
        }
    };
    // Parent: drop the slave (the cell has its own copy), keep the master.
    unsafe { libc::close(slave) };
    // fd-table churn for this spawn is done — release for sibling spawns.
    drop(spawn_guard);

    // master -> framed STDOUT. Once the cell (and any descendants holding the
    // slave) exit, the master read returns EIO, so this pump finishes on its own
    // after flushing every buffered byte. Signal completion so teardown can wait
    // for the drain before force-closing.
    let (pump_done_tx, pump_done_rx) = std::sync::mpsc::channel::<()>();
    let t_out = {
        let w = writer.clone();
        std::thread::spawn(move || {
            let mut buf = [0u8; 32 * 1024];
            loop {
                let n = unsafe { libc::read(master, buf.as_mut_ptr() as *mut c_void, buf.len()) };
                if n <= 0 {
                    break;
                }
                if ew_send_frame(&w, EW_STDOUT, &buf[..n as usize]).is_err() {
                    break;
                }
            }
            let _ = pump_done_tx.send(());
        })
    };

    // host STDIN -> master; RESIZE -> TIOCSWINSZ; SIGNAL -> killpg. Unblocked when
    // the cell exits via `shutdown(Read)` below.
    let reader_done = std::sync::Arc::new(AtomicBool::new(false));
    let t_in = {
        let mut rs = sock.try_clone()?;
        let reader_done = reader_done.clone();
        std::thread::spawn(move || {
            loop {
                match ew_read_frame(&mut rs) {
                    Ok((EW_STDIN, data)) => {
                        if data.is_empty() {
                            // Half-close on a tty: deliver EOF (^D) rather than
                            // closing the master (which would kill the session).
                            let eof = [0x04u8];
                            unsafe {
                                libc::write(master, eof.as_ptr() as *const c_void, 1);
                            }
                            continue;
                        }
                        let mut off = 0usize;
                        while off < data.len() {
                            let n = unsafe {
                                libc::write(
                                    master,
                                    data[off..].as_ptr() as *const c_void,
                                    data.len() - off,
                                )
                            };
                            if n <= 0 {
                                break;
                            }
                            off += n as usize;
                        }
                    }
                    Ok((EW_RESIZE, data)) if data.len() == 4 => {
                        let cols = u16::from_be_bytes([data[0], data[1]]);
                        let rows = u16::from_be_bytes([data[2], data[3]]);
                        let _ = set_winsize(master, cols, rows);
                    }
                    Ok((EW_SIGNAL, data)) if !data.is_empty() => {
                        signal_cell_tree(pid, data[0] as c_int);
                    }
                    Ok(_) => {}
                    Err(_) => break,
                }
                if reader_done.load(Ordering::Relaxed) {
                    break;
                }
            }
        })
    };

    // Wait for the cell tree to exit. BOUNDED by the exec watchdog
    // (cfg.exec_timeout_ms; 0 = unlimited) so a wedged tty exec can't hang forever.
    let (code, _) = wait_exit_deadline(pid, cfg.exec_timeout_ms)?;
    // Drain the pty BEFORE force-closing the master or sending EXIT, so the host
    // sees every byte a fast-exiting program wrote. Bounded so a backgrounded
    // descendant still holding the slave can't wedge us forever.
    let drained = pump_done_rx
        .recv_timeout(std::time::Duration::from_secs(2))
        .is_ok();
    if !drained {
        // A descendant still holds the slave open, so the master never hit EOF.
        // No shutdown(2) for char devices — dup2 /dev/null over the master to
        // force the pump's next read to return EOF.
        unsafe {
            let null = libc::open(b"/dev/null\0".as_ptr() as *const c_char, libc::O_RDWR);
            if null >= 0 {
                libc::dup2(null, master);
                libc::close(null);
            }
        }
    }
    // Unblock + join the reader, then close the master and join the pump.
    reader_done.store(true, Ordering::Relaxed);
    let _ = sock.shutdown(std::net::Shutdown::Read);
    let _ = t_in.join();
    let _ = t_out.join();
    unsafe { libc::close(master) };
    Ok(code)
}

struct OpenPty {
    master: c_int,
    slave_path: Vec<u8>,
    private_devpts: Option<std::path::PathBuf>,
}

/// Allocate a pty: returns the master fd (host-owned) and the slave device path.
/// Prefer the host's normal `/dev/ptmx`; if the host/container has no devpts
/// mounted there (`ENODEV`), create a private temporary devpts mount, open its
/// `ptmx`, and remove the mount after the slave fd has been opened.
fn open_pty() -> std::io::Result<OpenPty> {
    match open_pty_at(b"/dev/ptmx\0", None) {
        Ok(pty) => Ok(pty),
        Err(e) if e.raw_os_error() == Some(libc::ENODEV) => open_private_devpts_pty(),
        Err(e) => Err(e),
    }
}

fn open_pty_at(
    ptmx_path: &[u8],
    private_devpts: Option<std::path::PathBuf>,
) -> std::io::Result<OpenPty> {
    let master = unsafe {
        libc::open(
            ptmx_path.as_ptr() as *const c_char,
            libc::O_RDWR | libc::O_NOCTTY,
        )
    };
    if master < 0 {
        return Err(std::io::Error::last_os_error());
    }
    let fail = |m: c_int| {
        let e = std::io::Error::last_os_error();
        unsafe { libc::close(m) };
        e
    };
    if unsafe { libc::grantpt(master) } < 0 {
        return Err(fail(master));
    }
    if unsafe { libc::unlockpt(master) } < 0 {
        return Err(fail(master));
    }
    let slave_path = if let Some(dir) = private_devpts.as_ref() {
        let mut n: libc::c_uint = 0;
        if unsafe { libc::ioctl(master, libc::TIOCGPTN as _, &mut n as *mut _) } < 0 {
            return Err(fail(master));
        }
        dir.join(n.to_string())
            .to_string_lossy()
            .as_bytes()
            .to_vec()
    } else {
        let mut buf = [0u8; 256];
        let r = unsafe { libc::ptsname_r(master, buf.as_mut_ptr() as *mut c_char, buf.len()) };
        if r != 0 {
            unsafe { libc::close(master) };
            return Err(std::io::Error::from_raw_os_error(r));
        }
        let len = buf.iter().position(|&b| b == 0).unwrap_or(buf.len());
        buf[..len].to_vec()
    };
    Ok(OpenPty {
        master,
        slave_path,
        private_devpts,
    })
}

fn open_private_devpts_pty() -> std::io::Result<OpenPty> {
    static PTY_MOUNT_SEQ: AtomicU64 = AtomicU64::new(0);
    let seq = PTY_MOUNT_SEQ.fetch_add(1, Ordering::Relaxed);
    let dir =
        std::env::temp_dir().join(format!("sentry-devpts-{}-{seq}", unsafe { libc::getpid() }));
    std::fs::create_dir_all(&dir)?;
    let target = match std::ffi::CString::new(dir.to_string_lossy().as_bytes().to_vec()) {
        Ok(c) => c,
        Err(_) => {
            let _ = std::fs::remove_dir_all(&dir);
            return Err(std::io::Error::new(
                std::io::ErrorKind::InvalidInput,
                "private devpts path contains NUL",
            ));
        }
    };
    let source = b"devpts\0";
    let fstype = b"devpts\0";
    let data = b"newinstance,ptmxmode=0666,mode=0600\0";
    let mount_rc = unsafe {
        libc::mount(
            source.as_ptr() as *const c_char,
            target.as_ptr(),
            fstype.as_ptr() as *const c_char,
            (libc::MS_NOSUID | libc::MS_NOEXEC) as libc::c_ulong,
            data.as_ptr() as *const c_void,
        )
    };
    if mount_rc < 0 {
        let e = std::io::Error::last_os_error();
        let _ = std::fs::remove_dir_all(&dir);
        return Err(e);
    }
    let ptmx = dir.join("ptmx");
    let ptmx_c = match std::ffi::CString::new(ptmx.to_string_lossy().as_bytes().to_vec()) {
        Ok(c) => c,
        Err(_) => {
            cleanup_private_devpts(Some(&dir));
            return Err(std::io::Error::new(
                std::io::ErrorKind::InvalidInput,
                "private ptmx path contains NUL",
            ));
        }
    };
    match open_pty_at(ptmx_c.as_bytes_with_nul(), Some(dir.clone())) {
        Ok(pty) => Ok(pty),
        Err(e) => {
            cleanup_private_devpts(Some(&dir));
            Err(e)
        }
    }
}

fn cleanup_private_devpts(dir: Option<&std::path::Path>) {
    if let Some(dir) = dir {
        if let Ok(c) = std::ffi::CString::new(dir.to_string_lossy().as_bytes().to_vec()) {
            unsafe {
                libc::umount2(c.as_ptr(), libc::MNT_DETACH);
            }
        }
        let _ = std::fs::remove_dir_all(dir);
    }
}

/// `TIOCSWINSZ` on a pty master.
fn set_winsize(fd: c_int, cols: u16, rows: u16) -> std::io::Result<()> {
    let ws = libc::winsize {
        ws_row: rows,
        ws_col: cols,
        ws_xpixel: 0,
        ws_ypixel: 0,
    };
    let r = unsafe { libc::ioctl(fd, libc::TIOCSWINSZ as _, &ws as *const _) };
    if r < 0 {
        return Err(std::io::Error::last_os_error());
    }
    Ok(())
}

/// Parse a JSON array-of-strings (argv / a chain entry) into `Vec<String>`.
fn json_str_vec(v: Option<&serde_json::Value>) -> Vec<String> {
    v.and_then(|x| x.as_array())
        .map(|a| {
            a.iter()
                .filter_map(|s| s.as_str().map(|s| s.to_string()))
                .collect()
        })
        .unwrap_or_default()
}
/// Parse the REQUEST `env` object into `(KEY, VALUE)` pairs.
fn json_env(v: Option<&serde_json::Value>) -> Vec<(String, String)> {
    v.and_then(|x| x.as_object())
        .map(|o| {
            o.iter()
                .filter_map(|(k, val)| val.as_str().map(|s| (k.clone(), s.to_string())))
                .collect()
        })
        .unwrap_or_default()
}
/// Overlay `overlay` on `base`: every `base` key the `overlay` doesn't set is
/// kept, and a key present in both takes the `overlay` value. Order is `base`
/// first (in its original order, with overridden values updated in place), then
/// any `overlay`-only keys appended — so the image's env is the foundation and a
/// per-request env can extend or shadow it. Empty `overlay` returns `base` as-is;
/// empty `base` returns `overlay` (so the built-in default fallback still kicks in
/// downstream when both are empty).
fn merge_env(base: &[(String, String)], overlay: &[(String, String)]) -> Vec<(String, String)> {
    if overlay.is_empty() {
        return base.to_vec();
    }
    if base.is_empty() {
        return overlay.to_vec();
    }
    let mut out: Vec<(String, String)> = base.to_vec();
    for (k, val) in overlay {
        if let Some(slot) = out.iter_mut().find(|(ek, _)| ek == k) {
            slot.1 = val.clone();
        } else {
            out.push((k.clone(), val.clone()));
        }
    }
    out
}

impl Sandbox {
    /// Start an [`ExecServer`] for this sandbox's rootfs on `sock_path`: the
    /// no-virt analogue of a VM's exec socket. The product's [`crate::Vm`] points
    /// its exec path at this socket and drives execs/file-ops through it unchanged.
    /// `start_workload` launches the image's baked CMD in the background first.
    pub fn serve_exec(
        &self,
        sock_path: impl Into<std::path::PathBuf>,
        start_workload: bool,
    ) -> std::io::Result<ExecServer> {
        ExecServer::start(
            self.root.clone(),
            self.cfg(),
            sock_path.into(),
            start_workload,
        )
    }
}

// ─── persistent supervisor (Pool) ────────────────────────────────────────────
//
// A long-lived supervisor that sets up the rootfs dirfd + ring table + servicers
// ONCE and forks a fresh cell PER exec request over a control socket — instead of
// `run`/`Sandbox::exec` forking a whole new sandbox tree each time. This amortizes
// the per-exec setup and is the foundation for fork-from-warm (zygote) restore,
// namespace-shared exec, and a shared namespace set. The supervisor stays
// privileged and `process_vm_*`s the cells (which are its descendants, so this
// works under yama too).
//
// First slice: execs are SERIALIZED (one in flight) over a SEQPACKET socket, and
// each is a FRESH cell load (no warm-fork yet); stdio is the supervisor's (the
// library's at `Pool::new` time). Config (limits/egress/mounts/uid) and
// concurrency + warm-fork come next.

/// A control request from the library to the persistent supervisor.
enum Ctrl {
    Exec(Vec<String>),        // 'E' — fork a fresh cell, run argv, reply its exit code
    ExecCapture(Vec<String>), // 'C' — like Exec, but the cell's stdout/stderr go to
    //                                 the passed pipe fd (SCM_RIGHTS) for capture
    ExecFull(Vec<u8>), // 'X' — full exec request (JSON {argv,env,cwd,stage_files,chain});
    //                          stages files, runs argv+chain with per-exec env/cwd,
    //                          combined stdout+stderr to the passed pipe fd. The
    //                          unified-supervisor exec path (full feature parity).
    Warm(Vec<String>),   // 'W' — warm a zygote on argv (to its SENTINEL checkpoint)
    WarmRestore(String), // 'Z' <snapshot-dir> — restore a parked warm zygote from C4 state
    Acquire,             // 'A' — fork-from-warm an instance, reply its exit code
    AcquireCapture,      // 'a' — like Acquire, but the instance's stdout/stderr go to
    //                          the passed pipe fd (SCM_RIGHTS) for capture
    AcquireRunning, // 'R' — fork-from-warm a DETACHED long-lived instance (a warm
    //                          daemon); reply its pid. It survives across execs until
    //                          a matching Release. (L0b)
    Release(i32),     // 'r' <pid> — SIGKILL + reap a detached AcquireRunning instance
    Snapshot(String), // 'S' <dest-dir> — capture the parked warm cell state into dest
    Quit,             // EOF / 'Q'
}
/// Receive one control request (opcode byte + NUL-separated argv), plus an
/// optional passed fd (the capture pipe's write-end, via SCM_RIGHTS) from the
/// library. Uses `recvmsg` so it transparently handles both plain requests and
/// fd-carrying ones.
fn recv_ctrl(fd: c_int) -> (Ctrl, Option<c_int>) {
    let mut buf = vec![0u8; 256 * 1024];
    let mut passed: Option<c_int> = None;
    let n = unsafe {
        let mut iov = libc::iovec {
            iov_base: buf.as_mut_ptr() as *mut c_void,
            iov_len: buf.len(),
        };
        let mut cmsgbuf = [0u8; 64]; // room for one SCM_RIGHTS fd
        let mut msg: libc::msghdr = std::mem::zeroed();
        msg.msg_iov = &mut iov;
        msg.msg_iovlen = 1;
        msg.msg_control = cmsgbuf.as_mut_ptr() as *mut c_void;
        msg.msg_controllen = cmsgbuf.len() as _;
        let n = libc::recvmsg(fd, &mut msg, 0);
        if n > 0 {
            // Extract a passed fd if present.
            let mut cmsg = libc::CMSG_FIRSTHDR(&msg);
            while !cmsg.is_null() {
                if (*cmsg).cmsg_level == libc::SOL_SOCKET && (*cmsg).cmsg_type == libc::SCM_RIGHTS {
                    let mut f: c_int = -1;
                    std::ptr::copy_nonoverlapping(
                        libc::CMSG_DATA(cmsg),
                        &mut f as *mut c_int as *mut u8,
                        std::mem::size_of::<c_int>(),
                    );
                    passed = Some(f);
                }
                cmsg = libc::CMSG_NXTHDR(&msg, cmsg);
            }
        }
        n
    };
    if n <= 0 {
        if let Some(f) = passed {
            unsafe { libc::close(f) };
        }
        return (Ctrl::Quit, None);
    }
    let op = buf[0];
    let argv: Vec<String> = buf[1..n as usize]
        .split(|&b| b == 0)
        .filter(|s| !s.is_empty())
        .map(|s| String::from_utf8_lossy(s).into_owned())
        .collect();
    let ctrl = match op {
        b'X' => Ctrl::ExecFull(buf[1..n as usize].to_vec()),
        b'C' => Ctrl::ExecCapture(argv),
        b'W' => Ctrl::Warm(argv),
        b'Z' => Ctrl::WarmRestore(argv.first().cloned().unwrap_or_default()),
        b'A' => Ctrl::Acquire,
        b'a' => Ctrl::AcquireCapture,
        b'R' => Ctrl::AcquireRunning,
        b'r' => Ctrl::Release(argv.first().and_then(|s| s.parse().ok()).unwrap_or(-1)),
        b'S' => Ctrl::Snapshot(argv.first().cloned().unwrap_or_default()),
        b'Q' => Ctrl::Quit,
        _ => Ctrl::Exec(argv), // 'E'
    };
    (ctrl, passed)
}
/// Reply over the [`Pool`] control socket with the exit code AND the structured
/// [`SentryError`] reason as an 8-byte frame: `[code: i32 LE][err_tag: u32 LE]`.
/// (Widened from the bare 4-byte code so a seccomp-wall kill is distinguishable
/// from a guest that exited the same `128+sig` number; both ends are this file's,
/// so the wire change is internal.)
fn reply_status(fd: c_int, code: i32, err: Option<SentryError>) {
    let mut bytes = [0u8; 8];
    bytes[0..4].copy_from_slice(&code.to_le_bytes());
    bytes[4..8].copy_from_slice(&err_to_tag(err).to_le_bytes());
    unsafe { libc::send(fd, bytes.as_ptr() as *const c_void, 8, libc::MSG_NOSIGNAL) };
}
/// Reply with just an exit code and no structured reason (control acks / errors).
fn reply_code(fd: c_int, code: i32) {
    reply_status(fd, code, None);
}

/// Send a control request (opcode + NUL-separated argv) plus one passed fd
/// (SCM_RIGHTS) over the SEQPACKET control socket. Used by [`Pool::exec_capture`]
/// to hand the supervisor the capture pipe's write-end.
fn send_ctrl_with_fd(sock: c_int, payload: &[u8], passfd: c_int) -> std::io::Result<()> {
    unsafe {
        let mut iov = libc::iovec {
            iov_base: payload.as_ptr() as *mut c_void,
            iov_len: payload.len(),
        };
        let mut cmsgbuf = [0u8; 64];
        let mut msg: libc::msghdr = std::mem::zeroed();
        msg.msg_iov = &mut iov;
        msg.msg_iovlen = 1;
        msg.msg_control = cmsgbuf.as_mut_ptr() as *mut c_void;
        msg.msg_controllen = libc::CMSG_SPACE(std::mem::size_of::<c_int>() as u32) as _;
        let cmsg = libc::CMSG_FIRSTHDR(&msg);
        (*cmsg).cmsg_level = libc::SOL_SOCKET;
        (*cmsg).cmsg_type = libc::SCM_RIGHTS;
        (*cmsg).cmsg_len = libc::CMSG_LEN(std::mem::size_of::<c_int>() as u32) as _;
        std::ptr::copy_nonoverlapping(
            &passfd as *const c_int as *const u8,
            libc::CMSG_DATA(cmsg),
            std::mem::size_of::<c_int>(),
        );
        if libc::sendmsg(sock, &msg, libc::MSG_NOSIGNAL) < 0 {
            return Err(std::io::Error::last_os_error());
        }
    }
    Ok(())
}

/// Signal the parked warm zygote to fork-from-warm one instance (request id
/// `seq`) and block until it reports the instance's exit code via `ZygShared`.
fn zygote_acquire(seq: u32) -> i32 {
    let req = ring_word(unsafe { std::ptr::addr_of_mut!((*ZYG).req) });
    let done = ring_word(unsafe { std::ptr::addr_of_mut!((*ZYG).done) });
    req.store(seq, Ordering::Release);
    unsafe { host(SYS_FUTEX, req.as_ptr() as u64, FUTEX_WAKE, 1, 0, 0, 0) };
    while done.load(Ordering::Acquire) != seq {
        unsafe {
            host(
                SYS_FUTEX,
                done.as_ptr() as u64,
                FUTEX_WAIT,
                (seq - 1) as u64,
                0,
                0,
                0,
            )
        };
    }
    unsafe { (*ZYG).exit }
}

/// A cell-fork request to the single-threaded [`cell_fork_launcher`]: run `argv`
/// (the launcher inherits the pool `root`), optionally as a WARM (driven-zygote)
/// cell. Serialized over the launcher SEQPACKET socket.
#[derive(serde::Serialize, serde::Deserialize)]
struct LaunchReq {
    /// Guest-absolute executable path to load. When absent, defaults to
    /// `argv[0]` for compatibility with older launch requests.
    #[serde(default)]
    path: Option<String>,
    argv: Vec<String>,
    warm: bool,
    /// Per-exec env as `KEY=VALUE` strings (already merged base+request). EMPTY ⇒
    /// keep the launcher's inherited `guest_env` (the sandbox base env). The launcher
    /// is a SEPARATE process, so per-exec env/cwd must travel here, not via a
    /// supervisor global. `#[serde(default)]` for back-compat with plain launches.
    #[serde(default)]
    env: Vec<String>,
    /// Per-exec starting cwd. `None` ⇒ keep the launcher's inherited `guest_cwd_init`.
    #[serde(default)]
    cwd: Option<String>,
    /// C4 restore mode: map a captured `restore.snap`/`mem.blob` and enter the
    /// driven zygote loop instead of loading a fresh ELF.
    #[serde(default)]
    restore_dir: Option<String>,
    /// The guest vpid whose memory/register image should be restored from
    /// `restore_dir`. Defaults to vpid 1 for snapshots produced before
    /// multi-process restore orchestration.
    #[serde(default = "default_restore_vpid")]
    restore_vpid: state_snap::Vpid,
    /// Restore mode only: true means the restored process parks as the warm root
    /// zygote; false means it resumes the captured process directly.
    #[serde(default = "default_restore_warm")]
    restore_warm: bool,
    /// Do not wait/reap this launched process in the launcher. Used for restored
    /// non-root processes and other long-lived children that are owned by the
    /// supervisor control path.
    #[serde(default)]
    detached: bool,
    /// Optional pre-allocated ring slot for this child. Slot 0 remains the default
    /// root/foreground slot; restored non-root processes need their own servicer.
    #[serde(default)]
    slot: Option<u64>,
    /// Restore-live mode: stop after memory/register setup and wait for the
    /// supervisor to restore per-vpid fd/cwd state, then SIGCONT resumes into guest.
    #[serde(default)]
    stop_before_resume: bool,
    /// Launcher protocol gate: stop immediately after fork, before entering guest
    /// code. The supervisor seeds fd/cwd/ring state, then SIGCONT releases it.
    #[serde(default)]
    stop_before_start: bool,
    /// C4 live-capture mode: stop the listed live host processes, dump their
    /// per-vpid memory/register images to `dest_dir/mem.blob` plus
    /// `dest_dir/mem.meta`, then resume them. Mutually exclusive with normal
    /// launch/restore execution.
    #[serde(default)]
    capture: Option<LauncherCaptureReq>,
}

fn default_restore_vpid() -> state_snap::Vpid {
    1
}

fn default_restore_warm() -> bool {
    true
}

#[derive(serde::Serialize, serde::Deserialize)]
struct LauncherCaptureReq {
    dest_dir: String,
    targets: Vec<LauncherCaptureTarget>,
}

#[derive(Clone, serde::Serialize, serde::Deserialize)]
struct LauncherCaptureTarget {
    vpid: state_snap::Vpid,
    host_pid: i32,
}

#[cfg(all(target_os = "linux", target_arch = "x86_64"))]
fn ptrace_attach_stop(pid: i32) -> std::io::Result<()> {
    let r = unsafe {
        libc::ptrace(
            libc::PTRACE_ATTACH,
            pid,
            std::ptr::null_mut::<c_void>(),
            std::ptr::null_mut::<c_void>(),
        )
    };
    if r < 0 {
        let err = std::io::Error::last_os_error();
        if let Ok(status) = std::fs::read_to_string(format!("/proc/{pid}/status")) {
            for line in status.lines() {
                if line.starts_with("Name:")
                    || line.starts_with("State:")
                    || line.starts_with("Tgid:")
                    || line.starts_with("Pid:")
                    || line.starts_with("PPid:")
                    || line.starts_with("TracerPid:")
                    || line.starts_with("Uid:")
                    || line.starts_with("Gid:")
                    || line.starts_with("CoreDumping:")
                    || line.starts_with("NoNewPrivs:")
                    || line.starts_with("Seccomp:")
                {
                    eprintln!("sentry live capture: attach target {pid}: {line}");
                }
            }
        }
        return Err(err);
    }
    let mut st: c_int = 0;
    loop {
        let w = unsafe { libc::waitpid(pid, &mut st, 0) };
        if w < 0 {
            return Err(std::io::Error::last_os_error());
        }
        if w == pid && libc::WIFSTOPPED(st) {
            return Ok(());
        }
    }
}

#[cfg(all(target_os = "linux", target_arch = "x86_64"))]
fn ptrace_detach(pid: i32) {
    unsafe {
        libc::ptrace(
            libc::PTRACE_DETACH,
            pid,
            std::ptr::null_mut::<c_void>(),
            std::ptr::null_mut::<c_void>(),
        );
    }
}

#[cfg(all(target_os = "linux", target_arch = "x86_64"))]
fn capture_ptrace_thread_regs(
    pid: i32,
    vtid: state_snap::Vpid,
) -> std::io::Result<state_snap::ThreadRegs> {
    let mut regs: libc::user_regs_struct = unsafe { std::mem::zeroed() };
    let r = unsafe {
        libc::ptrace(
            libc::PTRACE_GETREGS,
            pid,
            std::ptr::null_mut::<c_void>(),
            &mut regs as *mut _ as *mut c_void,
        )
    };
    if r < 0 {
        return Err(std::io::Error::last_os_error());
    }
    if regs.rip >= WINDOW_FLOOR {
        if let Some(mut thread) = ring_snapshot_regs_for_pid(pid, vtid) {
            if regs.fs_base != 0 {
                thread.fs = regs.fs_base;
            }
            return Ok(thread);
        }
    }
    let mut gregs = [0i64; 18];
    gregs[REG_R8] = regs.r8 as i64;
    gregs[REG_R9] = regs.r9 as i64;
    gregs[REG_R10] = regs.r10 as i64;
    gregs[REG_R11] = regs.r11 as i64;
    gregs[REG_R12] = regs.r12 as i64;
    gregs[REG_R13] = regs.r13 as i64;
    gregs[REG_R14] = regs.r14 as i64;
    gregs[REG_R15] = regs.r15 as i64;
    gregs[REG_RDI] = regs.rdi as i64;
    gregs[REG_RSI] = regs.rsi as i64;
    gregs[REG_RBP] = regs.rbp as i64;
    gregs[REG_RBX] = regs.rbx as i64;
    gregs[REG_RDX] = regs.rdx as i64;
    gregs[REG_RAX] = regs.rax as i64;
    gregs[REG_RCX] = regs.rcx as i64;
    gregs[REG_RSP] = regs.rsp as i64;
    gregs[REG_RIP] = regs.rip as i64;
    Ok(state_snap::ThreadRegs {
        vtid,
        gregs,
        fs: regs.fs_base,
        xsave: Vec::new(),
    })
}

#[cfg(all(target_os = "linux", target_arch = "x86_64"))]
fn launcher_capture_one_proc(
    target: &LauncherCaptureTarget,
    ram: &mut std::fs::File,
) -> std::io::Result<state_snap::ProcMemImage> {
    use std::io::{Seek as _, SeekFrom, Write as _};

    let mut blob_off = ram.seek(SeekFrom::End(0))?;
    let misalign = blob_off % memimage::PAGE;
    if misalign != 0 {
        let pad = vec![0u8; (memimage::PAGE - misalign) as usize];
        ram.write_all(&pad)?;
        blob_off += memimage::PAGE - misalign;
    }

    let maps_path = format!("/proc/{}/maps", target.host_pid);
    let maps = std::fs::read(&maps_path).map_err(|e| {
        eprintln!("sentry live capture: read {maps_path} failed: {e}");
        e
    })?;
    let mem_path = format!("/proc/{}/mem", target.host_pid);
    let mem = std::fs::File::open(&mem_path).map_err(|e| {
        eprintln!("sentry live capture: open {mem_path} failed: {e}");
        e
    })?;
    let guest_ranges = capture_guest_vma_ranges(&mem);
    let entries = select_guest_map_entries(&maps, &guest_ranges);
    let regions = memimage::capture_regions(
        &entries,
        |va, dst| read_exact_process_mem(&mem, va, dst),
        ram,
    )
    .map_err(|e| {
        eprintln!(
            "sentry live capture: memory copy for pid {} vpid {} failed: {e}",
            target.host_pid, target.vpid
        );
        e
    })?;
    let thread = capture_ptrace_thread_regs(target.host_pid, target.vpid).map_err(|e| {
        eprintln!(
            "sentry live capture: ptrace regs for pid {} vpid {} failed: {e}",
            target.host_pid, target.vpid
        );
        e
    })?;
    Ok(state_snap::ProcMemImage {
        vpid: target.vpid,
        blob_off,
        regions,
        threads: vec![thread],
    })
}

#[cfg(all(target_os = "linux", target_arch = "x86_64"))]
fn launcher_capture_mem_images(req: &LauncherCaptureReq) -> std::io::Result<()> {
    use std::io::Write as _;

    if req.targets.is_empty() {
        return Err(std::io::Error::new(
            std::io::ErrorKind::InvalidInput,
            "snapshot: no live capture targets",
        ));
    }
    let dest = std::path::Path::new(&req.dest_dir);
    std::fs::create_dir_all(dest)?;
    let tmp_suffix = format!("{}-{}", std::process::id(), snapshot_tmp_suffix());
    let mem_tmp = dest.join(format!(".mem.blob.live.tmp-{tmp_suffix}"));
    let meta_tmp = dest.join(format!(".mem.meta.tmp-{tmp_suffix}"));

    launcher_quiesce_live_targets(&req.targets)?;

    let mut attached = Vec::new();
    for target in &req.targets {
        if let Err(e) = ptrace_attach_stop(target.host_pid) {
            for pid in attached {
                ptrace_detach(pid);
            }
            return Err(e);
        }
        attached.push(target.host_pid);
    }

    let result = (|| {
        let mut ram = std::fs::File::create(&mem_tmp)?;
        let mut procs = Vec::with_capacity(req.targets.len());
        for target in &req.targets {
            procs.push(launcher_capture_one_proc(target, &mut ram)?);
        }
        ram.flush()?;

        let first = procs.first().cloned().ok_or_else(|| {
            std::io::Error::new(std::io::ErrorKind::InvalidData, "snapshot: empty mem image")
        })?;
        let mem = state_snap::MemImage {
            regions: first.regions.clone(),
            threads: first.threads.clone(),
            procs,
        };
        let mut meta = Vec::new();
        mem.write_to(&mut meta)?;
        std::fs::write(&meta_tmp, meta)?;
        std::fs::rename(&mem_tmp, dest.join("mem.blob"))?;
        std::fs::rename(&meta_tmp, dest.join("mem.meta"))?;
        Ok(())
    })();

    for pid in attached {
        ptrace_detach(pid);
    }
    if result.is_err() {
        let _ = std::fs::remove_file(&mem_tmp);
        let _ = std::fs::remove_file(&meta_tmp);
    }
    result
}

/// THE single-threaded cell-fork launcher (fork-in-MT fix). Forked from
/// [`persistent_supervisor_main`] AFTER `setup_sandbox_env` (so it inherits the
/// shared ring) but BEFORE any servicer thread — so it is SINGLE-THREADED and its
/// `fork()`s are clean. Loops: recv a [`LaunchReq`], fork the cell, reply its pid,
/// and (for a non-warm exec cell) `wait4` + relay the raw wait status. The cells it
/// forks share the ring, so this supervisor's servicers service them exactly as if
/// the supervisor had forked them — only the unsafe-in-MT `fork()`+`cell_main`
/// (guest load) moves to this pristine context, ending the guest-stack corruption
/// (the residual "stack smashing"/`reentrant_under_load`). On request-socket EOF
/// (supervisor gone) we exit; any orphaned children reparent to init.
fn cell_fork_launcher(req_fd: c_int, root_owned: Option<std::path::PathBuf>) -> ! {
    loop {
        let mut buf = vec![0u8; 1 << 16];
        let n = unsafe { libc::recv(req_fd, buf.as_mut_ptr() as *mut c_void, buf.len(), 0) };
        if n <= 0 {
            launcher_kill_owned_processes_on_eof();
            unsafe { libc::_exit(0) }; // supervisor closed the channel
        }
        let req: LaunchReq = match serde_json::from_slice::<LaunchReq>(&buf[..n as usize]) {
            Ok(r) if !r.argv.is_empty() => r,
            _ => {
                let pid: i32 = -22; // -EINVAL
                unsafe { libc::send(req_fd, &pid as *const i32 as *const c_void, 4, 0) };
                continue;
            }
        };
        if let Some(capture) = req.capture {
            let code = match launcher_capture_mem_images(&capture) {
                Ok(()) => 0,
                Err(e) => -e.raw_os_error().unwrap_or(libc::EIO),
            };
            unsafe { libc::send(req_fd, &code as *const i32 as *const c_void, 4, 0) };
            continue;
        }
        let path = req.path.as_deref().unwrap_or(&req.argv[0]);
        set_guest_cmdline(&req.argv);
        // Per-exec env/cwd (the unified exec path): apply BEFORE the fork so the cell
        // inherits them via CoW. Empty/None preserves the launcher's inherited base
        // (set once in setup_sandbox_env) — so plain Exec/Warm launches are unchanged.
        if !req.env.is_empty() {
            *guest_env().lock().unwrap() = req.env.iter().map(|s| s.as_bytes().to_vec()).collect();
        }
        if let Some(cwd) = &req.cwd {
            *guest_cwd_init().lock().unwrap() = Some(cwd.as_bytes().to_vec());
        }
        if req.warm {
            WARM_MODE.store(true, Ordering::Relaxed);
            ZYG_DRIVEN.store(true, Ordering::Relaxed);
            OWN_LOOPBACK.store(true, Ordering::Relaxed);
        }
        let pid = unsafe { libc::fork() };
        if pid == 0 {
            unsafe { libc::close(req_fd) };
            if let Some(slot) = req.slot {
                set_slot(slot);
                unsafe {
                    let me = host(SYS_GETPID, 0, 0, 0, 0, 0, 0) as i32;
                    let tid = host(SYS_GETTID, 0, 0, 0, 0, 0, 0) as i32;
                    set_ring_owner(slot, me, tid);
                }
            }
            // Own process group (matches spawn_sandbox's `new_group`): the supervisor
            // tears down a cell tree with `killpg(-pid)` (exec timeout / client close /
            // straggler sweep). Without this the launcher-forked cell stays in the
            // launcher's group, so `kill(-pid)` misses it → a dropped/timed-out exec
            // leaks (and clobbers ring slot 0 for the next exec — vm_stop_during_exec).
            unsafe { libc::setpgid(0, 0) };
            if req.stop_before_start {
                unsafe {
                    let me = host(SYS_GETPID, 0, 0, 0, 0, 0, 0);
                    host(SYS_KILL, me as u64, libc::SIGSTOP as u64, 0, 0, 0, 0);
                }
            }
            if let Some(dir) = req.restore_dir.as_deref() {
                cell_restore_main(
                    dir,
                    req.restore_vpid,
                    req.restore_warm,
                    req.stop_before_resume,
                    root_owned.clone(),
                ); // never returns
            } else {
                cell_main(path, &req.argv, root_owned.clone()); // never returns
            }
        }
        if req.warm {
            WARM_MODE.store(false, Ordering::Relaxed); // the child kept `true`
            ZYG_DRIVEN.store(false, Ordering::Relaxed);
        }
        unsafe { libc::send(req_fd, &pid as *const i32 as *const c_void, 4, 0) };
        // A non-warm exec cell: we are its parent, so wait4 + relay the status. The
        // supervisor services it via the ring and enforces the timeout (it can kill
        // it cross-process; our wait4 then returns). A WARM cell is long-lived (it
        // parks + forks its own instances) — don't reap it here.
        if !req.warm && !req.detached && pid > 0 {
            let mut st: c_int = 0;
            unsafe { libc::waitpid(pid, &mut st, 0) };
            unsafe { libc::send(req_fd, &st as *const i32 as *const c_void, 4, 0) };
        }
    }
}

/// Supervisor side: ask the launcher to fork a cell for `argv`; return its pid
/// (`< 0` on failure).
fn launch_cell(launch_fd: c_int, argv: &[String], warm: bool) -> i32 {
    launch_cell_env(launch_fd, None, argv, warm, &[], None)
}

/// As [`launch_cell`] but with a per-exec env (`KEY=VALUE` strings) and starting
/// cwd carried in the [`LaunchReq`] (the launcher applies them pre-fork). Empty
/// env / `None` cwd is identical to [`launch_cell`].
fn launch_cell_env(
    launch_fd: c_int,
    path: Option<&str>,
    argv: &[String],
    warm: bool,
    env: &[String],
    cwd: Option<&str>,
) -> i32 {
    let req = LaunchReq {
        path: path.map(|s| s.to_string()),
        argv: argv.to_vec(),
        warm,
        env: env.to_vec(),
        cwd: cwd.map(|s| s.to_string()),
        restore_dir: None,
        restore_vpid: 1,
        restore_warm: true,
        detached: false,
        slot: None,
        stop_before_resume: false,
        stop_before_start: true,
        capture: None,
    };
    let json = match serde_json::to_vec(&req) {
        Ok(j) => j,
        Err(_) => return -1,
    };
    unsafe { libc::send(launch_fd, json.as_ptr() as *const c_void, json.len(), 0) };
    let mut pid: i32 = -1;
    let n = unsafe { libc::recv(launch_fd, &mut pid as *mut i32 as *mut c_void, 4, 0) };
    if n != 4 {
        return -1;
    }
    // Record the launched program's argv/exe as its `/proc/<pid>/{cmdline,exe}`:
    // a launched cell runs `argv` via `load_elf` (not a real execve), so no
    // CTL_SET_CMDLINE/CTL_SET_EXE fires for the INITIAL program — only for
    // subsequent in-cell execs. Without these seeds, top-level workloads have an
    // empty cmdline and direct execs such as `rustc` can't resolve
    // `/proc/self/exe` for `current_exe()`.
    if pid > 0 && !argv.is_empty() {
        let mut joined: Vec<u8> = Vec::with_capacity(256);
        for a in argv {
            joined.extend_from_slice(a.as_bytes());
            joined.push(0);
            if joined.len() >= 4096 {
                break;
            }
        }
        proc_cmdline().lock().unwrap().insert(pid, joined);
        proc_exe()
            .lock()
            .unwrap()
            .insert(pid, path.unwrap_or(&argv[0]).as_bytes().to_vec());
    }
    pid
}

fn launch_restore_cell_vpid(
    launch_fd: c_int,
    snapshot_dir: &std::path::Path,
    restore_vpid: state_snap::Vpid,
    restore_warm: bool,
    detached: bool,
    slot: Option<u64>,
    stop_before_resume: bool,
) -> i32 {
    let req = LaunchReq {
        path: Some("/.supermachine/restore".to_string()),
        argv: vec!["/.supermachine/restore".to_string()],
        warm: restore_warm,
        env: Vec::new(),
        cwd: None,
        restore_dir: Some(snapshot_dir.to_string_lossy().into_owned()),
        restore_vpid,
        restore_warm,
        detached,
        slot,
        stop_before_resume,
        stop_before_start: true,
        capture: None,
    };
    let json = match serde_json::to_vec(&req) {
        Ok(j) => j,
        Err(_) => return -1,
    };
    unsafe { libc::send(launch_fd, json.as_ptr() as *const c_void, json.len(), 0) };
    let mut pid: i32 = -1;
    let n = unsafe { libc::recv(launch_fd, &mut pid as *mut i32 as *mut c_void, 4, 0) };
    if n != 4 {
        return -1;
    }
    if pid > 0 {
        proc_cmdline()
            .lock()
            .unwrap()
            .insert(pid, b"/.supermachine/restore\0".to_vec());
        proc_exe()
            .lock()
            .unwrap()
            .insert(pid, b"/.supermachine/restore".to_vec());
    }
    pid
}

fn launch_restore_cell(launch_fd: c_int, snapshot_dir: &std::path::Path) -> i32 {
    launch_restore_cell_vpid(launch_fd, snapshot_dir, 1, true, false, None, false)
}

#[cfg(all(target_os = "linux", target_arch = "x86_64"))]
fn continue_launched_cell(pid: i32) {
    if pid > 0 {
        // The launched cell self-SIGSTOPs (stop_before_start) ASYNCHRONOUSLY after
        // fork (fork → setpgid → raise(SIGSTOP)). The supervisor is NOT its parent
        // (the single-threaded launcher is), so it can't waitpid the stop — and a
        // SIGCONT delivered BEFORE the cell's SIGSTOP lands is a no-op, after which the
        // cell stops forever. That race wedged every bake RUN-step (the supervisor then
        // blocks unbounded in await_launched_exit). Wait (cross-process /proc poll) for
        // the cell to actually be stopped, THEN resume it. Returns within ~1ms once the
        // self-stop lands; the bound only guards a cell that never stops.
        wait_pid_stopped(pid, 5_000);
        unsafe {
            libc::kill(pid, libc::SIGCONT);
        }
    }
}

#[cfg(all(target_os = "linux", target_arch = "x86_64"))]
fn wait_pid_stopped(pid: i32, timeout_ms: u64) -> bool {
    let deadline = now_ms().saturating_add(timeout_ms as i64);
    loop {
        if let Ok(status) = std::fs::read_to_string(format!("/proc/{pid}/status")) {
            if status
                .lines()
                .any(|line| line.starts_with("State:") && line.contains("stopped"))
            {
                return true;
            }
        }
        if now_ms() >= deadline {
            return false;
        }
        let ts = [0i64, 1_000_000]; // 1ms
        unsafe {
            host(SYS_NANOSLEEP, ts.as_ptr() as u64, 0, 0, 0, 0, 0);
        }
    }
}

/// Supervisor side: await a launched (non-warm) cell's exit, bounded by
/// `timeout_ms` (0 = unlimited). On timeout, SIGKILL the cell tree (cross-process —
/// the launcher is its parent and `wait4`s it) and report [`SentryError::Timeout`].
/// Mirrors [`wait_exit_deadline`] for the launcher path; the launcher relays the
/// raw wait status, which we classify (cgroup cleanup is ours).
fn await_launched_exit(launch_fd: c_int, pid: i32, timeout_ms: u64) -> (i32, Option<SentryError>) {
    let deadline = if timeout_ms > 0 {
        now_ms().saturating_add(timeout_ms as i64)
    } else {
        i64::MAX
    };
    loop {
        let remaining: i64 = if timeout_ms > 0 {
            (deadline - now_ms()).max(0)
        } else {
            -1
        };
        let mut pf = pollfd_bytes(launch_fd, POLLIN_BIT);
        let r = unsafe {
            host(
                SYS_POLL,
                pf.as_mut_ptr() as u64,
                1,
                remaining as u64,
                0,
                0,
                0,
            )
        };
        if r > 0 {
            let mut st: c_int = 0;
            let n = unsafe { libc::recv(launch_fd, &mut st as *mut c_int as *mut c_void, 4, 0) };
            if n != 4 {
                return (-1, None);
            }
            remove_cgroup(pid);
            return classify_status(st);
        }
        if r == 0 && timeout_ms > 0 {
            unsafe {
                if libc::getpgid(pid) == pid {
                    libc::kill(-pid, libc::SIGKILL);
                } else {
                    libc::kill(pid, libc::SIGKILL);
                }
            }
            let mut st: c_int = 0;
            let _ = unsafe { libc::recv(launch_fd, &mut st as *mut c_int as *mut c_void, 4, 0) };
            remove_cgroup(pid);
            return (128 + libc::SIGKILL, Some(SentryError::Timeout));
        }
        // r < 0 (EINTR) — re-poll the remaining budget.
    }
}

/// Service a full exec request (`Ctrl::ExecFull`) inside the persistent supervisor:
/// stage files into the confined rootfs, then run `argv` (and each `chain` argv,
/// while the previous exited 0) as fresh cells forked in the single-threaded
/// launcher, with the per-exec merged env + cwd, combined stdout+stderr to `cap`.
/// Returns the last command's exit code. Takes ownership of `cap` (closes it when
/// every write-end is gone so the library's reader EOFs). Mirrors the ExecServer's
/// `handle_request`/`run_one` feature set (minus tty/streaming/separate-stderr,
/// which the daemon-client request/response model doesn't carry yet).
#[cfg(all(target_os = "linux", target_arch = "x86_64"))]
fn run_exec_full(
    launch_fd: c_int,
    root: Option<&std::path::Path>,
    base_cfg: &SandboxCfg,
    body: &[u8],
    cap: c_int,
    warm_pid: i32,
) -> i32 {
    let close = |cap: c_int| unsafe { libc::close(cap) };
    let v: serde_json::Value = match serde_json::from_slice(body) {
        Ok(v) => v,
        Err(_) => {
            close(cap);
            return 127;
        }
    };
    // stage_files: write each into the confined rootfs BEFORE any cell runs.
    if let Some(root) = root {
        if let Some(sf) = v.get("stage_files").and_then(|s| s.as_array()) {
            for f in sf {
                let path = f.get("path").and_then(|p| p.as_str()).unwrap_or("");
                let data = f.get("data_b64").and_then(|d| d.as_str()).unwrap_or("");
                let mode = f.get("mode").and_then(|m| m.as_u64());
                let bytes = match crate::api::b64_decode(data) {
                    Ok(b) => b,
                    Err(_) => {
                        close(cap);
                        return 127;
                    }
                };
                if write_in_root(root, path, &bytes, mode).is_err() {
                    close(cap);
                    return 127;
                }
            }
        }
    }
    // Per-exec env (merge base + request) as KEY=VALUE strings, and starting cwd.
    let req_env = json_env(v.get("env"));
    let merged = merge_env(&base_cfg.env, &req_env);
    let env_kv: Vec<String> = merged.iter().map(|(k, val)| format!("{k}={val}")).collect();
    let cwd = v.get("cwd").and_then(|c| c.as_str()).map(|s| s.to_string());
    // argv + chain run sequentially; stop at the first non-zero exit.
    let mut argvs: Vec<Vec<String>> = vec![json_str_vec(v.get("argv"))];
    for c in v
        .get("chain")
        .and_then(|c| c.as_array())
        .into_iter()
        .flatten()
    {
        argvs.push(json_str_vec(Some(c)));
    }
    let mut last = 0i32;
    for av in &argvs {
        if av.is_empty() {
            continue;
        }
        // Resolve argv[0] to a concrete rootfs-confined path via PATH (bare names
        // like `sh`/`node`), exactly as the ExecServer's `run_one` does — else
        // `load_elf` can't find a bare-name exe ("cannot read guest exe", 127).
        let argv = av.clone();
        let path = if let Some(root) = root {
            match resolve_in_root_path(root, &path_from_env(&merged), &argv[0]) {
                Some(p) => Some(canonical_exe_in_root(root, &p).unwrap_or(p)),
                None => {
                    last = 127; // command not found
                    break;
                }
            }
        } else {
            None
        };
        let pid = launch_cell_env(
            launch_fd,
            path.as_deref(),
            &argv,
            false,
            &env_kv,
            cwd.as_deref(),
        );
        if pid <= 0 {
            last = 127;
            break;
        }
        // Seed the SUPERVISOR's per-pid cwd: the launcher set guest_cwd_init so the
        // cell resolves relative opens correctly, but getcwd/AT_FDCWD are serviced by
        // THIS supervisor against its own `cwds()` map — the cell-side seed lands in
        // the cell's private copy the supervisor never reads (cf. sandbox_main).
        if let Some(c) = &cwd {
            cwds().lock().unwrap().insert(pid, c.as_bytes().to_vec());
        }
        seed_cell_fds_capture(pid, cap); // dups cap into the cell's fd 1/2
        unsafe { set_ring_owner(0, pid, pid) };
        continue_launched_cell(pid);
        let (c, _e) = await_launched_exit(launch_fd, pid, base_cfg.exec_timeout_ms);
        shm_reap_pid(pid);
        fd_drop(pid);
        free_slots_of(pid);
        // Reclaim or preserve stragglers this command orphaned. Normal execs
        // sweep them to guarantee capture-pipe EOF; builder warmup preserves
        // them so the following C4 snapshot includes live daemons/listeners.
        if base_cfg.preserve_stragglers {
            preserve_stragglers_as_running(if warm_pid > 0 { warm_pid } else { 0 });
        } else {
            sweep_stragglers(if warm_pid > 0 { warm_pid } else { 0 });
        }
        if warm_pid > 0 {
            unsafe { set_ring_owner(0, warm_pid, warm_pid) };
        }
        last = c;
        if last != 0 {
            break;
        }
    }
    close(cap); // the last write-end → the library's drain EOFs, then reads status
    last
}

/// The persistent supervisor: set up the environment once, spawn servicer 0, then
/// service control requests — fork a fresh cell per `Exec`, or warm a zygote
/// (`Warm`) and fork-from-warm instances (`Acquire`).
fn persistent_supervisor_main(
    ctrl_fd: c_int,
    root: Option<&std::path::Path>,
    cfg: &SandboxCfg,
    exec_sock: Option<std::path::PathBuf>,
) -> ! {
    setup_sandbox_env(root, cfg);
    install_child_subreaper(b"persistent");
    // A unified workload+exec supervisor exposes host ingress through EW_BRIDGE,
    // which connects into this supervisor's owned LoopNet. Enable the owned plane
    // before forking the launcher so every normal in-process exec cell and the
    // background workload inherit the same loopback.
    if exec_sock.is_some() {
        OWN_LOOPBACK.store(true, Ordering::Relaxed);
    }
    let root_owned = root.map(|r| r.to_path_buf());
    // ── Single-threaded CELL-FORK LAUNCHER (fork-in-MT fix) ──────────────────
    // Fork the launcher NOW — after setup_sandbox_env (it inherits the shared ring)
    // but BEFORE any servicer thread spawns (so it's single-threaded → clean cell
    // forks) AND BEFORE drop_supervisor_caps/harden_supervisor. The order matters:
    // `sandbox_main` (the one-shot/exec_capture path, which is corruption-free) forks
    // its cell BEFORE supervisor_main hardens, so its cells run with FULL caps and NO
    // inherited harden seccomp. Forking the launcher after harden gave POOL cells a
    // DIFFERENT environment (dropped caps + inherited harden filter) than exec_capture
    // cells — the difference behind the warm/pool guest corruption. Forking here makes
    // the launcher (and every cell it forks) match the clean exec_capture model. The
    // launcher is trusted + minimal (recv → fork → relay); the SUPERVISOR still hardens
    // below, and each cell still seals itself (SUD + install_wall).
    let mut lsv = [0 as c_int; 2];
    if unsafe { libc::socketpair(libc::AF_UNIX, libc::SOCK_SEQPACKET, 0, lsv.as_mut_ptr()) } < 0 {
        die(b"sentry: launcher socketpair failed\n");
    }
    let (launch_fd, launch_child_fd) = (lsv[0], lsv[1]);
    let lpid = unsafe { libc::fork() };
    if lpid == 0 {
        unsafe { libc::close(launch_fd) };
        cell_fork_launcher(launch_child_fd, root_owned.clone()); // never returns
    }
    unsafe { libc::close(launch_child_fd) };
    // Self-harden the trusted SUPERVISOR now (the launcher + cells were forked above
    // with full caps, matching sandbox_main): drop unneeded capabilities, then deny
    // escape primitives. Servicer threads spawned below inherit both.
    drop_supervisor_caps();
    harden_supervisor();
    // servicer 0 is a dedicated thread here (the main thread runs the control
    // loop, unlike the per-exec supervisor where the main thread IS servicer 0).
    let _ = std::thread::Builder::new().spawn(|| servicer_loop(0));
    // Pre-spawn the bounded DELEG-pool servicers (Part A) — see supervisor_main.
    spawn_deleg_servicers();
    // Deadlock-breaking fd-leak sweeper (see supervisor_main / fd_leak_sweeper): the
    // pool foreground cell lives in slot 0 (never swept; the loop starts at slot 1),
    // so only its forked children are reaped when /proc says they're dead. Harmless
    // alongside sweep_stragglers (which runs between commands).
    let _ = std::thread::Builder::new().spawn(fd_leak_sweeper);
    let _ = std::thread::Builder::new().spawn(move || adopted_orphan_reaper(lpid));
    // DIAGNOSTIC: hang dump (SENTRY_HANGDUMP=<delay_s> env, OR a /tmp/sentry_hangdump
    // file whose contents are the delay) — the POOL path lands HERE, not supervisor_main,
    // so the env check there never fires for warm-restored VMs. The file marker is read
    // via raw host syscalls (wall-safe, env-propagation-proof through the reexec).
    {
        let mut delay: Option<u64> = std::env::var("SENTRY_HANGDUMP")
            .ok()
            .map(|v| v.trim().parse().unwrap_or(8));
        if delay.is_none() {
            let mut buf = [0u8; 16];
            let fd = unsafe {
                host(
                    SYS_OPENAT,
                    (-100i64) as u64,
                    b"/tmp/sentry_hangdump\0".as_ptr() as u64,
                    libc::O_RDONLY as u64,
                    0,
                    0,
                    0,
                )
            };
            if fd >= 0 {
                let n = unsafe {
                    host(
                        SYS_READ,
                        fd as u64,
                        buf.as_mut_ptr() as u64,
                        buf.len() as u64,
                        0,
                        0,
                        0,
                    )
                };
                unsafe { host(SYS_CLOSE, fd as u64, 0, 0, 0, 0, 0) };
                delay = Some(if n > 0 {
                    std::str::from_utf8(&buf[..n as usize])
                        .ok()
                        .and_then(|s| s.trim().parse().ok())
                        .unwrap_or(8)
                } else {
                    8
                });
            }
        }
        ipc_logf(
            &[(
                b"HANGDUMP-GATE delay=",
                delay.map(|d| d as i64).unwrap_or(-1),
            )],
            b"",
        );
        if let Some(d) = delay {
            let _ = std::thread::Builder::new().spawn(move || hang_dump_watchdog(d));
        }
    }

    // Unified workload+exec backend: when an exec socket was requested, bind it and
    // serve the FULL streaming exec protocol (handle_conn → handle_request/-control)
    // with cells forked IN-PROCESS via the launcher, so they share this supervisor's
    // loop_state (owned loopback) + proctree with the backgrounded workload. Accept
    // connections concurrently so a long-running workload exec does not block
    // EW_BRIDGE ingress; launcher access itself stays serialized by `launch_mu`.
    if let Some(sock_path) = exec_sock {
        use std::os::unix::net::UnixListener;
        let _ = std::fs::remove_file(&sock_path);
        match UnixListener::bind(&sock_path) {
            Ok(listener) => {
                let root_owned2 = root_owned.clone();
                let cfg2 = cfg.clone();
                let launch_mu = std::sync::Arc::new(Mutex::new(launch_fd));
                let _ = std::thread::Builder::new()
                    .name("sentry-exec-accept-inproc".into())
                    .spawn(move || {
                        let workload: std::sync::Arc<Mutex<Option<Child>>> =
                            std::sync::Arc::new(Mutex::new(None));
                        for conn in listener.incoming() {
                            match conn {
                                Ok(stream) => {
                                    let root_for_conn =
                                        root_owned2.clone().unwrap_or_else(|| "/".into());
                                    let cfg_for_conn = cfg2.clone();
                                    let workload_for_conn = workload.clone();
                                    let launch_for_conn = launch_mu.clone();
                                    let _ = std::thread::Builder::new()
                                        .name("sentry-exec-conn-inproc".into())
                                        .spawn(move || {
                                            let _ = handle_conn(
                                                stream,
                                                &root_for_conn,
                                                &cfg_for_conn,
                                                &workload_for_conn,
                                                Some(launch_for_conn),
                                            );
                                        });
                                }
                                Err(_) => break,
                            }
                        }
                    });
            }
            Err(_) => die(b"sentry: unified exec socket bind failed\n"),
        }
    }

    let mut warm_pid: i32 = 0; // the parked zygote, once warmed
    let mut pending_live_checkout: i32 = 0; // restored LiveTree root waiting for acquire
    let mut acq_seq: u32 = 0; // monotonic acquire request id
    loop {
        let (ctrl, cap_fd) = recv_ctrl(ctrl_fd);
        match ctrl {
            Ctrl::Quit => unsafe {
                if let Some(f) = cap_fd {
                    libc::close(f);
                }
                if warm_pid > 0 {
                    libc::kill(warm_pid, libc::SIGKILL);
                }
                if pending_live_checkout > 0 {
                    libc::kill(pending_live_checkout, libc::SIGKILL);
                }
                for pid in running_instances().lock().unwrap().drain(..) {
                    if pid > 1 {
                        libc::kill(pid, libc::SIGKILL);
                    }
                }
                libc::_exit(0);
            },
            Ctrl::Exec(argv) if !argv.is_empty() => {
                // Fork the cell in the SINGLE-THREADED launcher (fork-in-MT fix); we
                // still service it via the shared ring + classify its exit. The
                // launcher set_guest_cmdline's before forking.
                let pid = launch_cell(launch_fd, &argv, false);
                let (code, err) = if pid > 0 {
                    seed_cell_fds(pid);
                    unsafe { set_ring_owner(0, pid, pid) };
                    continue_launched_cell(pid);
                    let (c, e) = await_launched_exit(launch_fd, pid, cfg.exec_timeout_ms);
                    shm_reap_pid(pid);
                    fd_drop(pid);
                    free_slots_of(pid);
                    // Part D: SIGKILL + reclaim any straggler this command orphaned
                    // (e.g. `( sleep & )`) before we reply, so its servicer stops
                    // holding any pipe/fd and a future exec's slots aren't leaked.
                    if cfg.preserve_stragglers {
                        preserve_stragglers_as_running(0);
                    } else {
                        sweep_stragglers(0);
                    }
                    // Restore slot 0 to the warm zygote (warm-daemon pool): this exec
                    // overwrote ring[0].pid with the now-dead client, but the parked
                    // warm cell delegates CTL_FORK_TABLE via slot 0 — so a LATER acquire
                    // would fd_snapshot the dead client's (empty) table and the
                    // fork-from-warm instance would inherit no fds (no listener, no
                    // epoll → its serve loop spins on a never-draining loop efd).
                    if warm_pid > 0 {
                        unsafe { set_ring_owner(0, warm_pid, warm_pid) };
                    }
                    (c, e)
                } else {
                    (-1, None)
                };
                reply_status(ctrl_fd, code, err);
            }
            Ctrl::ExecCapture(argv) if !argv.is_empty() && cap_fd.is_some() => {
                // Like Exec, but the cell's stdout/stderr go to the passed pipe. The
                // cell is forked in the SINGLE-THREADED launcher (fork-in-MT fix); it
                // never holds `cap` (output is delegated + serviced onto the fd-table
                // dups), so there's no stray write-end to close in the cell.
                let cap = cap_fd.unwrap();
                let pid = launch_cell(launch_fd, &argv, false);
                let (code, err) = if pid > 0 {
                    seed_cell_fds_capture(pid, cap);
                    unsafe { set_ring_owner(0, pid, pid) }; // drop our cap copy; pipe EOFs
                    continue_launched_cell(pid);
                    unsafe { libc::close(cap) }; //          when the fd-table dups close
                    let (c, e) = await_launched_exit(launch_fd, pid, cfg.exec_timeout_ms);
                    shm_reap_pid(pid);
                    fd_drop(pid);
                    free_slots_of(pid);
                    // Part D — THE pool-path EOF fix: SIGKILL + reclaim any orphaned
                    // straggler (`( sleep & )`) whose parked servicer still holds the
                    // capture pipe's write-end, so the library's reader hits EOF and
                    // doesn't hang after this command returns.
                    if cfg.preserve_stragglers {
                        preserve_stragglers_as_running(0);
                    } else {
                        sweep_stragglers(0);
                    }
                    // Restore slot 0 to the warm zygote (warm-daemon pool): this exec
                    // overwrote ring[0].pid with the now-dead client, but the parked
                    // warm cell delegates CTL_FORK_TABLE via slot 0 — so a LATER acquire
                    // would fd_snapshot the dead client's (empty) table and the
                    // fork-from-warm instance would inherit no fds (no listener, no
                    // epoll → its serve loop spins on a never-draining loop efd).
                    if warm_pid > 0 {
                        unsafe { set_ring_owner(0, warm_pid, warm_pid) };
                    }
                    (c, e)
                } else {
                    unsafe { libc::close(cap) };
                    (-1, None)
                };
                reply_status(ctrl_fd, code, err);
            }
            Ctrl::ExecFull(body) if cap_fd.is_some() => {
                // Unified exec: full request (stage_files + per-exec env/cwd + chain),
                // each command a fresh cell forked in the SINGLE-THREADED launcher,
                // combined stdout+stderr to the passed pipe. Feature parity with the
                // ExecServer's `handle_request`, but IN this supervisor so the cells
                // share its loop_state + proctree (workload-contract).
                let cap = cap_fd.unwrap();
                let code =
                    run_exec_full(launch_fd, root_owned.as_deref(), cfg, &body, cap, warm_pid);
                reply_status(ctrl_fd, code, None);
            }
            Ctrl::WarmRestore(snapshot_dir) if !snapshot_dir.is_empty() => {
                OWN_LOOPBACK.store(true, Ordering::Relaxed);
                let snapshot_dir = std::path::PathBuf::from(snapshot_dir);
                let state = match read_state_snapshot_dir(&snapshot_dir) {
                    Ok(s) => s,
                    Err(e) => {
                        eprintln!(
                            "sentry snapshot restore failed reading {}: {e}",
                            snapshot_dir.display()
                        );
                        reply_code(ctrl_fd, -e.raw_os_error().unwrap_or(libc::EIO));
                        continue;
                    }
                };
                let mut bindings: Vec<(state_snap::Vpid, i32)> = Vec::new();
                let mut restored_live: Vec<i32> = Vec::new();
                let mut restored_live_root: i32 = 0;
                let mut restore_err: Option<std::io::Error> = None;
                let live_tree = state.entry_kind == state_snap::SnapshotEntryKind::LiveTree;

                let mut procs = state.proctree.procs.clone();
                if procs.is_empty() {
                    procs.push(state_snap::GuestProcRecord {
                        vpid: 1,
                        parent_vpid: 0,
                        pgid: 0,
                        sid: 0,
                        threads: Vec::new(),
                        state: state_snap::ProcState::Running,
                    });
                }
                procs.sort_by_key(|p| (p.vpid != 1, p.parent_vpid, p.vpid));

                for rec in &procs {
                    if rec.vpid == 1 {
                        if live_tree {
                            let slot = alloc_slot();
                            let slot_u64 = slot as u64;
                            if slot as usize >= MAX_SLOTS || !ensure_servicer(slot_u64) {
                                restore_err = Some(std::io::Error::new(
                                    std::io::ErrorKind::Other,
                                    "snapshot: failed to allocate ring slot for restored root process",
                                ));
                                break;
                            }
                            let pid = launch_restore_cell_vpid(
                                launch_fd,
                                &snapshot_dir,
                                rec.vpid,
                                false,
                                true,
                                Some(slot_u64),
                                true,
                            );
                            if pid <= 0 {
                                free_slot(slot as u32);
                                restore_err = Some(std::io::Error::new(
                                    std::io::ErrorKind::Other,
                                    "snapshot: failed to launch restored root process",
                                ));
                                break;
                            }
                            unsafe { set_ring_owner(slot_u64, pid, pid) };
                            continue_launched_cell(pid);
                            if !wait_pid_stopped(pid, 5_000) {
                                restore_err = Some(std::io::Error::new(
                                    std::io::ErrorKind::TimedOut,
                                    "snapshot: restored root did not stop before resume",
                                ));
                                break;
                            }
                            bindings.push((1, pid));
                            restored_live.push(pid);
                            restored_live_root = pid;
                        } else {
                            let pid = launch_restore_cell(launch_fd, &snapshot_dir);
                            if pid <= 0 {
                                restore_err = Some(std::io::Error::new(
                                    std::io::ErrorKind::Other,
                                    "snapshot: failed to launch restored root process",
                                ));
                                break;
                            }
                            seed_cell_fds(pid);
                            unsafe { set_ring_owner(0, pid, pid) };
                            continue_launched_cell(pid);
                            bindings.push((1, pid));
                            warm_pid = pid;
                        }
                    } else {
                        let slot = alloc_slot();
                        let slot_u64 = slot as u64;
                        if slot as usize >= MAX_SLOTS || !ensure_servicer(slot_u64) {
                            restore_err = Some(std::io::Error::new(
                                std::io::ErrorKind::Other,
                                "snapshot: failed to allocate ring slot for restored process",
                            ));
                            break;
                        }
                        let pid = launch_restore_cell_vpid(
                            launch_fd,
                            &snapshot_dir,
                            rec.vpid,
                            false,
                            true,
                            Some(slot_u64),
                            true,
                        );
                        if pid <= 0 {
                            free_slot(slot as u32);
                            restore_err = Some(std::io::Error::new(
                                std::io::ErrorKind::Other,
                                "snapshot: failed to launch restored child process",
                            ));
                            break;
                        }
                        unsafe { set_ring_owner(slot_u64, pid, pid) };
                        continue_launched_cell(pid);
                        if !wait_pid_stopped(pid, 5_000) {
                            restore_err = Some(std::io::Error::new(
                                std::io::ErrorKind::TimedOut,
                                "snapshot: restored child did not stop before resume",
                            ));
                            break;
                        }
                        bindings.push((rec.vpid, pid));
                        restored_live.push(pid);
                    }
                }

                if restore_err.is_none() {
                    proctree::restore_bindings(&state.proctree, &bindings);
                    for (idx, (vpid, pid)) in bindings.iter().copied().enumerate() {
                        if let Err(e) =
                            restore_supervisor_state_for_vpid(pid, vpid, &state, idx == 0)
                        {
                            restore_err = Some(e);
                            break;
                        }
                    }
                }

                if let Some(e) = restore_err {
                    eprintln!("sentry snapshot restore supervisor state failed: {e}");
                    for (_, pid) in &bindings {
                        proctree::mark_dead(*pid);
                        unsafe { libc::kill(*pid, libc::SIGKILL) };
                    }
                    warm_pid = 0;
                    reply_code(ctrl_fd, -e.raw_os_error().unwrap_or(libc::EIO));
                    continue;
                }

                for pid in &restored_live {
                    unsafe { libc::kill(*pid, libc::SIGCONT) };
                }
                running_instances().lock().unwrap().extend(restored_live);

                if live_tree {
                    if restored_live_root > 0 {
                        pending_live_checkout = restored_live_root;
                        reply_code(ctrl_fd, 0);
                    } else {
                        reply_code(ctrl_fd, -1);
                    }
                } else if warm_pid > 0 {
                    let ready = ring_word(unsafe { std::ptr::addr_of_mut!((*ZYG).ready) });
                    if wait_ready_deadline(ready, warm_pid, cfg.exec_timeout_ms) {
                        reply_code(ctrl_fd, 0);
                    } else {
                        proctree::mark_dead(warm_pid);
                        unsafe { libc::kill(warm_pid, libc::SIGKILL) };
                        warm_pid = 0;
                        reply_code(ctrl_fd, -1);
                    }
                } else {
                    reply_code(ctrl_fd, -1);
                }
            }
            Ctrl::Warm(argv) if !argv.is_empty() => {
                // A warm pool routes loopback through the supervisor-OWNED LoopNet:
                // the NEV warm-restore primitive needs a live loopback connection to
                // survive fork-from-checkpoint in serializable supervisor state, and
                // the warm-daemon pools (which run `without_netns` for host-reachability)
                // need per-supervisor loopback isolation that shared host `lo` can't
                // give. NON-warm sandboxes use the real kernel `lo` (full mac/linux
                // parity). Set before the warm cell forks so its loopback ops route
                // correctly.
                // The SUPERVISOR also routes the warm cell's loopback through its own
                // LoopNet, so set OWN_LOOPBACK here (the launcher sets it for the cell).
                OWN_LOOPBACK.store(true, Ordering::Relaxed);
                // Fork a WARM (driven-zygote) cell in the SINGLE-THREADED launcher
                // (warm=true → it sets WARM_MODE/ZYG_DRIVEN/OWN_LOOPBACK + forks); the
                // cell runs argv to the SENTINEL checkpoint, signals ZYG.ready, then
                // parks + forks its own (single-threaded) instances on acquire.
                let pid = launch_cell(launch_fd, &argv, true);
                if pid > 0 {
                    seed_cell_fds(pid);
                    proctree::register_main(pid);
                    unsafe { set_ring_owner(0, pid, pid) };
                    continue_launched_cell(pid);
                    warm_pid = pid;
                    // Wait for the warm cell to reach the checkpoint — BOUNDED by the
                    // exec watchdog (cfg.exec_timeout_ms; 0 = unlimited but still
                    // liveness-checked) so a wedged/killed warm cell fails the pool
                    // build instead of hanging the supervisor forever.
                    let ready = ring_word(unsafe { std::ptr::addr_of_mut!((*ZYG).ready) });
                    if wait_ready_deadline(ready, pid, cfg.exec_timeout_ms) {
                        reply_code(ctrl_fd, 0);
                    } else {
                        // The cell tree was already torn down by wait_ready_deadline.
                        proctree::mark_dead(pid);
                        warm_pid = 0;
                        reply_code(ctrl_fd, -1);
                    }
                } else {
                    reply_code(ctrl_fd, -1);
                }
            }
            Ctrl::Acquire if warm_pid > 0 => {
                // Signal the warm cell to fork-from-warm one instance; wait for it.
                acq_seq += 1;
                let code = zygote_acquire(acq_seq);
                // Part D: reclaim any straggler the instance orphaned, SPARING the
                // long-lived warm zygote (warm_pid) which owns slot 0 and serves the
                // next acquire.
                sweep_stragglers(warm_pid);
                reply_code(ctrl_fd, code);
            }
            Ctrl::AcquireCapture if warm_pid > 0 && cap_fd.is_some() => {
                // Capture variant: point the warm zygote's stdout/stderr at the
                // passed pipe BEFORE the fork (so the instance inherits it via the
                // fd-table snapshot, race-free), acquire, then restore.
                let cap = cap_fd.unwrap();
                let (old1, old2) = warm_capture_redirect(warm_pid, cap);
                unsafe { libc::close(cap) }; // fdt[warm] holds the dups now
                acq_seq += 1;
                let code = zygote_acquire(acq_seq);
                // Part D: SIGKILL + reclaim any orphaned straggler holding the
                // capture pipe write-end, sparing the warm zygote, so the reader EOFs.
                sweep_stragglers(warm_pid);
                // The instance's dups were closed by its CTL_REAP (in the warm
                // loop) before `done`; closing the warm cell's dups here drops the
                // last write-ends, so the library's drain hits EOF.
                warm_capture_restore(warm_pid, old1, old2);
                reply_code(ctrl_fd, code);
            }
            Ctrl::AcquireRunning if pending_live_checkout > 0 => {
                let pid = pending_live_checkout;
                pending_live_checkout = 0;
                reply_code(ctrl_fd, pid);
            }
            Ctrl::AcquireRunning if warm_pid > 0 => {
                // Fork-from-warm a DETACHED, long-lived instance (a warm daemon) and
                // reply its pid. run_mode=1 tells the warm cell not to wait/reap it.
                acq_seq += 1;
                unsafe { (*ZYG).run_mode = 1 };
                let _ = zygote_acquire(acq_seq);
                let inst_pid = unsafe { (*ZYG).inst_pid };
                unsafe { (*ZYG).run_mode = 0 }; // reset for run-to-completion acquires
                if inst_pid > 1 {
                    running_instances().lock().unwrap().push(inst_pid);
                }
                // Sweep stragglers the warm fork may have orphaned, SPARING the warm
                // zygote AND every live detached instance (running_instances()).
                sweep_stragglers(warm_pid);
                reply_code(ctrl_fd, inst_pid);
            }
            Ctrl::Release(victim) if warm_pid > 0 => {
                // SIGKILL + reap a detached AcquireRunning instance. Drop it from the
                // protected set FIRST so the post-kill sweep can reclaim any straggler
                // it spawned, then drive the warm cell's release path (run_mode=2).
                running_instances().lock().unwrap().retain(|&p| p != victim);
                // The supervisor (trusted tier) issues the SIGKILL — the warm cell
                // can't (kill isn't in the cell seccomp allowlist). The instance is
                // the warm cell's child, so we then pump the warm cell (mode 2) to
                // reap its zombie, and finally free its ring slot + host fds here.
                if victim > 1 {
                    unsafe { libc::kill(victim, libc::SIGKILL) };
                }
                acq_seq += 1;
                unsafe {
                    (*ZYG).inst_pid = victim;
                    (*ZYG).run_mode = 2;
                }
                let _ = zygote_acquire(acq_seq);
                unsafe { (*ZYG).run_mode = 0 };
                reap_dead_pid(victim);
                sweep_stragglers(warm_pid);
                reply_code(ctrl_fd, 0);
            }
            Ctrl::Release(victim) => {
                running_instances().lock().unwrap().retain(|&p| p != victim);
                if victim > 1 {
                    unsafe { libc::kill(victim, libc::SIGKILL) };
                    let mut st: c_int = 0;
                    unsafe { libc::waitpid(victim, &mut st, 0) };
                    reap_dead_pid(victim);
                }
                sweep_stragglers(0);
                reply_code(ctrl_fd, 0);
            }
            Ctrl::Snapshot(dest) if !dest.is_empty() => {
                let code = match capture_state_snapshot(
                    launch_fd,
                    warm_pid,
                    std::path::Path::new(&dest),
                ) {
                    Ok(()) => 0,
                    Err(e) => {
                        eprintln!("sentry snapshot capture failed for pid {warm_pid}: {e}");
                        -e.raw_os_error().unwrap_or(libc::EIO)
                    }
                };
                reply_code(ctrl_fd, code);
            }
            _ => {
                // malformed / acquire-before-warm / capture without a pipe fd
                if let Some(f) = cap_fd {
                    unsafe { libc::close(f) };
                }
                reply_code(ctrl_fd, -1);
            }
        }
    }
}

/// A persistent-supervisor handle: holds the control socket to a long-lived
/// supervisor that forks a fresh cell per [`exec`](Pool::exec), reusing one
/// rootfs/ring/servicer setup. Execs are SERIALIZED (a per-Pool lock). Stdio is
/// inherited from the process that created the Pool.
///
/// The supervisor's config (egress / mounts / cgroup limits / uid-drop) applies
/// to EVERY cell it forks — `exec`, `warm`/`acquire` instances alike. Build it
/// from a configured [`Sandbox`] via [`Sandbox::pool`]; [`Pool::new`] is the
/// unconfigured shortcut.
/// Cumulative, lock-free operational counters for a sentry [`Pool`] — the no-virt
/// analogue of the VM backend's [`crate::api::PoolStats`]. Updated library-side on
/// every `exec`/`exec_capture` (the per-exec outcome rides back on the control
/// reply); snapshot via [`Pool::stats`]. Every exec also emits a structured
/// `tracing` event (target `supermachine.sentry.exec`) carrying argv + outcome +
/// duration — the audit trail.
#[derive(Default)]
struct PoolMetrics {
    execs: std::sync::atomic::AtomicU64, // total exec/exec_capture requests served
    ok: std::sync::atomic::AtomicU64,    // exited 0
    nonzero: std::sync::atomic::AtomicU64, // exited non-zero (workload error, not a kill)
    seccomp: std::sync::atomic::AtomicU64, // killed by the seccomp wall (SeccompViolation)
    signaled: std::sync::atomic::AtomicU64, // killed by another signal (OOM/limit/host kill)
    timeouts: std::sync::atomic::AtomicU64, // SIGKILL'd by the exec-timeout watchdog
    errors: std::sync::atomic::AtomicU64, // transport/protocol error (no exit code)
    total_ns: std::sync::atomic::AtomicU64, // Σ exec wall-time (for the mean)
    max_ns: std::sync::atomic::AtomicU64, // slowest exec
}

/// A snapshot of a sentry [`Pool`]'s operational counters (see [`Pool::stats`]) —
/// the single-tenant observability surface, mirroring the VM backend's
/// [`crate::api::PoolStats`] DX. The outcome tallies sum to `execs`.
#[derive(Debug, Clone, Copy)]
pub struct SentryPoolStats {
    pub execs: u64,
    pub ok: u64,
    pub nonzero: u64,
    pub seccomp_kills: u64,
    pub signaled: u64,
    pub timeouts: u64,
    pub errors: u64,
    pub mean_exec_ms: f64,
    pub max_exec_ms: f64,
}

/// Process-global registry of LIVE pool ctrl `lib_fd`s (the library-side ends of each
/// pool's supervisor socketpair). A supervisor forked for a new pool sheds every fd in
/// here at fork (see [`Pool::start`]) so it can't pin an earlier pool's ctrl socket open
/// — which would make that pool's [`stop`](Pool::stop) `waitpid` wedge (no EOF reaches
/// its supervisor). Mutated only on the library thread (start pushes, stop removes).
fn live_ctrl_fds() -> &'static Mutex<Vec<c_int>> {
    static R: std::sync::OnceLock<Mutex<Vec<c_int>>> = std::sync::OnceLock::new();
    R.get_or_init(|| Mutex::new(Vec::new()))
}

pub struct Pool {
    ctrl: std::sync::atomic::AtomicI32,
    pid: c_int,
    lock: Mutex<()>,
    /// The shared cgroup name (when limits are set), reaped on [`stop`](Pool::stop).
    cgroup_name: Option<String>,
    /// Cumulative operational counters (see [`Pool::stats`]).
    metrics: PoolMetrics,
    /// The streaming exec socket the supervisor serves (unified backend), unlinked
    /// on [`stop`](Pool::stop). `None` for a ctrl-socket-only pool.
    exec_sock: Option<std::path::PathBuf>,
}
impl Pool {
    /// Start a persistent supervisor confined to `root`, unconfigured (no egress
    /// policy, mounts, limits, or uid-drop). Use [`Sandbox::pool`] to start a
    /// configured pool.
    pub fn new(root: Option<&std::path::Path>) -> std::io::Result<Pool> {
        Pool::start(root, &SandboxCfg::default(), None)
    }

    /// The streaming exec socket path this pool's supervisor serves (unified
    /// backend), or `None` for a ctrl-socket-only pool. A `Vm` dials this with the
    /// normal `ExecBuilder` socket protocol (full streaming/stdin/separate-stderr).
    pub fn exec_socket_path(&self) -> Option<&std::path::Path> {
        self.exec_sock.as_deref()
    }

    /// Start a persistent supervisor confined to `root` with the given config.
    /// The config is applied once in the supervisor (`setup_sandbox_env`) and
    /// CoW-inherited by every cell it forks. When `exec_sock` is set the supervisor
    /// also binds + serves the unified streaming exec socket there.
    fn start(
        root: Option<&std::path::Path>,
        cfg: &SandboxCfg,
        exec_sock: Option<std::path::PathBuf>,
    ) -> std::io::Result<Pool> {
        let mut sv = [0 as c_int; 2];
        if unsafe { libc::socketpair(libc::AF_UNIX, libc::SOCK_SEQPACKET, 0, sv.as_mut_ptr()) } < 0
        {
            return Err(std::io::Error::last_os_error());
        }
        let (lib_fd, sup_fd) = (sv[0], sv[1]);
        let cgroup_name = cfg.cgroup_name.clone();
        // Snapshot the ctrl lib_fds of EARLIER live pools (taken BEFORE the fork so the
        // child sees exactly the foreign fds it must shed). A supervisor forked for a
        // LATER pool inherits earlier pools' ctrl lib_fds; unless it closes them, a
        // later `Pool::stop` on an earlier pool never EOFs that pool's supervisor (this
        // process still holds a dup of its lib_fd), so its `waitpid` wedges forever (the
        // cross-pool ctrl-fd leak). The shared ring is mmap (address space, not an fd),
        // so only stdio + this pool's sup_fd are needed at fork.
        let inherited_ctrl: Vec<c_int> = live_ctrl_fds().lock().unwrap().clone();
        // Serialize the persistent-supervisor config; the fork child re-execs THIS
        // binary into a pristine, single-threaded address space (fork-in-MT fix —
        // see spawn_sandbox / sentry_reexec_ctor) where it runs the supervisor.
        let cfg_mfd = write_reexec_config(&ReexecConfig::Persistent {
            root: root.map(|p| p.to_path_buf()),
            cfg: cfg.clone(),
            exec_sock: exec_sock.clone(),
        })?;
        let reexec_exe = reexec_exe_cstring()?;
        let pid = unsafe { libc::fork() };
        if pid < 0 {
            unsafe {
                libc::close(lib_fd);
                libc::close(sup_fd);
                libc::close(cfg_mfd);
            }
            return Err(std::io::Error::last_os_error());
        }
        if pid == 0 {
            unsafe {
                libc::close(lib_fd);
                for fd in inherited_ctrl {
                    libc::close(fd); // shed earlier pools' inherited ctrl ends
                }
                // Relocate config→REEXEC_CONFIG_FD(4) + ctrl socket→REEXEC_CTRL_FD(5)
                // without clobbering each other: F_DUPFD to a high scratch, then dup2
                // down (dup2 clears CLOEXEC so they survive the execve). Then shed
                // everything ≥6 + the stray fd 3, and re-exec.
                let cfg_hi = libc::fcntl(cfg_mfd, libc::F_DUPFD, 20);
                let sup_hi = libc::fcntl(sup_fd, libc::F_DUPFD, 20);
                libc::dup2(cfg_hi, REEXEC_CONFIG_FD);
                libc::dup2(sup_hi, REEXEC_CTRL_FD);
                libc::close(STATUS_PIPE_FD); // 3: no status pipe on the pool path
                let lo = REEXEC_CTRL_FD as u64 + 1;
                if host(SYS_CLOSE_RANGE, lo, u32::MAX as u64, 0, 0, 0, 0) < 0 {
                    let mut lim: libc::rlimit = std::mem::zeroed();
                    let max = if libc::getrlimit(libc::RLIMIT_NOFILE, &mut lim) == 0 {
                        (lim.rlim_cur as c_int).clamp(64, 4096)
                    } else {
                        1024
                    };
                    for fd in lo as c_int..max {
                        libc::close(fd);
                    }
                }
                let argv: [*const c_char; 3] = [
                    reexec_exe.as_ptr(),
                    REEXEC_MARKER.as_ptr() as *const c_char,
                    std::ptr::null(),
                ];
                libc::execve(reexec_exe.as_ptr(), argv.as_ptr(), current_environ());
                libc::_exit(127); // execve failed — fail closed
            }
        }
        unsafe {
            libc::close(sup_fd);
            libc::close(cfg_mfd);
        };
        // Register AFTER the fork so this pool's own lib_fd isn't in the child's
        // inherited-shed list, but IS shed by any pool started later.
        live_ctrl_fds().lock().unwrap().push(lib_fd);
        Ok(Pool {
            ctrl: std::sync::atomic::AtomicI32::new(lib_fd),
            pid,
            lock: Mutex::new(()),
            cgroup_name,
            metrics: PoolMetrics::default(),
            exec_sock,
        })
    }

    /// Read the 8-byte reply frame (`[code: i32 LE][err_tag: u32 LE]`, see
    /// [`reply_status`]) off the control socket and decode it into
    /// `(exit_code, reason)`.
    fn recv_status(fd: c_int) -> std::io::Result<(i32, Option<SentryError>)> {
        let mut buf = [0u8; 8];
        let n = unsafe { libc::recv(fd, buf.as_mut_ptr() as *mut c_void, 8, 0) };
        if n != 8 {
            return Err(std::io::Error::new(
                std::io::ErrorKind::BrokenPipe,
                "supervisor closed",
            ));
        }
        let code = i32::from_le_bytes(buf[0..4].try_into().unwrap());
        let tag = u32::from_le_bytes(buf[4..8].try_into().unwrap());
        Ok((code, tag_to_err(code, tag)))
    }

    /// Send an opcode + argv request and read back the `(exit_code, reason)` reply.
    /// Serialized per Pool.
    fn request(&self, op: u8, argv: &[String]) -> std::io::Result<(i32, Option<SentryError>)> {
        let mut req = vec![op];
        for a in argv {
            req.extend_from_slice(a.as_bytes());
            req.push(0);
        }
        let _g = self.lock.lock().unwrap();
        let fd = self.ctrl.load(Ordering::Relaxed);
        let sent = unsafe {
            libc::send(
                fd,
                req.as_ptr() as *const c_void,
                req.len(),
                libc::MSG_NOSIGNAL,
            )
        };
        if sent < 0 {
            return Err(std::io::Error::last_os_error());
        }
        Self::recv_status(fd)
    }

    /// Record one exec's outcome into the cumulative counters AND emit the audit
    /// `tracing` event. `code = None` means a transport/protocol error (no exit
    /// code). Called library-side by `exec_classified`/`exec_capture_classified`,
    /// where the `tracing` subscriber lives (the supervisor is a separate process).
    fn record_exec(
        &self,
        op: &'static str,
        argv: &[String],
        code: Option<i32>,
        reason: Option<&SentryError>,
        dur: std::time::Duration,
    ) {
        use std::sync::atomic::Ordering::Relaxed;
        let m = &self.metrics;
        m.execs.fetch_add(1, Relaxed);
        let ns = dur.as_nanos().min(u64::MAX as u128) as u64;
        m.total_ns.fetch_add(ns, Relaxed);
        m.max_ns.fetch_max(ns, Relaxed);
        let outcome = match (code, reason) {
            (None, _) => {
                m.errors.fetch_add(1, Relaxed);
                "error"
            }
            (Some(_), Some(SentryError::SeccompViolation { .. })) => {
                m.seccomp.fetch_add(1, Relaxed);
                "seccomp_kill"
            }
            (Some(_), Some(SentryError::Signaled { .. })) => {
                m.signaled.fetch_add(1, Relaxed);
                "signaled"
            }
            (Some(_), Some(SentryError::Timeout)) => {
                m.timeouts.fetch_add(1, Relaxed);
                "timeout"
            }
            (Some(0), None) => {
                m.ok.fetch_add(1, Relaxed);
                "ok"
            }
            (Some(_), None) => {
                m.nonzero.fetch_add(1, Relaxed);
                "nonzero"
            }
        };
        tracing::info!(
            target: "supermachine.sentry.exec",
            op,
            argv = ?argv,
            code = code.unwrap_or(-1),
            outcome,
            dur_ms = dur.as_secs_f64() * 1e3,
            "sentry exec"
        );
    }

    /// Snapshot this pool's cumulative operational counters — the single-tenant
    /// observability surface (the no-virt analogue of [`crate::api::Pool::stats`]).
    /// Lock-free; safe to poll concurrently with execs.
    pub fn stats(&self) -> SentryPoolStats {
        use std::sync::atomic::Ordering::Relaxed;
        let m = &self.metrics;
        let execs = m.execs.load(Relaxed);
        let total_ns = m.total_ns.load(Relaxed);
        SentryPoolStats {
            execs,
            ok: m.ok.load(Relaxed),
            nonzero: m.nonzero.load(Relaxed),
            seccomp_kills: m.seccomp.load(Relaxed),
            signaled: m.signaled.load(Relaxed),
            timeouts: m.timeouts.load(Relaxed),
            errors: m.errors.load(Relaxed),
            mean_exec_ms: if execs > 0 {
                (total_ns as f64 / execs as f64) / 1e6
            } else {
                0.0
            },
            max_exec_ms: m.max_ns.load(Relaxed) as f64 / 1e6,
        }
    }

    /// Run a command to completion in a fresh cell under the persistent
    /// supervisor, confined to the rootfs; returns its exit code. `argv[0]` is
    /// the guest-visible exe path (e.g. `/bin/sh`). Serialized per Pool.
    pub fn exec<I, S>(&self, argv: I) -> std::io::Result<i32>
    where
        I: IntoIterator<Item = S>,
        S: Into<String>,
    {
        self.exec_classified(argv).map(|(c, _)| c)
    }

    /// As [`exec`](Pool::exec), but also returns the structured [`SentryError`]
    /// reason (`Some(SeccompViolation { .. })` for a forbidden-syscall kill, etc.)
    /// so a sandbox-policy kill is distinguishable from a workload's own non-zero
    /// exit — both of which flatten to the same `128+sig` integer code.
    pub fn exec_classified<I, S>(&self, argv: I) -> std::io::Result<(i32, Option<SentryError>)>
    where
        I: IntoIterator<Item = S>,
        S: Into<String>,
    {
        let v: Vec<String> = argv.into_iter().map(Into::into).collect();
        if v.is_empty() {
            return Err(std::io::Error::from(std::io::ErrorKind::InvalidInput));
        }
        let t0 = std::time::Instant::now();
        let r = self.request(b'E', &v);
        let (code, reason) = match &r {
            Ok((c, rn)) => (Some(*c), rn.as_ref()),
            Err(_) => (None, None),
        };
        self.record_exec("exec", &v, code, reason, t0.elapsed());
        r
    }

    /// Like [`exec`](Pool::exec) but captures the command's stdout+stderr,
    /// returning `(exit_code, output)`. The library passes a pipe's write-end to
    /// the supervisor over the control socket (SCM_RIGHTS); the supervisor seeds
    /// the fresh cell's stdout/stderr from it. Serialized per Pool.
    pub fn exec_capture<I, S>(&self, argv: I) -> std::io::Result<(i32, Vec<u8>)>
    where
        I: IntoIterator<Item = S>,
        S: Into<String>,
    {
        self.exec_capture_classified(argv).map(|(c, o, _)| (c, o))
    }

    /// As [`exec_capture`](Pool::exec_capture), but also returns the structured
    /// [`SentryError`] reason (`(exit_code, output, reason)`) so a seccomp-wall kill
    /// is distinguishable from a workload's own non-zero exit.
    pub fn exec_capture_classified<I, S>(
        &self,
        argv: I,
    ) -> std::io::Result<(i32, Vec<u8>, Option<SentryError>)>
    where
        I: IntoIterator<Item = S>,
        S: Into<String>,
    {
        let v: Vec<String> = argv.into_iter().map(Into::into).collect();
        if v.is_empty() {
            return Err(std::io::Error::from(std::io::ErrorKind::InvalidInput));
        }
        let mut fds = [0 as c_int; 2];
        if unsafe { libc::pipe(fds.as_mut_ptr()) } < 0 {
            return Err(std::io::Error::last_os_error());
        }
        let (r, w) = (fds[0], fds[1]);
        let t0 = std::time::Instant::now();
        let mut req = vec![b'C'];
        for a in &v {
            req.extend_from_slice(a.as_bytes());
            req.push(0);
        }
        let _g = self.lock.lock().unwrap();
        let fd = self.ctrl.load(Ordering::Relaxed);
        if let Err(e) = send_ctrl_with_fd(fd, &req, w) {
            unsafe {
                libc::close(r);
                libc::close(w);
            }
            return Err(e);
        }
        // Drop our write-end; only the cell's (serviced) dups now hold it open, so
        // the pipe EOFs when the cell exits. Drain it to EOF BEFORE reading the
        // reply code — the supervisor blocks in wait_exit (it doesn't drain), so we
        // must, or a >pipe-buffer guest would deadlock against a full pipe.
        unsafe { libc::close(w) };
        let mut out = Vec::new();
        let mut buf = [0u8; 8192];
        loop {
            let n = unsafe { libc::read(r, buf.as_mut_ptr() as *mut c_void, buf.len()) };
            if n > 0 {
                out.extend_from_slice(&buf[..n as usize]);
            } else if n == 0 {
                break;
            } else {
                let e = std::io::Error::last_os_error();
                if e.kind() == std::io::ErrorKind::Interrupted {
                    continue;
                }
                break;
            }
        }
        unsafe { libc::close(r) };
        let res = Self::recv_status(fd).map(|(code, err)| (code, out, err));
        let (code, reason) = match &res {
            Ok((c, _, rn)) => (Some(*c), rn.as_ref()),
            Err(_) => (None, None),
        };
        self.record_exec("exec_capture", &v, code, reason, t0.elapsed());
        res
    }

    /// Full-request variant of [`exec_capture`](Pool::exec_capture): `body` is the
    /// JSON exec request (`{argv,env,cwd,stage_files,chain}`). The supervisor stages
    /// files into the rootfs, applies the per-exec merged env + cwd, runs argv (then
    /// each `chain` argv while the previous exited 0), and streams combined
    /// stdout+stderr back. Returns `(exit_code, output)`. The request rides one
    /// SEQPACKET control message, so very large `stage_files` (≳200 KiB) aren't
    /// supported yet — fine for command/script execs; large-file staging stays on
    /// the ExecServer path until streamed staging lands.
    pub fn exec_capture_full(&self, body: &[u8]) -> std::io::Result<(i32, Vec<u8>)> {
        let mut fds = [0 as c_int; 2];
        if unsafe { libc::pipe(fds.as_mut_ptr()) } < 0 {
            return Err(std::io::Error::last_os_error());
        }
        let (r, w) = (fds[0], fds[1]);
        let t0 = std::time::Instant::now();
        let mut req = vec![b'X'];
        req.extend_from_slice(body);
        let _g = self.lock.lock().unwrap();
        let fd = self.ctrl.load(Ordering::Relaxed);
        if let Err(e) = send_ctrl_with_fd(fd, &req, w) {
            unsafe {
                libc::close(r);
                libc::close(w);
            }
            return Err(e);
        }
        unsafe { libc::close(w) };
        let mut out = Vec::new();
        let mut buf = [0u8; 8192];
        loop {
            let n = unsafe { libc::read(r, buf.as_mut_ptr() as *mut c_void, buf.len()) };
            if n > 0 {
                out.extend_from_slice(&buf[..n as usize]);
            } else if n == 0 {
                break;
            } else {
                let e = std::io::Error::last_os_error();
                if e.kind() == std::io::ErrorKind::Interrupted {
                    continue;
                }
                break;
            }
        }
        unsafe { libc::close(r) };
        let res = Self::recv_status(fd);
        // Record the exec in the pool metrics — this is the route a WARM-DAEMON pooled
        // Vm's `.output()` takes (ExecBuilder::with_warm_pool → exec_capture_full), so
        // without this the warm-daemon `sentry_stats()` would always report execs=0
        // (only exec/exec_capture were instrumented). argv lives inside the JSON `body`;
        // the op tag + metrics are what observability needs, so log an empty argv.
        let (rcode, reason) = match &res {
            Ok((c, rn)) => (Some(*c), rn.as_ref()),
            Err(_) => (None, None),
        };
        self.record_exec("exec_capture_full", &[], rcode, reason, t0.elapsed());
        let (code, _err) = res?;
        Ok((code, out))
    }

    /// Warm a zygote: run `argv` to its SENTINEL checkpoint (`syscall(0x5359)` =
    /// "ready") and park it. Subsequent [`acquire`](Pool::acquire) calls
    /// fork-from-warm an instance that resumes from the checkpoint. One zygote per
    /// Pool. Returns once warmed.
    pub fn warm<I, S>(&self, argv: I) -> std::io::Result<()>
    where
        I: IntoIterator<Item = S>,
        S: Into<String>,
    {
        let v: Vec<String> = argv.into_iter().map(Into::into).collect();
        if v.is_empty() {
            return Err(std::io::Error::from(std::io::ErrorKind::InvalidInput));
        }
        // A negative reply means the supervisor's bounded ready-wait
        // (`wait_ready_deadline`) gave up — the warm cell wedged or died before the
        // SENTINEL checkpoint and its tree was already torn down. Surface it as an
        // Err so `SentryPool::build` fails cleanly instead of returning a "warm" pool
        // whose zygote never reached the checkpoint.
        let (code, _) = self.request(b'W', &v)?;
        if code < 0 {
            return Err(std::io::Error::new(
                std::io::ErrorKind::TimedOut,
                "sentry warm: cell wedged or died before reaching the SENTINEL checkpoint",
            ));
        }
        Ok(())
    }

    /// Restore a warm zygote from a C4 state snapshot directory (`restore.snap` +
    /// `mem.blob`) and park it for [`acquire`](Self::acquire). This is the
    /// fresh-process counterpart to [`warm`](Self::warm): it does not re-run the
    /// workload up to the SENTINEL, it maps the captured memory/register state.
    pub fn warm_restore(&self, snapshot_dir: impl AsRef<std::path::Path>) -> std::io::Result<()> {
        let path = snapshot_dir.as_ref().to_string_lossy().into_owned();
        let (code, _) = self.request(b'Z', &[path])?;
        if code == 0 {
            Ok(())
        } else if code < 0 {
            Err(std::io::Error::from_raw_os_error((-code).max(1)))
        } else {
            Err(std::io::Error::new(
                std::io::ErrorKind::Other,
                format!("sentry warm_restore returned unexpected code {code}"),
            ))
        }
    }

    /// Fork-from-warm an instance from the [`warm`](Pool::warm)ed zygote: it
    /// resumes the guest at the checkpoint (skipping load + relocation + init).
    /// Returns the instance's exit code. Serialized per Pool.
    pub fn acquire(&self) -> std::io::Result<i32> {
        self.acquire_classified().map(|(c, _)| c)
    }

    /// As [`acquire`](Pool::acquire), but also returns the structured [`SentryError`]
    /// reason when the instance ended other than a plain exit.
    pub fn acquire_classified(&self) -> std::io::Result<(i32, Option<SentryError>)> {
        self.request(b'A', &[])
    }

    /// Like [`acquire`](Pool::acquire) but captures the fork-from-warm instance's
    /// stdout+stderr, returning `(exit_code, output)`. The pipe write-end is passed
    /// to the supervisor (SCM_RIGHTS), which points the warm zygote's stdout/stderr
    /// at it before forking the instance — so the instance inherits the pipe via
    /// the fd-table snapshot (race-free). Serialized per Pool.
    pub fn acquire_capture(&self) -> std::io::Result<(i32, Vec<u8>)> {
        let mut fds = [0 as c_int; 2];
        if unsafe { libc::pipe(fds.as_mut_ptr()) } < 0 {
            return Err(std::io::Error::last_os_error());
        }
        let (r, w) = (fds[0], fds[1]);
        let _g = self.lock.lock().unwrap();
        let fd = self.ctrl.load(Ordering::Relaxed);
        if let Err(e) = send_ctrl_with_fd(fd, b"a", w) {
            unsafe {
                libc::close(r);
                libc::close(w);
            }
            return Err(e);
        }
        // Drop our write-end and drain to EOF BEFORE reading the reply code (the
        // supervisor blocks waiting on the instance and never drains — a guest
        // writing past the pipe buffer would otherwise deadlock).
        unsafe { libc::close(w) };
        let mut out = Vec::new();
        let mut buf = [0u8; 8192];
        loop {
            let n = unsafe { libc::read(r, buf.as_mut_ptr() as *mut c_void, buf.len()) };
            if n > 0 {
                out.extend_from_slice(&buf[..n as usize]);
            } else if n == 0 {
                break;
            } else {
                let e = std::io::Error::last_os_error();
                if e.kind() == std::io::ErrorKind::Interrupted {
                    continue;
                }
                break;
            }
        }
        unsafe { libc::close(r) };
        let (code, _err) = Self::recv_status(fd)?;
        Ok((code, out))
    }

    /// L0b — fork-from-warm a DETACHED, long-lived instance (a warm daemon): it
    /// resumes the guest at the checkpoint and keeps running (e.g. its serve loop),
    /// owning a ring slot + servicer, until a matching [`release`](Pool::release).
    /// Returns the instance's pid. Unlike [`acquire`](Pool::acquire) this does NOT
    /// wait for it to finish — the instance lives on so subsequent client
    /// [`exec`](Pool::exec)s can reach it (e.g. over the owned loopback). Serialized
    /// per Pool.
    pub fn acquire_running(&self) -> std::io::Result<i32> {
        let (pid, _) = self.request(b'R', &[])?;
        if pid <= 1 {
            return Err(std::io::Error::new(
                std::io::ErrorKind::Other,
                "acquire_running: warm fork failed",
            ));
        }
        Ok(pid)
    }

    /// Release a detached instance acquired via [`acquire_running`](Pool::acquire_running):
    /// SIGKILL + reap it (frees its ring slot + host fds). Idempotent for an unknown
    /// pid (the warm cell no-ops). Serialized per Pool.
    pub fn release(&self, inst_pid: i32) -> std::io::Result<()> {
        self.request(b'r', &[inst_pid.to_string()]).map(|_| ())
    }

    /// Capture the parked warm cell's owned sentry state into an already-created
    /// snapshot directory (`restore.snap` plus `mem.blob`). The pool must have
    /// reached [`warm`](Self::warm); capture is serialized with other control
    /// requests by the pool lock.
    pub fn snapshot(&self, dest_dir: impl AsRef<std::path::Path>) -> std::io::Result<()> {
        let path = dest_dir.as_ref().to_string_lossy().into_owned();
        let (code, _) = self.request(b'S', &[path])?;
        if code == 0 {
            Ok(())
        } else {
            Err(std::io::Error::from_raw_os_error((-code).max(1)))
        }
    }

    /// Stop the supervisor (close the control socket) and reap it, then reap the
    /// pool's shared cgroup (if limits were set). Idempotent.
    pub fn stop(&self) {
        let fd = self.ctrl.swap(-1, Ordering::Relaxed);
        if fd >= 0 {
            // Deregister so a future pool's supervisor doesn't try to shed a recycled
            // fd number, and so this ctrl end is no longer tracked as live.
            live_ctrl_fds().lock().unwrap().retain(|&f| f != fd);
            unsafe { libc::close(fd) };
            let mut st: c_int = 0;
            unsafe { libc::waitpid(self.pid, &mut st, 0) };
            if let Some(name) = &self.cgroup_name {
                remove_cgroup_named(name);
            }
            if let Some(p) = &self.exec_sock {
                let _ = std::fs::remove_file(p);
            }
        }
    }
}
impl Drop for Pool {
    fn drop(&mut self) {
        self.stop();
    }
}

/// The sandbox launcher (runs in a forked child of `run`): set up the rootfs
/// dirfd + shared ring, then fork the per-tenant supervisor and the sealed cell.
fn sandbox_main(
    path: &std::path::Path,
    args: &[String],
    root: Option<&std::path::Path>,
    cfg: &SandboxCfg,
) -> ! {
    setup_sandbox_env(root, cfg);

    let path_str = path.to_string_lossy().to_string();
    // argv[0] = the ELF path; the rest are the guest's args.
    let mut cell_args: Vec<String> = Vec::with_capacity(args.len() + 1);
    cell_args.push(path_str.clone());
    cell_args.extend(args.iter().cloned());
    let root_owned = root.map(|r| r.to_path_buf());
    set_guest_cmdline(&cell_args);

    let pid = unsafe { libc::fork() };
    if pid < 0 {
        die(b"fork failed\n");
    }
    if pid == 0 {
        cell_main(&path_str, &cell_args, root_owned);
    } else {
        supervisor_main(pid);
    }
}

/// Stash the guest argv as `/proc/self/cmdline` bytes (NUL-separated, trailing
/// NUL) + the exe path, for the supervisor to serve. Set before the cell fork.
fn set_guest_cmdline(cell_args: &[String]) {
    let mut cl = Vec::new();
    for a in cell_args {
        cl.extend_from_slice(a.as_bytes());
        cl.push(0);
    }
    *guest_cmdline().lock().unwrap() = cl;
    *guest_exe().lock().unwrap() = cell_args
        .first()
        .map(|s| s.as_bytes().to_vec())
        .unwrap_or_default();
}

/// One-time per-sandbox environment setup shared by the per-exec [`sandbox_main`]
/// and the [`persistent_supervisor_main`]: egress policy, uid-drop target, rootfs
/// dirfd + canonical path, bind-mounts, cgroup, and the shared ring table.
fn setup_sandbox_env(root: Option<&std::path::Path>, cfg: &SandboxCfg) {
    seed_guest_monotonic_clock();
    // Keep sentry's real host fd table larger than any guest-visible NOFILE budget:
    // the supervisor may hold many host fds on behalf of many guest processes, while
    // guest setrlimit/prlimit64 is virtualized per pid below.
    unsafe {
        let mut lim: libc::rlimit = std::mem::zeroed();
        if libc::getrlimit(libc::RLIMIT_NOFILE, &mut lim) == 0 {
            GUEST_NOFILE_DEFAULT_CUR.store(lim.rlim_cur as u64, Ordering::Relaxed);
            GUEST_NOFILE_DEFAULT_MAX.store(lim.rlim_max as u64, Ordering::Relaxed);
            const WANT: u64 = 1_048_576;
            if (lim.rlim_cur as u64) < WANT || (lim.rlim_max as u64) < WANT {
                let raised = libc::rlimit {
                    rlim_cur: WANT as libc::rlim_t,
                    rlim_max: WANT.max(lim.rlim_max as u64) as libc::rlim_t,
                };
                if libc::setrlimit(libc::RLIMIT_NOFILE, &raised) != 0 {
                    let fallback = libc::rlimit {
                        rlim_cur: WANT.min(lim.rlim_max as u64) as libc::rlim_t,
                        rlim_max: lim.rlim_max,
                    };
                    let _ = libc::setrlimit(libc::RLIMIT_NOFILE, &fallback);
                }
                let _ = libc::getrlimit(libc::RLIMIT_NOFILE, &mut lim);
            }
        }
    }
    emfile_reserve_open();
    // Probe the host IPv6 route ONCE, BEFORE the netns unshare below — egress is
    // re-homed to the HOST netns ([`rehome_to_host_netns`]), so the v6 reachability
    // the cell's sockets actually get is the host's, not the (route-less) sandbox
    // netns's. Priming the cache here (rather than lazily on the first net syscall,
    // which would run inside the isolated netns) keeps the AAAA / AF_INET6 gate in
    // sync with the virtio-vsock muxer's. The result is frozen for this process.
    let _ = host_v6_route();
    // Isolate the network namespace FIRST (before any servicer thread spawns or
    // cell forks), so the whole sandbox tree — and every socket the servicers open
    // on the guest's behalf — lives in the private netns. Single-threaded here, so
    // the unshare is safe. ON by DEFAULT (`SandboxCfg::netns`); the only cells that
    // skip it are explicit `without_netns()` ones (a host-reachable workload).
    if cfg.netns {
        setup_netns();
    }
    // Set the per-sandbox egress policy. This forked process is private to the
    // sandbox, so the global never leaks into the library caller.
    if let Some(p) = &cfg.egress {
        crate::vmm::egress_policy::set(p);
    }
    // The uid/gid the cell drops to before sealing (read in cell_main, pre-fork).
    *run_uid().lock().unwrap() = cfg.run_uid;
    // Guest environment + initial cwd (read in cell_main; CoW-inherited by the cell).
    if !cfg.env.is_empty() {
        *guest_env().lock().unwrap() = cfg
            .env
            .iter()
            .map(|(k, v)| {
                let mut b = k.as_bytes().to_vec();
                b.push(b'=');
                b.extend_from_slice(v.as_bytes());
                b
            })
            .collect();
    }
    if let Some(c) = &cfg.cwd {
        *guest_cwd_init().lock().unwrap() = Some(c.as_bytes().to_vec());
    }
    // Ports this sandbox publishes (host-reachable) when it's a netns sandbox.
    if !cfg.published_ports.is_empty() {
        *published_ports().lock().unwrap() = cfg.published_ports.clone();
    }
    // Rewrite fast-path (Phase 1b): opt-in with SENTRY_REWRITE=1. It is a useful
    // speed path for narrower workloads, but Chromium under the 2-vCPU shopify
    // conformance profile still exposes rewrite-only control-flow corruption
    // (SIGSEGV/SI_KERNEL #GP during patchright launch). Keep the parity path on
    // pure SUD by default; callers can explicitly opt into rewrite for debugging
    // or benchmarks.
    // Map the VA-0 trampoline NOW, while the supervisor still has CAP_SYS_RAWIO
    // (before drop_supervisor_caps) — cells fork later and inherit the page via CoW;
    // setup_trampoline auto-disables (REWRITE_ON=false, transparent SIGSYS fallback)
    // if VA 0 is unavailable (no CAP_SYS_RAWIO / mmap_min_addr != 0). Then arm the
    // lazy patcher in the cell.
    if std::env::var("SENTRY_REWRITE")
        .map(|v| v == "1")
        .unwrap_or(false)
    {
        REWRITE_ON.store(true, Ordering::Relaxed);
        setup_trampoline();
    }
    let open_dir = |p: &std::path::Path| -> i32 {
        match std::ffi::CString::new(p.to_string_lossy().as_bytes().to_vec()) {
            Ok(c) => unsafe { libc::open(c.as_ptr(), libc::O_PATH | libc::O_DIRECTORY) },
            Err(_) => -1,
        }
    };
    if let Some(r) = root {
        // Open the rootfs as an O_PATH|O_DIRECTORY dirfd; service() resolves every
        // guest path beneath it via openat2(RESOLVE_IN_ROOT).
        let fd = open_dir(r);
        if fd < 0 {
            die(b"cannot open root directory\n");
        }
        ROOT_FD.store(fd, Ordering::Relaxed);
        // Record the CANONICAL host path of the rootfs so `chdir` can map a
        // subdir's canonical path back to the guest-absolute cwd even if `root`
        // was passed with symlinks or `..` in it.
        if let Some(canon) = readlink_fd(fd) {
            *root_path().lock().unwrap() = canon;
        }
        ensure_runtime_dirs_in_rootfs(r);
        // /etc/resolv.conf: OCI rootfs images often ship none (or a stub the cell
        // can't reach), and the sentry has no init to write one — so getaddrinfo
        // gets no nameserver and every lookup fails ("bad address"). Mirror the
        // HOST's first nameserver as a SINGLE-nameserver, option-free resolv.conf
        // (musl mishandles multi-nameserver/`options` blocks — cf. the KVM init's
        // dual-nameserver regression). Pooled sandboxes run in the host netns, so
        // the host's resolver address is reachable. Best-effort.
        {
            let ns = host_resolv_nameserver();
            // Checkpoint helper (see `sm_checkpoint_elf`): lets a non-cooperating
            // service CMD run as a warm-daemon detached instance sharing the pool
            // supervisor's loopback + proctree with execs. ~170 bytes; written for
            // every sandbox so the warm path can use it (workload-contract / S2).
            use std::os::unix::fs::PermissionsExt;
            // These setup files land IN the (possibly SHARED) rootfs dir — multiple
            // sandboxes can serve the same content-addressed image rootfs concurrently
            // (e.g. a build's verify-execs, or two VMs from one image). A plain
            // truncate+write is observable half-written by a peer cell's loader →
            // torn-tree failures ("cannot read ld.so", "metadata.json EOF"). Write to a
            // unique sibling tmp then rename(2) (atomic), and skip the checkpoint ELF
            // when it already exists (it is a constant, so one writer suffices).
            let pid = unsafe { libc::getpid() };
            let smdir = r.join(".supermachine");
            let _ = std::fs::create_dir_all(&smdir);
            let ckpt = smdir.join("sm-checkpoint");
            if !ckpt.exists() {
                let tmp = smdir.join(format!(".sm-checkpoint.{pid}.tmp"));
                if std::fs::write(&tmp, sm_checkpoint_elf()).is_ok() {
                    let _ = std::fs::set_permissions(&tmp, std::fs::Permissions::from_mode(0o755));
                    if std::fs::rename(&tmp, &ckpt).is_err() {
                        let _ = std::fs::remove_file(&tmp);
                    }
                } else {
                    let _ = std::fs::remove_file(&tmp);
                }
            }
            let etc = r.join("etc");
            let _ = std::fs::create_dir_all(&etc);
            // `single-request no-aaaa` mirrors the KVM/HVF init: it forces glibc to
            // issue the A and AAAA queries sequentially (not in one UDP datagram —
            // the relay's msg_name handling is single-destination) and to skip AAAA
            // on a v6-less host, so getaddrinfo doesn't stall on an unroutable v6
            // answer. musl ignores unknown options, so this is safe for both libcs.
            let resolv_tmp = etc.join(format!(".resolv.conf.{pid}.tmp"));
            if std::fs::write(
                &resolv_tmp,
                format!("nameserver {ns}\noptions single-request no-aaaa\n"),
            )
            .is_ok()
            {
                if std::fs::rename(&resolv_tmp, etc.join("resolv.conf")).is_err() {
                    let _ = std::fs::remove_file(&resolv_tmp);
                }
            } else {
                let _ = std::fs::remove_file(&resolv_tmp);
            }
        }
    }
    // Bind-mounts: open each host dir as a confined dirfd; `open_path`/`service`
    // redirect guest paths under the mount's prefix to it. A mount whose host dir
    // can't be opened is fatal (the caller asked for a volume that isn't there).
    if !cfg.mounts.is_empty() {
        let mut mt = mounts().lock().unwrap();
        for (guest, host_dir, ro) in &cfg.mounts {
            let fd = open_dir(host_dir);
            if fd < 0 {
                die(b"cannot open mount host directory\n");
            }
            let host_canonical = readlink_fd(fd).unwrap_or_default();
            // Normalize the guest prefix: absolute, no trailing slash.
            let mut gp = guest.to_string_lossy().as_bytes().to_vec();
            while gp.last() == Some(&b'/') && gp.len() > 1 {
                gp.pop();
            }
            if gp.first() != Some(&b'/') {
                die(b"mount guest_path must be absolute\n");
            }
            mt.push(Mount {
                guest_prefix: gp,
                dirfd: fd,
                host_canonical,
                readonly: *ro,
            });
        }
    }
    // /dev/shm: OCI rootfs images ship no `/dev/shm`, but runtimes expect a
    // writable POSIX-shm dir there (python `multiprocessing`, glibc `shm_open`,
    // chromium, redis). Back it with a private host tmpdir exposed as an implicit
    // writable mount at `/dev/shm` (served by the same confined-dirfd machinery as
    // any volume). Best-effort: if the tmpdir can't be created/opened, `/dev/shm`
    // simply isn't present (the pre-existing behavior). A per-supervisor-pid name
    // keeps concurrent sandboxes from sharing the dir; a stale dir from a recycled
    // pid is cleared first (as `setup_cgroup` does), and teardown leaves the
    // (typically empty) dir for the OS tmp-reaper.
    if root.is_some() {
        let shm = std::env::temp_dir().join(format!("sentry-shm-{}", unsafe { libc::getpid() }));
        let _ = std::fs::remove_dir_all(&shm);
        let _ = std::fs::create_dir_all(&shm);
        let fd = open_dir(&shm);
        if fd >= 0 {
            let host_canonical = readlink_fd(fd).unwrap_or_default();
            mounts().lock().unwrap().push(Mount {
                guest_prefix: b"/dev/shm".to_vec(),
                dirfd: fd,
                host_canonical,
                readonly: false,
            });
        }
    }
    unsafe { TRACE = std::env::var("SENTRY_TRACE").is_ok() };
    unsafe { IPCTRACE = std::env::var("SENTRY_IPCTRACE").is_ok() };
    unsafe { WAITTRACE = std::env::var("SENTRY_WAITTRACE").is_ok() };
    unsafe { SYSCALLTRACE = std::env::var("SENTRY_SYSCALLTRACE").is_ok() };
    unsafe { FDTRACE = std::env::var("SENTRY_FDTRACE").is_ok() };
    unsafe { SLOTDUMP = std::env::var("SENTRY_SLOTDUMP").is_ok() };
    REGDIFF.store(
        std::env::var("SENTRY_REGDIFF").is_ok(),
        std::sync::atomic::Ordering::Relaxed,
    );
    ENVDBG.store(
        std::env::var("SENTRY_ENVDBG").is_ok(),
        std::sync::atomic::Ordering::Relaxed,
    );
    ENVSCAN.store(
        std::env::var("SENTRY_ENVSCAN").is_ok(),
        std::sync::atomic::Ordering::Relaxed,
    );

    // cgroup-v2 limits: create-or-join the guest cgroup now and stash the path
    // for the cell to join pre-seal. The supervisor stays OUT, so the caps bound
    // only the guest tree. A `Sandbox` passes a STABLE per-sandbox name so all
    // its execs SHARE one cgroup (concurrent execs share the cap); `run`/`spawn`
    // get a per-call `sentry-<pid>` (reaped by wait_exit). Best-effort.
    if cfg.limits.any() {
        // Stash the caps so the cgroup-aware `/proc` synthesizers can reflect them
        // (the guest sees the cap as its "machine size", not the host's). Done even
        // when `setup_cgroup` below fails to create the real cgroup: the synthesized
        // view is still the contract the guest was promised, and the fidelity is
        // what runtimes size pools against.
        *cell_limits().lock().unwrap() = cfg.limits.clone();
        let shared = cfg.cgroup_name.is_some();
        let name = cfg
            .cgroup_name
            .clone()
            .unwrap_or_else(|| format!("sentry-{}", unsafe { libc::getpid() }));
        match setup_cgroup(&name, &cfg.limits, !shared) {
            Some(dir) => *cgroup_path().lock().unwrap() = Some(dir),
            None => {
                if unsafe { TRACE } {
                    log(b"[supervisor] cgroup v2 unavailable; running uncapped\n");
                }
            }
        }
    }

    // Shared ring TABLE (one Ring per slot): MAP_SHARED|ANON so both processes see
    // the same pages after fork. The tail holds the SLOT_WORDS-word slot bitmap +
    // zygote signaling (ZygShared); then a [SlotStack; MAX_SLOTS] for the
    // per-trap-DEPTH ring lease (Part A). All in the SAME shared mapping so fork
    // children never diverge.
    let stacks_bytes = std::mem::size_of::<SlotStack>() * MAX_SLOTS;
    // SLOT_WORDS u64 bitmap words, then a 64-byte region for ZygShared (≤64 bytes).
    let tail_bytes = SLOT_WORDS * 8 + 64;
    let table_bytes = std::mem::size_of::<Ring>() * MAX_SLOTS + tail_bytes + stacks_bytes;
    let rings = unsafe {
        libc::mmap(
            std::ptr::null_mut(),
            table_bytes,
            libc::PROT_READ | libc::PROT_WRITE,
            libc::MAP_SHARED | libc::MAP_ANONYMOUS | libc::MAP_NORESERVE,
            -1,
            0,
        )
    };
    if rings == libc::MAP_FAILED {
        die(b"ring mmap failed\n");
    }
    unsafe {
        libc::madvise(rings, table_bytes, libc::MADV_DONTDUMP);
    }
    unsafe {
        RINGS = rings as *mut Ring;
        std::ptr::write_bytes(rings as *mut u8, 0, table_bytes);
        for i in 0..MAX_SLOTS as u64 {
            (*ring_at(i)).cancel_efd = -1;
            (*ring_at(i)).intr_efd = -1;
        }
        let tail = (rings as *mut u8).add(std::mem::size_of::<Ring>() * MAX_SLOTS);
        SLOT_BM = tail as *mut u64;
        *SLOT_BM = 1; // slot 0 (the main cell thread) is claimed (word 0, bit 0);
                      // the other SLOT_WORDS-1 bitmap words stay 0 from write_bytes above.
                      // Zygote signaling lives in the same shared page, after the bitmap.
        ZYG = tail.add(SLOT_WORDS * 8) as *mut ZygShared;
        // Per-base-slot trap-depth stacks live after the bitmap + ZygShared region
        // (already zeroed above ⇒ depth 0 everywhere ⇒ every slot starts at base).
        SLOT_STACKS = tail.add(tail_bytes) as *mut SlotStack;
        // INV-1: record the EXACT [base, base+table_bytes) extent of this whole
        // MAP_SHARED mapping (rings + SLOT_BM + ZYG + SLOT_STACKS) so vm_arg_ok rejects
        // any cell-supplied transfer VA overlapping it. Written once, pre-fork,
        // single-threaded; children inherit by value (same VA in every cell).
        SHARED_ZONE_LO.store(rings as u64, Ordering::Relaxed);
        SHARED_ZONE_HI.store(rings as u64 + table_bytes as u64, Ordering::Relaxed);
    }
    // Cross-cell shared-memory POOL: MAP_SHARED|ANON, created here (single-threaded,
    // PRE-fork) so every cell this supervisor forks inherits it at the SAME virtual
    // address — like the ring TABLE above. It backs guest `mmap(MAP_SHARED, fd)` so a
    // memfd/file shared between a parent and the children it forks/launches gets REAL
    // shared pages (Chromium's persistent_memory_allocator + mojo IPC). It is NOT in
    // the INV-1 forbidden zone: unlike the ring table, the guest legitimately reads/
    // writes its own shared memory here. Per-supervisor (so distinct tenants — distinct
    // supervisors — get distinct pools, no cross-tenant leak). Best-effort: on failure
    // SHM_POOL_BASE stays 0 and guest_mmap falls back to its private-copy path.
    unsafe {
        let pool = libc::mmap(
            std::ptr::null_mut(),
            SHM_POOL_SIZE as usize,
            libc::PROT_READ | libc::PROT_WRITE,
            libc::MAP_SHARED | libc::MAP_ANONYMOUS | libc::MAP_NORESERVE,
            -1,
            0,
        );
        if pool != libc::MAP_FAILED {
            libc::madvise(pool, SHM_POOL_SIZE as usize, libc::MADV_DONTDUMP);
            SHM_POOL_BASE.store(pool as u64, Ordering::Relaxed);
        }
    }
    if cfg.warm {
        WARM_MODE.store(true, Ordering::Relaxed);
    }
}

fn ensure_runtime_dirs_in_rootfs(root: &std::path::Path) {
    use std::os::unix::fs::PermissionsExt;

    let ensure_dir = |rel: &str, mode: u32| {
        let p = root.join(rel);
        if std::fs::create_dir_all(&p).is_ok() {
            let _ = std::fs::set_permissions(&p, std::fs::Permissions::from_mode(mode));
        }
    };

    // KVM/HVF init mounts tmpfs at /tmp and /run, then creates the X11/dbus
    // runtime dirs. Sentry serves the extracted rootfs directly, so recreate the
    // same baseline as real VM boot state in the writable rootfs.
    ensure_dir("tmp", 0o1777);
    ensure_dir("tmp/.X11-unix", 0o1777);
    if let Ok(entries) = std::fs::read_dir(root.join("tmp/.X11-unix")) {
        for entry in entries.flatten() {
            let _ = std::fs::remove_file(entry.path());
        }
    }
    if let Ok(entries) = std::fs::read_dir(root.join("tmp")) {
        for entry in entries.flatten() {
            let name = entry.file_name();
            let name = name.to_string_lossy();
            if name.starts_with(".X") && name.ends_with("-lock") {
                let _ = std::fs::remove_file(entry.path());
            }
        }
    }
    ensure_dir("run", 0o755);
    ensure_dir("run/dbus", 0o755);
    ensure_dir("var/run/dbus", 0o755);
}

/// Probe whether KVM is actually usable on this host — char device present AND
/// openable AND `KVM_CREATE_VM` succeeds. (The design-doc-corrected probe: never
/// trust `/dev/kvm`'s mere existence — see §9.) When this is false (a nested
/// guest with no hardware virt, a customer sandbox, …), the sentry is the backend.
pub fn kvm_usable() -> bool {
    use std::os::unix::fs::FileTypeExt;
    // 1. char device?
    match std::fs::metadata("/dev/kvm") {
        Ok(m) if m.file_type().is_char_device() => {}
        _ => return false,
    }
    // 2. openable read-write?
    let c = match std::ffi::CString::new("/dev/kvm") {
        Ok(c) => c,
        Err(_) => return false,
    };
    let fd = unsafe { libc::open(c.as_ptr(), libc::O_RDWR | libc::O_CLOEXEC) };
    if fd < 0 {
        return false;
    }
    // 3. KVM_CREATE_VM succeeds? (_IO(KVMIO, 0x01) = 0xAE01)
    const KVM_CREATE_VM: libc::c_ulong = 0xAE01;
    let vmfd = unsafe { libc::ioctl(fd, KVM_CREATE_VM, 0) };
    let ok = vmfd >= 0;
    if vmfd >= 0 {
        unsafe { libc::close(vmfd) };
    }
    unsafe { libc::close(fd) };
    ok
}
