# shellcheck shell=bash # C2-14 (#1728, row 227 R3): the sandbox root of scripts/test-case2.sh and the cleanup that hangs # off it. test-case2.sh sources this file before it allocates anything. The kill sweep and the # removal each check the root themselves, so a caller that runs them early, late or with an empty # value gets a refusal, never a sweep of the host. scripts/case2/sandbox-check.sh exercises the # guard and the match against dummy symlinks. # The tools socket is the longest Unix socket path a run builds. packages/seat/src/session.mjs # newRun puts it at /launches//r<12 hex digits>/tools.sock and refuses a path # over SOCKET_LIMIT bytes. The driver's data root is /root/data (fixture() in # packages/cli/tests/helpers.mjs, given /root by test-case2.sh) and its business is acme # (scripts/case2/e2e.mjs). socket_shape builds that path on a real sandbox root with a run id of the # same length, so its byte length is the length the run will have. SOCKET_LIMIT=107 socket_shape() { printf '%s/root/data/launches/acme/r%s/tools.sock' "$1" 000000000000; } # The length of $1 in bytes, not characters, whatever the caller's locale. byte_length() { local LC_ALL=C printf '%s' "${#1}" } # The device:inode of $1, the identity recorded when mktemp creates the sandbox. sandbox_id() { stat -c '%d:%i' -- "$1" 2>/dev/null; } # sandbox_ok ROOT ID: ROOT is the sandbox this run created. It is non-empty, absolute and not /; # it is its own physical path (no symlink in it, no trailing slash, no . or .. part), which is the # form a /proc//cwd link reads; it is a directory this user owns; and its device:inode is ID. sandbox_ok() { local root=${1-} id=${2-} physical [ -n "$root" ] && [ -n "$id" ] || return 1 case "$root" in /?*) ;; *) return 1 ;; esac [ -d "$root" ] && [ ! -L "$root" ] && [ -O "$root" ] || return 1 physical=$(cd -P -- "$root" 2>/dev/null && pwd -P) || return 1 [ "$physical" = "$root" ] || return 1 [ "$(sandbox_id "$root")" = "$id" ] } # dir_empty DIR: DIR holds no entry, dot entries included. dir_empty() ( shopt -s nullglob dotglob set -- "$1"/* [ "$#" -eq 0 ] ) # cwd_in ROOT TARGET: TARGET, a cwd link target, is ROOT or lies under ROOT/. The two are compared # as strings. Quoted text in a case pattern matches only itself, so *, ? and [ in ROOT are literal; # the only pattern character is the /* written here. A cwd in a deleted directory reads # " (deleted)". One under ROOT/ still matches. "ROOT (deleted)" does not: the sweep runs only # after sandbox_ok has shown ROOT exists, so that target is another directory, one whose name ends # in " (deleted)". cwd_in() { case "${1-}" in /?*) ;; *) return 1 ;; esac case "${2-}" in "$1" | "$1"/*) return 0 ;; esac return 1 } # cwd_links_in ROOT LINK...: print, each followed by NUL, every LINK whose target cwd_in puts in # ROOT. One find reads every target (%l), so the cost is one process however many the host runs. A # link this user may not read, another user's process, gives an empty target, which never matches. # Each record is LINK, a tab, then TARGET; LINK has no tab and TARGET may hold anything but NUL. cwd_links_in() { local root=$1 record shift [ "$#" -gt 0 ] || return 0 while IFS= read -r -d '' record; do if cwd_in "$root" "${record#*$'\t'}"; then printf '%s\0' "${record%%$'\t'*}"; fi done < <(command find "$@" -maxdepth 0 -printf '%p\t%l\0' 2>/dev/null) } # link_pid LINK: the pid in a link of the form /proc//cwd, or failure for anything else. link_pid() { local pid=${1#/proc/} [ "$pid" != "$1" ] || return 1 pid=${pid%/cwd} case "$pid" in '' | *[!0-9]*) return 1 ;; esac printf '%s' "$pid" } # sweep_sandbox ROOT ID: SIGKILL every process whose cwd is in ROOT. It refuses, before it reads any # link, unless sandbox_ok ROOT ID. Up to three passes, so a child forked during one pass is caught # by the next. Prints a note when it killed something; returns 1 when it refused. sweep_sandbox() { local root=${1-} id=${2-} link pid pass hit local -A killed=() if ! sandbox_ok "$root" "$id"; then printf 'test-case2: kill sweep refused: %q is not the sandbox this run created\n' "$root" >&2 return 1 fi for pass in 1 2 3; do hit=0 while IFS= read -r -d '' link; do pid=$(link_pid "$link") || continue if kill -KILL "$pid" 2>/dev/null; then hit=1; killed[$pid]=1; fi done < <(cwd_links_in "$root" /proc/[0-9]*/cwd) [ "$hit" = 1 ] || break done if [ "${#killed[@]}" -gt 0 ]; then echo "note: ${#killed[@]} process(es) under the sandbox outlived the driver; sent SIGKILL" fi return 0 } # remove_sandbox ROOT ID: remove ROOT, refusing unless sandbox_ok ROOT ID. --one-file-system keeps # rm out of any mount left under it. remove_sandbox() { local root=${1-} id=${2-} if ! sandbox_ok "$root" "$id"; then printf 'test-case2: removal refused: %q is not the sandbox this run created\n' "$root" >&2 return 1 fi rm -rf --one-file-system -- "$root" }