Files
erofs-freebsd-out-tree/docs/pre9-controlled-manual-qemu.md
T
2026-08-18 09:20:44 +02:00

8.4 KiB

Pre9 controlled manual QEMU evidence audit

Date: 2026-08-13

Scope and evidence status

This report audits the already completed manual run at /work/tests-dev/temp/pre9-manual-20260813-run3/. No QEMU test was rerun. The long-lived guard VM (PID 26318, host port 9222) was not touched.

The automated TC004 implementation remains PENDING_FIX/BLOCKED in /work/tests-dev/fix-todo/tc004-automation.md. Run3 is a manual substitute; it is not evidence that TC004 automation passes.

Run history:

  • Run1: infrastructure failure; no retained result directory is available.
  • Run2: INFRA. Guest SSH authentication failed for Pre7 and Pre8, then the host runner was interrupted while preparing Pre9. It produced no DUT result.
  • Run3: valid manual run. All three DUTs built, loaded, mounted, executed the bounded probes, produced a live SHA stack sample, and cleaned up.

Verdict

Question Verdict Evidence
4 KiB, 16 KiB, 64 KiB and 1 MiB single-syscall read correctness PASS Every version returned the requested byte count, errno=0, matched the source-range SHA-256, exited zero, and has exactly one target-file pread() in truss.
First 1 MiB sequential dd read PASS Every version returned zero and copied 1 MiB within the 20-second bound. This is a bounded smoke check, not proof of one 1 MiB kernel read.
Full-file SHA correctness BLOCKED No version retained a hash or an exit status for the background SHA. sha_after is empty and process disappearance is not correctness evidence.
Full-file read liveness/performance FAIL After an eight-second observation delay, SHA was still running in the LZMA decode path and had advanced only about 1.0-1.25 MiB. This is unacceptable for the 8 MiB smoke fixture and reproduces across all three versions.
Pre7 to Pre8/Pre9 regression attribution PASS: no Pre9 regression demonstrated Pre8 and Pre9 have the same src tree and identical KLD hash. Pre7 has a different source tree/KLD but shows the same symptom and stack. The evidence attributes the issue to shared behavior, not a Pre9-only change.

Overall verdict: PARTIAL. Bounded reads are correct, but the full-file correctness verdict is blocked and full-file liveness fails.

Runner semantics audit

The runner creates a fresh qcow2 overlay per version over the read-only base, archives the selected repository subtree, builds the KLD in the guest, mounts the same LZMA fixture, and runs four probes. Each probe invokes a C program that contains one target-file pread() and writes the returned bytes to a file. The runner then hashes that file and compares it with bytes read from the uncompressed source fixture.

Important field meanings:

  • commit: Git tree object for the version directory at the enclosing repo HEAD. It is not a standalone commit ID.
  • src_tree: Git tree object for that version's src directory.
  • probes[].elapsed: host wall time for the complete SSH command, including SSH and truss overhead. It is not pure kernel decompression time.
  • metadata.hash_match: equality between the mounted output-range hash and the uncompressed source-range hash.
  • signal=0: runner shorthand for probe return code zero. It is not a signal collected through waitpid().
  • dd_1m: a userspace dd bs=1m count=1 result. It does not establish the size or count of VOP/kernel reads.
  • sha_pid: PID printed after starting a background sha256 command.
  • diagnostic_sample: process table, kernel stack, descriptor offset, mount, md device and dmesg captured after an unconditional eight-second sleep.
  • sha_after: process status followed by sha.out and sha.err. Empty output means neither a process row nor captured SHA/error output was available.
  • status=PARTIAL: runner-generated fallback when any bounded probe succeeds. It does not mean full SHA correctness passed.

The full SHA was not run under timeout. The runner waited eight seconds, sampled it, sent TERM, slept two seconds, then attempted KILL, and finally read the output files. No start/end timestamp or exit status was recorded. Therefore the exact SHA lifetime is unknown; the only defensible timing statement is that it was still active approximately eight seconds after launch.

sha_after contains only ,state=,command= for all versions. The subsequent KILL reports No such process. This establishes only that the sampled PID no longer existed after TERM plus the two-second delay. It does not distinguish a successful completion from TERM handling, and the absent sha.out means no hash can be validated. Process disappearance must not be reported as PASS.

