qemu and build: review fixes across the rig driver, boot script, and fetch helpers

Bounded waits and validated arguments in run.sh and ui-drive.sh, a seeded
settings directory and root-only staged rootfs permissions with their own
tests, qmp.py and imgtools.py hardening, the fetch scripts checking what they
download, and ASCII typography throughout. Each fix carries its test under
qemu/tests or tests/.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01N3G6m9Aw5RyVY4ZowtKzEj
This commit is contained in:
Noah
2026-09-09 19:17:54 -06:00
co-authored by Claude Fable 5.1
parent bda6c6c633
commit 2b6e8a2098
24 changed files with 1823 additions and 137 deletions
+149
View File
@@ -0,0 +1,149 @@
#!/usr/bin/env bash
# Offline regression test for run.sh's own argv construction: no real QEMU,
# no kernel image. A fake `qemu-system-arm` placed first on PATH dumps the
# argv it was handed (one token per line) and exits, so this pins the exact
# contract run.sh:121-138 and rootfs/sbin/init:54,161 share without either
# side moving: init decides ttyS0 vs ttyS1 for the Modbus alias purely by
# grepping warden.ctl off /proc/cmdline, so run.sh has to keep two promises
# every single invocation -- the ctl pci-serial device, when present, comes
# BEFORE the rs485 one in argv (virt's PCI bus enumerates in that order),
# and an rs485 pci-serial device (real or null-backed) is always there so
# the port count init relies on never shifts.
#
# What is worth pinning: nothing else exercises this. test-ui-drive-rs485.sh
# stubs run.sh out entirely (a fake VM), and the only real boot in CI
# (boot-smoke.sh) passes neither --ctl nor --rs485, so a swapped
# `[ -n "$CTL" ]`/`[ -n "$RS485" ]` block, or a dropped null-chardev
# fallback, would reach a panel as Modbus polls landing on the debug channel
# (run.sh:131-133's own incident) before anything here caught it.
#
# bash run-sh-args-test.sh
set -uo pipefail
HERE="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
RUN_SH="$HERE/../run.sh"
SCRATCH="$(mktemp -d /tmp/runshargs.XXXXXX)"
trap 'rm -rf "$SCRATCH"' EXIT
FAIL=0
pass() { printf '[PASS] %s\n' "$1"; }
fail() { printf '[FAIL] %s\n' "$1"; FAIL=1; }
KERNEL="$SCRATCH/fake-zImage"
INITRD="$SCRATCH/fake-initramfs.cpio.gz"
: > "$KERNEL"
: > "$INITRD"
BIN="$SCRATCH/bin"
mkdir -p "$BIN"
cat > "$BIN/qemu-system-arm" <<'STUB'
#!/usr/bin/env bash
# Stand-in for the real binary: record argv, one token per line, and exit
# straight away. $ARGV_CAPTURE names where -- run.sh always `exec`s this as
# its very last step, so nothing downstream of it ever runs.
printf '%s\n' "$@" > "$ARGV_CAPTURE"
STUB
chmod +x "$BIN/qemu-system-arm"
# run_case ARGV_FILE EXTRA_ARGS...: invoke the real run.sh --no-disk (so
# nothing under the real qemu/out/ is ever touched) with the fake binary
# first on PATH, capturing its argv into ARGV_FILE. Fails the case loudly if
# run.sh itself exits nonzero -- a silent empty capture would otherwise look
# just like "the assertions below simply found nothing".
run_case() {
local argv_file="$1"; shift
local out rc
out="$(cd "$SCRATCH" && PATH="$BIN:$PATH" ARGV_CAPTURE="$argv_file" \
bash "$RUN_SH" --kernel "$KERNEL" --initrd "$INITRD" --no-disk "$@" 2>&1)"
rc=$?
[ "$rc" -eq 0 ] || { fail "run.sh exited $rc for: $* -- output: $out"; return 1; }
[ -s "$argv_file" ] || { fail "run.sh produced no captured argv for: $*"; return 1; }
return 0
}
# chardev_line ARGV_FILE PREFIX: 1-indexed line number of the first argv
# token starting with PREFIX (the socket/null chardev spec, which always
# immediately follows the "-chardev" token it belongs to), or empty.
chardev_line() { grep -n -m1 "^$2" "$1" | cut -d: -f1; }
# --- case: neither --ctl nor --rs485 -> null-backed rs485, no ctl device ---
argv="$SCRATCH/argv-neither.txt"
if run_case "$argv"; then
if ! grep -qF 'id=ctl' "$argv"; then
pass "neither flag: no ctl chardev/device at all"
else
fail "neither flag: a ctl chardev/device appeared unrequested"
fi
if grep -qF -- '-append' "$argv" && ! grep -qw 'warden.ctl' "$argv"; then
pass "neither flag: -append omits warden.ctl"
else
fail "neither flag: -append should omit warden.ctl"
fi
if [ -n "$(chardev_line "$argv" 'null,id=rs485')" ] \
&& grep -qF 'pci-serial,chardev=rs485' "$argv"; then
pass "neither flag: null-backed rs485 pci-serial device is still present"
else
fail "neither flag: expected a null-backed rs485 device (port count must not shift)"
fi
fi
# --- case: --rs485 alone -> real rs485 device, still no ctl device ---------
argv="$SCRATCH/argv-rs485-only.txt"
if run_case "$argv" --rs485 "$SCRATCH/rs.sock"; then
if ! grep -qF 'id=ctl' "$argv"; then
pass "rs485 only: no ctl chardev/device"
else
fail "rs485 only: a ctl chardev/device appeared unrequested"
fi
if ! grep -qw 'warden.ctl' "$argv"; then
pass "rs485 only: -append omits warden.ctl"
else
fail "rs485 only: -append should omit warden.ctl"
fi
if [ -n "$(chardev_line "$argv" "socket,id=rs485,path=$SCRATCH/rs.sock,")" ]; then
pass "rs485 only: rs485 chardev carries the requested socket path"
else
fail "rs485 only: rs485 chardev did not carry the requested socket path"
fi
fi
# --- case: --ctl alone -> ctl device first, null-backed rs485 still present,
# and warden.ctl on the cmdline ------------------------------------------
argv="$SCRATCH/argv-ctl-only.txt"
if run_case "$argv" --ctl "$SCRATCH/ctl.sock"; then
ctl_ln="$(chardev_line "$argv" "socket,id=ctl,path=$SCRATCH/ctl.sock,")"
rs_ln="$(chardev_line "$argv" 'null,id=rs485')"
if [ -n "$ctl_ln" ] && [ -n "$rs_ln" ] && [ "$ctl_ln" -lt "$rs_ln" ]; then
pass "ctl only: ctl chardev (line $ctl_ln) precedes the null rs485 chardev (line $rs_ln)"
else
fail "ctl only: expected ctl chardev before a null-backed rs485 chardev, got ctl=$ctl_ln rs485=$rs_ln"
fi
if grep -qw 'warden.ctl' "$argv"; then
pass "ctl only: -append carries warden.ctl"
else
fail "ctl only: -append should carry warden.ctl"
fi
fi
# --- case: --ctl and --rs485 together -> ctl device still enumerates first -
argv="$SCRATCH/argv-both.txt"
if run_case "$argv" --ctl "$SCRATCH/ctl.sock" --rs485 "$SCRATCH/rs.sock"; then
ctl_ln="$(chardev_line "$argv" "socket,id=ctl,path=$SCRATCH/ctl.sock,")"
rs_ln="$(chardev_line "$argv" "socket,id=rs485,path=$SCRATCH/rs.sock,")"
if [ -n "$ctl_ln" ] && [ -n "$rs_ln" ] && [ "$ctl_ln" -lt "$rs_ln" ]; then
pass "both flags: ctl chardev (line $ctl_ln) precedes the rs485 chardev (line $rs_ln)"
else
fail "both flags: expected ctl chardev before rs485 chardev, got ctl=$ctl_ln rs485=$rs_ln"
fi
if grep -qw 'warden.ctl' "$argv"; then
pass "both flags: -append carries warden.ctl"
else
fail "both flags: -append should carry warden.ctl"
fi
fi
if [ "$FAIL" -eq 0 ]; then
echo "ALL RUN.SH ARGV TESTS PASSED"
exit 0
else
echo "RUN.SH ARGV TESTS FAILED"
exit 1
fi