research(fspy): evaluate SIGSYS interception #19
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| name: fspy SIGSYS research | |
| permissions: | |
| contents: read | |
| on: | |
| workflow_dispatch: | |
| pull_request: | |
| paths: | |
| - '.github/workflows/fspy-sigsys-research.yml' | |
| - 'crates/vite_task_bin/tests/e2e_snapshots/fixtures/vitest_browser_cache/**' | |
| - 'research/ptrace-exec-prototype/**' | |
| - 'research/sigsys-prototype/**' | |
| defaults: | |
| run: | |
| shell: bash | |
| jobs: | |
| native-x86: | |
| name: Native x86-64 | |
| runs-on: ubuntu-24.04 | |
| steps: | |
| - uses: taiki-e/checkout-action@7d1e50e93dc4fb3bba58f85018fadf77898aee8b # v1.4.2 | |
| - name: Run syscall and exec prototypes | |
| run: | | |
| set -euxo pipefail | |
| test "$(uname -m)" = x86_64 | |
| probe_dir=$(mktemp -d) | |
| cp -a research/sigsys-prototype "$probe_dir/sigsys" | |
| cp -a research/ptrace-exec-prototype "$probe_dir/ptrace" | |
| cd "$probe_dir/sigsys" | |
| cc -O2 -Wall -Wextra -Werror -pthread trap_bench.c -o trap_bench | |
| ./trap_bench | |
| cc -O2 -Wall -Wextra -Werror unotify_bench.c -o unotify_bench | |
| ./unotify_bench | |
| cc -O2 -Wall -Wextra -Werror reexec_bootstrap.c -o reexec_bootstrap | |
| ./reexec_bootstrap | |
| cd "$probe_dir/ptrace" | |
| make check | |
| timeout 30s ./recursive_injector /bin/true | |
| cc -O2 -Wall -Wextra -Werror -pthread -std=gnu11 \ | |
| nonleader_exec.c -o nonleader_exec | |
| ./nonleader_exec /bin/true | |
| vitest-browser-x86: | |
| name: Sandboxed Vitest browser under transient ptrace | |
| runs-on: ubuntu-24.04 | |
| timeout-minutes: 15 | |
| steps: | |
| - uses: taiki-e/checkout-action@7d1e50e93dc4fb3bba58f85018fadf77898aee8b # v1.4.2 | |
| - uses: oxc-project/setup-node@4c588e9266bd930b6ddc34307df0659ed511d187 # v1.3.1 | |
| - name: Install Chromium system dependencies | |
| run: pnpm --filter vite-task-tools exec playwright install --with-deps chromium | |
| - name: Enable Chromium's user-namespace sandbox | |
| run: | | |
| set -euxo pipefail | |
| sysctl kernel.unprivileged_userns_clone | |
| sysctl kernel.apparmor_restrict_unprivileged_userns | |
| sudo sysctl -w kernel.apparmor_restrict_unprivileged_userns=0 | |
| - name: Verify the CI host can launch sandboxed Chromium | |
| run: | | |
| set -euxo pipefail | |
| sandbox_control=' | |
| const { chromium } = await import("playwright"); | |
| const browser = await chromium.launch({ | |
| headless: true, | |
| chromiumSandbox: true, | |
| }); | |
| const page = await browser.newPage(); | |
| await page.setContent("<p>sandbox control</p>"); | |
| await browser.close(); | |
| ' | |
| pnpm --filter vite-task-tools exec node --input-type=module \ | |
| --eval "$sandbox_control" | |
| setpriv --no-new-privs \ | |
| pnpm --filter vite-task-tools exec node --input-type=module \ | |
| --eval "$sandbox_control" | |
| - name: Run sandboxed Vitest browser mode through the exec bridge | |
| run: | | |
| set -euxo pipefail | |
| test "$(uname -m)" = x86_64 | |
| make -C research/ptrace-exec-prototype recursive_injector | |
| probe_dir=$(mktemp -d) | |
| cp -a crates/vite_task_bin/tests/e2e_snapshots/fixtures/vitest_browser_cache \ | |
| "$probe_dir/fixture" | |
| ln -s "$PWD/packages/tools/node_modules" "$probe_dir/node_modules" | |
| injector="$PWD/research/ptrace-exec-prototype/recursive_injector" | |
| vitest="$PWD/packages/tools/node_modules/.bin/vitest" | |
| cd "$probe_dir/fixture" | |
| sandbox_record="$probe_dir/chromium-sandbox.txt" | |
| no_sandbox_record="$probe_dir/chromium-no-sandbox.txt" | |
| ( | |
| set +x | |
| while :; do | |
| for cmdline_file in /proc/[0-9]*/cmdline; do | |
| test -r "$cmdline_file" || continue | |
| command_line=$(tr '\0' ' ' < "$cmdline_file" 2>/dev/null || true) | |
| case "$command_line" in | |
| *chrome-headless-shell*) ;; | |
| *) continue ;; | |
| esac | |
| case "$command_line" in | |
| *--no-sandbox*) | |
| printf '%s\n' "$command_line" > "$no_sandbox_record" | |
| ;; | |
| esac | |
| case "$command_line" in | |
| *--type=renderer*) ;; | |
| *) continue ;; | |
| esac | |
| pid=${cmdline_file#/proc/} | |
| pid=${pid%/cmdline} | |
| filters=$(awk '/^Seccomp_filters:/ { print $2 }' \ | |
| "/proc/$pid/status" 2>/dev/null || true) | |
| if test "${filters:-0}" -ge 2; then | |
| { | |
| printf 'pid=%s\ncmdline=%s\n' "$pid" "$command_line" | |
| awk '/^(NoNewPrivs|Seccomp|Seccomp_filters):/ { print }' \ | |
| "/proc/$pid/status" 2>/dev/null || true | |
| } > "$sandbox_record" | |
| exit 0 | |
| fi | |
| done | |
| sleep 0.02 | |
| done | |
| ) & | |
| observer_pid=$! | |
| set +e | |
| VITEST_CHROMIUM_SANDBOX=true \ | |
| DEBUG=vitest:browser:playwright,pw:browser \ | |
| timeout 120s "$injector" "$vitest" run \ | |
| 2>&1 | tee "$probe_dir/bridge.log" | |
| run_status=${PIPESTATUS[0]} | |
| set -e | |
| kill "$observer_pid" 2>/dev/null || true | |
| wait "$observer_pid" 2>/dev/null || true | |
| if test "$run_status" -ne 0; then | |
| sudo dmesg | tail -200 || true | |
| fi | |
| test "$run_status" -eq 0 | |
| test -s dist/result.json | |
| grep -F '"success":true' dist/result.json | |
| grep -F 'chromiumSandbox: true' "$probe_dir/bridge.log" | |
| test ! -s "$no_sandbox_record" | |
| test -s "$sandbox_record" | |
| grep -F $'NoNewPrivs:\t1' "$sandbox_record" | |
| grep -F $'Seccomp:\t2' "$sandbox_record" | |
| grep -E $'Seccomp_filters:\t([2-9]|[1-9][0-9]+)' "$sandbox_record" | |
| cat "$sandbox_record" | |
| grep -E 'bridge: injected exec .*exe=.*/(chrome|headless_shell)' \ | |
| "$probe_dir/bridge.log" | |
| grep -E 'bridge: summary injected_execs=[2-9][0-9]* failed_execs=' \ | |
| "$probe_dir/bridge.log" | |
| docker-x86: | |
| name: Docker default seccomp | |
| runs-on: ubuntu-24.04 | |
| steps: | |
| - uses: taiki-e/checkout-action@7d1e50e93dc4fb3bba58f85018fadf77898aee8b # v1.4.2 | |
| - name: Run under an existing filter with no-new-privileges | |
| run: | | |
| set -euxo pipefail | |
| docker run --rm \ | |
| --security-opt no-new-privileges \ | |
| -v "$PWD/research:/src:ro" \ | |
| gcc:14-bookworm \ | |
| sh -eux -c ' | |
| grep Seccomp /proc/self/status | |
| grep NoNewPrivs /proc/self/status | |
| cat /proc/self/attr/current || true | |
| mkdir /tmp/sigsys /tmp/ptrace | |
| cp /src/sigsys-prototype/trap_bench.c /tmp/sigsys/ | |
| cp /src/sigsys-prototype/unotify_bench.c /tmp/sigsys/ | |
| cp /src/sigsys-prototype/reexec_bootstrap.c /tmp/sigsys/ | |
| cp /src/ptrace-exec-prototype/injector.c /tmp/ptrace/ | |
| cp /src/ptrace-exec-prototype/nested_trap.c /tmp/ptrace/ | |
| cp /src/ptrace-exec-prototype/recursive_injector.c /tmp/ptrace/ | |
| cp /src/ptrace-exec-prototype/target.c /tmp/ptrace/ | |
| cp /src/ptrace-exec-prototype/Makefile /tmp/ptrace/ | |
| cd /tmp/sigsys | |
| cc -O2 -Wall -Wextra -Werror -pthread trap_bench.c -o trap_bench | |
| ./trap_bench | |
| cc -O2 -Wall -Wextra -Werror unotify_bench.c -o unotify_bench | |
| ./unotify_bench | |
| cc -O2 -Wall -Wextra -Werror reexec_bootstrap.c -o reexec_bootstrap | |
| ./reexec_bootstrap | |
| cd /tmp/ptrace | |
| make check | |
| timeout 30s ./recursive_injector /bin/true | |
| ' |