Controlled identities and setup

The common fixture hashes were:

  • Image: 32107a084b27362a093768b88746c37c2e99a74b9f1301a9d4046988479defe9
  • Source: ddda39737f0f6093e828a032ec161511fefbb1fa361bc6cbffdbc91e48e4c461
Version Repository tree src tree KLD SHA-256 Build/load/mount
Pre7 b8f9af2cd55225c4348b79ff5910ae6fc83cd517 dc203534d7b5721905f8538026e4c59346106020 33a52f2f16a94afda0501d305b238678b96c71e420d4b8bbcbeec6ffcdf1aae5 PASS/PASS/PASS
Pre8 1e24c992a5b071e6fdabfdc42d342b6ed5a91cf5 cbf93a19df8d1ce2fb66e2aeb7276a4ea6d4dcc2 348cb1f5a91d47e1412747d285c795c49375a61a66cfb3d35d22f38ecfde6ea0 PASS/PASS/PASS
Pre9 dc0c01157b5c925684bd77c57ed6703904af7453 cbf93a19df8d1ce2fb66e2aeb7276a4ea6d4dcc2 348cb1f5a91d47e1412747d285c795c49375a61a66cfb3d35d22f38ecfde6ea0 PASS/PASS/PASS

Pre8 and Pre9 are runtime-identical for this test according to both the source tree and the produced KLD hash.

Single-syscall probes

All probes used offset zero. Times below include SSH/truss overhead.

Version Length Returned Hash match Elapsed Exactly one target pread()
Pre7 4 KiB 4 KiB yes 1.299 s yes
Pre7 16 KiB 16 KiB yes 1.291 s yes
Pre7 64 KiB 64 KiB yes 1.438 s yes
Pre7 1 MiB 1 MiB yes 1.432 s yes
Pre8 4 KiB 4 KiB yes 1.216 s yes
Pre8 16 KiB 16 KiB yes 1.327 s yes
Pre8 64 KiB 64 KiB yes 1.430 s yes
Pre8 1 MiB 1 MiB yes 1.442 s yes
Pre9 4 KiB 4 KiB yes 1.791 s yes
Pre9 16 KiB 16 KiB yes 1.341 s yes
Pre9 64 KiB 64 KiB yes 1.374 s yes
Pre9 1 MiB 1 MiB yes 1.737 s yes

The target-file truss lines are preserved in pre9-manual-evidence/probe-summary.txt. Dynamic-loader pread() calls are not counted as target-file calls.

The 1 MiB dd results were:

  • Pre7: 1 MiB in 0.195526 s.
  • Pre8: 1 MiB in 0.179484 s.
  • Pre9: 1 MiB in 0.367417 s.

These values are not stable enough for version performance ranking, but all three bounded operations completed.

Full SHA sample

Version State at sample File offset Kernel stack Final hash
Pre7 running (RC) 1,245,184 lzma2_lzma -> erofs_xz_dec_microlzma_run -> lzma_decompress -> z_erofs_decompress -> z_erofs_do_read -> z_erofs_read_uio absent
Pre8 running (RC) 1,150,976 same absent
Pre9 running (RC) 1,044,480 same absent

The stack is direct evidence of active CPU-side decompression, not an EROFS lock wait. It does not by itself prove why decompression is slow. Combined with the static review, the leading explanation remains repeated full mapped-extent decompression as z_erofs_read_uio() segments reads at MAXPHYS. There is no direct evidence here of an XZ infinite loop or an EROFS lock deadlock.

Infrastructure integrity and cleanup

The base image SHA-256 before and after run3 was identical and matched the expected value:

67f359621f23a1d745f0889370cbb99a096cee3e99a0b2f3bb18fc7a91bf6fef

For each version, umount, mdconfig -d and kldunload returned zero. Each QEMU PID was no longer alive and each overlay was deleted. The run3 result set therefore supports successful test-VM cleanup. This report does not infer the state of the unrelated long-lived guard VM and did not inspect or modify it.

Required follow-up

A supplemental manual test is still required if full-file correctness must be closed. It should run one full SHA with an explicit wall-clock deadline, record start/end timestamps and exit status, capture sha.out before cleanup, compare the hash to the source fixture, and separately record whether TERM/KILL was used. Until then, full-file SHA correctness remains BLOCKED.