229 lines
6.8 KiB
Bash
Executable File
229 lines
6.8 KiB
Bash
Executable File
#!/bin/sh
|
|
set -eu
|
|
|
|
dut=${1:?DUT path is required}
|
|
freebsd_src=${2:?FreeBSD source path is required}
|
|
base=${3:?base image is required}
|
|
askpass=${4:?askpass path is required}
|
|
output=${5:?output path is required}
|
|
protected_pid=26318
|
|
protected_port=9222
|
|
builder=$dut/tests/pre15/fixtures/B12-build-kld.sh
|
|
|
|
test ! -e "$output" || {
|
|
printf '%s\n' "B12 QEMU output exists: $output" >&2
|
|
exit 20
|
|
}
|
|
mkdir -p "$output"
|
|
work=$(mktemp -d /tmp/pre15-b12-qemu.XXXXXX)
|
|
overlay=$work/guest-overlay.qcow2
|
|
module=$work/B12-erofs-zstdio0.ko
|
|
qemu_pid=
|
|
port=
|
|
guest_ready=0
|
|
module_loaded=0
|
|
|
|
protected_start=$(awk '{print $22}' /proc/$protected_pid/stat)
|
|
protected_cmd=$(sha256sum /proc/$protected_pid/cmdline | awk '{print $1}')
|
|
stat -c 'path=%n size=%s inode=%i mode=%f mtime=%Y ctime=%Z' "$base" \
|
|
>"$output/base-metadata-before.txt"
|
|
|
|
ssh_options()
|
|
{
|
|
printf '%s\n' \
|
|
-q \
|
|
-o BatchMode=no \
|
|
-o PubkeyAuthentication=no \
|
|
-o PreferredAuthentications=keyboard-interactive,password \
|
|
-o NumberOfPasswordPrompts=1 \
|
|
-o StrictHostKeyChecking=no \
|
|
-o UserKnownHostsFile=/dev/null \
|
|
-o ConnectTimeout=5 \
|
|
-p "$port"
|
|
}
|
|
|
|
guest()
|
|
{
|
|
timeout -k 5 "${B12_GUEST_TIMEOUT:-60}" env DISPLAY=:0 \
|
|
SSH_ASKPASS="$askpass" SSH_ASKPASS_REQUIRE=force \
|
|
ssh $(ssh_options) [email protected] "$@"
|
|
}
|
|
|
|
copy_to_guest()
|
|
{
|
|
timeout -k 5 "${B12_COPY_TIMEOUT:-120}" env DISPLAY=:0 \
|
|
SSH_ASKPASS="$askpass" SSH_ASKPASS_REQUIRE=force \
|
|
scp -O -q \
|
|
-o BatchMode=no \
|
|
-o PubkeyAuthentication=no \
|
|
-o PreferredAuthentications=keyboard-interactive,password \
|
|
-o NumberOfPasswordPrompts=1 \
|
|
-o StrictHostKeyChecking=no \
|
|
-o UserKnownHostsFile=/dev/null \
|
|
-o ConnectTimeout=5 \
|
|
-P "$port" "$1" [email protected]:"$2"
|
|
}
|
|
|
|
port_free()
|
|
{
|
|
python3 - "$1" <<'PY'
|
|
import socket
|
|
import sys
|
|
|
|
with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as sock:
|
|
sock.settimeout(0.2)
|
|
raise SystemExit(0 if sock.connect_ex(("127.0.0.1", int(sys.argv[1]))) != 0 else 1)
|
|
PY
|
|
}
|
|
|
|
cleanup()
|
|
{
|
|
set +e
|
|
if test "$guest_ready" = 1 && test "$module_loaded" = 1; then
|
|
guest kldunload /root/B12-erofs-zstdio0.ko \
|
|
>>"$output/cleanup.log" 2>&1
|
|
module_loaded=0
|
|
fi
|
|
if test -n "$qemu_pid" && kill -0 "$qemu_pid" 2>/dev/null; then
|
|
kill -TERM "$qemu_pid" 2>/dev/null
|
|
wait_count=0
|
|
while kill -0 "$qemu_pid" 2>/dev/null && test "$wait_count" -lt 20; do
|
|
sleep 1
|
|
wait_count=$((wait_count + 1))
|
|
done
|
|
if kill -0 "$qemu_pid" 2>/dev/null; then
|
|
kill -KILL "$qemu_pid" 2>/dev/null
|
|
fi
|
|
wait "$qemu_pid" 2>/dev/null
|
|
fi
|
|
rm -rf "$work"
|
|
}
|
|
trap cleanup EXIT HUP INT TERM
|
|
|
|
for tool in awk clang file nm python3 qemu-img qemu-system-x86_64 scp \
|
|
sha256sum ssh stat timeout; do
|
|
command -v "$tool" >/dev/null 2>&1 || {
|
|
printf '%s\n' "missing B12 QEMU tool: $tool" >&2
|
|
exit 21
|
|
}
|
|
done
|
|
test -d "$freebsd_src/sys"
|
|
test -f "$base"
|
|
test -x "$askpass"
|
|
test -r /proc/$protected_pid/stat
|
|
|
|
timeout -k 10 600 /bin/sh "$builder" "$dut" "$freebsd_src" "$module" \
|
|
"$work/kld-work" >"$output/build.stdout" 2>"$output/build.stderr"
|
|
file "$module" >"$output/module.file"
|
|
sha256sum "$module" >"$output/module.sha256"
|
|
nm -g "$module" | LC_ALL=C sort >"$output/module.nm-global"
|
|
nm -u "$module" | LC_ALL=C sort >"$output/module.nm-u"
|
|
|
|
qemu-img create -q -f qcow2 -F qcow2 -b "$base" "$overlay"
|
|
port=$(python3 - "$protected_port" <<'PY'
|
|
import socket
|
|
import sys
|
|
|
|
protected = int(sys.argv[1])
|
|
while True:
|
|
with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as sock:
|
|
sock.bind(("127.0.0.1", 0))
|
|
port = sock.getsockname()[1]
|
|
if port != protected:
|
|
print(port)
|
|
break
|
|
PY
|
|
)
|
|
printf '%s\n' "$port" >"$output/owned-port.txt"
|
|
qemu-system-x86_64 -accel tcg,thread=multi -cpu qemu64 \
|
|
-m 3072 -smp 2 \
|
|
-drive "file=$overlay,if=virtio,format=qcow2,cache=none" \
|
|
-netdev "user,id=net0,hostfwd=tcp:127.0.0.1:$port-:22" \
|
|
-device virtio-net-pci,netdev=net0 -display none \
|
|
-serial "file:$output/serial.log" -monitor none \
|
|
-D "$output/qemu.log" >"$output/qemu-process.log" 2>&1 &
|
|
qemu_pid=$!
|
|
printf '%s\n' "$qemu_pid" >"$output/owned-pid.txt"
|
|
printf '%s\n' \
|
|
"qemu-system-x86_64 -m 3072 -smp 2 -drive owned-overlay -port $port" \
|
|
>"$output/qemu-argv-summary.txt"
|
|
|
|
boot_wait=0
|
|
while test "$boot_wait" -lt "${B12_BOOT_TIMEOUT:-300}"; do
|
|
if ! kill -0 "$qemu_pid" 2>/dev/null; then
|
|
printf '%s\n' 'owned QEMU exited before SSH readiness' >&2
|
|
exit 21
|
|
fi
|
|
if B12_GUEST_TIMEOUT=10 guest true >/dev/null 2>&1; then
|
|
guest_ready=1
|
|
break
|
|
fi
|
|
sleep 2
|
|
boot_wait=$((boot_wait + 2))
|
|
done
|
|
test "$guest_ready" = 1 || {
|
|
printf '%s\n' 'owned QEMU SSH boot deadline expired' >&2
|
|
exit 21
|
|
}
|
|
|
|
guest uname -a >"$output/guest-uname.txt"
|
|
guest sysctl -n kern.osreldate >"$output/guest-osreldate.txt"
|
|
test "$(tr -d '\r\n' < "$output/guest-osreldate.txt")" = 1500068 || {
|
|
printf '%s\n' 'guest OSREL is not exact FreeBSD 15 ABI 1500068' >&2
|
|
exit 10
|
|
}
|
|
if guest kldstat -q -m erofs >/dev/null 2>&1; then
|
|
printf '%s\n' 'guest already has an EROFS module loaded' >&2
|
|
exit 21
|
|
fi
|
|
copy_to_guest "$module" /root/B12-erofs-zstdio0.ko
|
|
guest sha256 -q /root/B12-erofs-zstdio0.ko >"$output/guest-module.sha256"
|
|
test "$(awk '{print $1}' "$output/module.sha256")" = \
|
|
"$(tr -d '\r\n' < "$output/guest-module.sha256")"
|
|
guest dmesg >"$output/dmesg-before.txt"
|
|
guest kldload /root/B12-erofs-zstdio0.ko >"$output/kldload.stdout" \
|
|
2>"$output/kldload.stderr"
|
|
module_loaded=1
|
|
guest kldstat -n B12-erofs-zstdio0.ko >"$output/kldstat-loaded.txt"
|
|
guest kldunload /root/B12-erofs-zstdio0.ko >"$output/kldunload.stdout" \
|
|
2>"$output/kldunload.stderr"
|
|
module_loaded=0
|
|
guest dmesg >"$output/dmesg-after.txt"
|
|
diff -u "$output/dmesg-before.txt" "$output/dmesg-after.txt" \
|
|
>"$output/dmesg.diff" || true
|
|
sed -n '/^+++ /d; /^+/s/^+//p' "$output/dmesg.diff" \
|
|
>"$output/dmesg-added.txt"
|
|
if grep -Eqi 'panic:|lock order reversal|witness.*warning|use-after-free|link_elf_obj: symbol .* undefined' \
|
|
"$output/dmesg-added.txt"; then
|
|
printf '%s\n' 'B12 exact-ABI load produced a kernel diagnostic' >&2
|
|
exit 10
|
|
fi
|
|
|
|
cleanup
|
|
trap - EXIT HUP INT TERM
|
|
stat -c 'path=%n size=%s inode=%i mode=%f mtime=%Y ctime=%Z' "$base" \
|
|
>"$output/base-metadata-after.txt"
|
|
cmp "$output/base-metadata-before.txt" "$output/base-metadata-after.txt"
|
|
test -r /proc/$protected_pid/stat
|
|
test "$(awk '{print $22}' /proc/$protected_pid/stat)" = "$protected_start"
|
|
test "$(sha256sum /proc/$protected_pid/cmdline | awk '{print $1}')" = "$protected_cmd"
|
|
port_free "$port"
|
|
cat >"$output/result.json" <<EOF
|
|
{
|
|
"candidate": "P15-087",
|
|
"exact_abi_osreldate": 1500068,
|
|
"kld_load": "PASS",
|
|
"kld_unload": "PASS",
|
|
"kernel_log": "PASS",
|
|
"owned_overlay_removed": true,
|
|
"owned_pid_stopped": true,
|
|
"owned_port": $port,
|
|
"owned_port_free_after": true,
|
|
"protected_pid": $protected_pid,
|
|
"protected_port": $protected_port,
|
|
"schema": 1,
|
|
"status": "PASS"
|
|
}
|
|
EOF
|
|
printf '%s\n' 'B12 exact-ABI QEMU load/unload PASS'
|