aboutsummaryrefslogtreecommitdiff
path: root/tests/config
diff options
context:
space:
mode:
authorLena <lena@omega>2026-08-01 00:00:00 +0000
committerLena <lena@omega>2026-08-01 00:00:00 +0000
commitc235cb62beefd03087e7cfc7f7f55f0c7c6601c9 (patch)
tree2776f86d9226d55c78e3d735764fcd9ecbe5e08e /tests/config
downloadvmm-c235cb62beefd03087e7cfc7f7f55f0c7c6601c9.tar.gz
Enter vmmHEADmaster
A QEMU/KVM virtual machine manager in one POSIX shell script. Each VM is a directory of plain files under $VMMDIR, configured by a hand-edited KEY=VALUE file that is parsed rather than sourced, so nothing in it can inject a raw QEMU argument. Control is a QMP FIFO pair inside that directory, guarded by its permissions alone; nothing binds a TCP port. The tests are TAP without a framework and boot real guests where the host allows.
Diffstat (limited to 'tests/config')
-rw-r--r--tests/config187
1 files changed, 187 insertions, 0 deletions
diff --git a/tests/config b/tests/config
new file mode 100644
index 0000000..2229bf5
--- /dev/null
+++ b/tests/config
@@ -0,0 +1,187 @@
+# The config parser, against hostile input. dryrun is the parser's only
+# observable behaviour, so every case runs through it.
+
+"$VMM" create cfg1 64M >/dev/null 2>&1
+
+: > "$VMMDIR/cfg1/config"
+exits 0 "minimal config parses" dryrun cfg1
+
+cfg cfg1 <<EOF
+CPUS=2 # two of them
+EOF
+refuses 'config:1' "inline comment is rejected by line" dryrun cfg1
+
+cfg cfg1 <<EOF
+MEMORY=4096
+EOF
+refuses 'unknown key' "a typo'd key is an error, not silence" dryrun cfg1
+
+cfg cfg1 <<EOF
+CPUS=1
+CPUS=2
+EOF
+refuses 'duplicate key' "duplicate keys are an error" dryrun cfg1
+
+cfg cfg1 <<EOF
+IMAGE_FORMAT="qcow2,readonly=on"
+EOF
+refuses 'IMAGE_FORMAT must be' "IMAGE_FORMAT injection is refused" dryrun cfg1
+
+cfg cfg1 <<EOF
+GRAPHICS=bogus
+EOF
+refuses 'GRAPHICS must be' "an invalid GRAPHICS value is refused" dryrun cfg1
+
+cfg cfg1 <<EOF
+CPUS=abc
+EOF
+refuses 'CPUS must be' "non-numeric CPUS is refused" dryrun cfg1
+
+cfg cfg1 <<EOF
+HOSTFWD="70000:22"
+EOF
+refuses 'out of range' "out of range host port is refused" dryrun cfg1
+
+cfg cfg1 <<EOF
+HOSTFWD="0:22"
+EOF
+refuses 'out of range' "host port 0 is refused" dryrun cfg1
+
+cfg cfg1 <<EOF
+HOSTFWD="99999999999999999999:22"
+EOF
+refuses 'out of range' "a port too big for arithmetic is refused" dryrun cfg1
+
+cfg cfg1 <<EOF
+HOSTFWD=",,,"
+EOF
+refuses 'no usable entries' "a HOSTFWD of only separators is refused" dryrun cfg1
+
+# Only the dotted quad binds what it looks like: inet_aton reads 127.1
+# as 127.0.0.1 and 010.0.0.1 as octal for 8.0.0.1, and a host name is
+# not an address at all.
+cfg cfg1 <<EOF
+HOSTFWD="localhost:2222:22"
+EOF
+refuses 'must be A.B.C.D' "a host name in HOSTFWD is refused" dryrun cfg1
+
+cfg cfg1 <<EOF
+HOSTFWD="127.0.0.256:2222:22"
+EOF
+refuses 'must be A.B.C.D' "an octet out of range is refused" dryrun cfg1
+
+cfg cfg1 <<EOF
+HOSTFWD="127.1:2222:22"
+EOF
+refuses 'must be A.B.C.D' "a short form address is refused" dryrun cfg1
+
+cfg cfg1 <<EOF
+HOSTFWD="010.0.0.1:2222:22"
+EOF
+refuses 'must be A.B.C.D' "an octal octet is refused" dryrun cfg1
+
+cfg cfg1 <<EOF
+HOSTFWD=":2222:22"
+EOF
+refuses 'must be A.B.C.D' "an entry with no address at all is refused" dryrun cfg1
+
+# The address check runs inside the loop over entries and keeps its own
+# scratch names, since the shell has no others to give it.
+cfg cfg1 <<EOF
+HOSTFWD="2222:22,localhost:8080:80"
+EOF
+refuses 'must be A.B.C.D' "a bad address in a later entry is refused" dryrun cfg1
+
+# A forward the guest cannot receive is a config that lies, not a
+# default. A restricted NIC is still a NIC.
+cfg cfg1 <<EOF
+NETWORK=no
+HOSTFWD="2222:22"
+EOF
+refuses 'nothing to forward' "HOSTFWD without a NIC is refused" dryrun cfg1
+
+cfg cfg1 <<EOF
+NETWORK=hostonly
+HOSTFWD="2222:22"
+EOF
+exits 0 "HOSTFWD with a restricted NIC is kept" dryrun cfg1
+
+cfg cfg1 <<EOF
+BOOT_ORDER=cc
+EOF
+refuses 'BOOT_ORDER must be' "a repeated BOOT_ORDER letter is refused" dryrun cfg1
+
+# A boot list restricts nothing unless some device it names exists: with
+# no bootindex anywhere, QEMU falls back and boots the disk the list
+# left out.
+cfg cfg1 <<EOF
+BOOT_ORDER=d
+EOF
+refuses 'no device to boot' "a boot list of absent devices is refused" dryrun cfg1
+
+cfg cfg1 <<EOF
+BOOT_ORDER=n
+NETWORK=no
+EOF
+refuses 'no device to boot' "netboot without a NIC is refused" dryrun cfg1
+
+cfg cfg1 <<EOF
+BOOT_ORDER=dc
+EOF
+exits 0 "a boot list that still names the disk is kept" dryrun cfg1
+
+cfg cfg1 <<EOF
+IMAGE="$WORK/shared.qcow2"
+EOF
+refuses 'unknown key: IMAGE' "the primary disk path cannot be configured" dryrun cfg1
+
+cfg cfg1 <<EOF
+CDROM="/tmp/install.iso
+EOF
+refuses 'unbalanced quote' "unbalanced quote is refused" dryrun cfg1
+
+cfg cfg1 <<EOF
+CPUS=$(printf '2\001')
+EOF
+refuses 'non-printable' "a non-printable byte is refused" dryrun cfg1
+
+# Shell variables cannot carry NUL, so the parser must inspect the file
+# before read(1) can silently remove it.
+printf 'CPUS=2\nMEM=2\000\n' > "$VMMDIR/cfg1/config"
+refuses 'config:2: NUL byte' "a NUL byte is refused on its own line" dryrun cfg1
+
+# The parser is byte oriented whatever the caller's locale says, or a
+# multibyte character passes for one printable character.
+printf 'CDROM="/tmp/\303\251.iso"\n' > "$VMMDIR/cfg1/config"
+out=$(env LANG=en_US.UTF-8 LC_CTYPE=en_US.UTF-8 "$VMM" dryrun cfg1 2>&1) && got=0 || got=$?
+if [ "$got" != 0 ] && printf '%s' "$out" | grep -q 'non-printable'; then
+ ok "a UTF-8 byte is refused under a UTF-8 locale"
+else
+ notok "a UTF-8 byte is refused under a UTF-8 locale" "exit $got: $out"
+fi
+
+# A final line with no trailing newline must still be seen.
+printf 'CPUS=3' > "$VMMDIR/cfg1/config"
+outputs "-smp '3'" "final line without a newline is parsed" dryrun cfg1
+
+# CRLF must not leak into a value.
+printf 'CPUS=4\r\n' > "$VMMDIR/cfg1/config"
+outputs "-smp '4'" "CRLF line endings are handled" dryrun cfg1
+
+# An unreadable config must say so, not blame a key it never got to read.
+chmod 000 "$VMMDIR/cfg1/config"
+refuses 'not readable' "an unreadable config names the real problem" dryrun cfg1
+chmod 600 "$VMMDIR/cfg1/config"
+
+# edit exists to re-read the config once the editor has been through it,
+# which is the only reason to run one from here. EDITOR=true is exported
+# by the harness, so what is left of edit is the check.
+: > "$VMMDIR/cfg1/config"
+exits 0 "edit re-validates the config it just wrote" edit cfg1
+outputs 'config ok' "edit says so when the config parses" edit cfg1
+
+cfg cfg1 <<EOF
+MEMORY=4096
+EOF
+refuses 'unknown key' "edit refuses to leave a broken config unreported" edit cfg1
+exits 2 "edit of an unknown VM exits 2" edit nosuchvm