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
150 lines
6.1 KiB
Bash
Executable File
150 lines
6.1 KiB
Bash
Executable File
#!/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
|