ppb_MS_loop() emits MS_DBRA(-1) whose branch lands on the DBRA itself β the loop body executes exactly once regardless of `iter`
Summary
The adapter branch contract is target = mi + offset + 1 (ppc.c:1447-1451, corroborated by nibble_inbyte DBRA(-7)@6->0): ppb_MS_loop's generated body CALL at index 2 and DBRA(-1) at index 3 make the DBRA its own target - the body runs once while the DBRA spins in place. Harness: iter=5 -> body=1, dbra_spins=4; -2 offset fix -> body=5. Zero in-tree callers (grep: definition only) - latent API logic damage.
Discussion (0)
PoC verification
Evidence pack
findings/poc/DF-5575 Β· 11 files| File | Type | Description | Size | |
|---|---|---|---|---|
| README.md | β | 2.4 KB | β raw | |
| VERDICT.md | β | 3.3 KB | β raw | |
| msqhx.c | β | 19.3 KB | view raw | |
| Makefile | β | 311 B | β download | |
| build.sh | β | 126 B | view raw | |
| run.sh | β | 150 B | view raw | |
| build.log | β | 7.7 KB | view raw | |
| run.log | β | 1.1 KB | view raw | |
| fix_run.log | β | 191 B | view raw | |
| fix.diff | β | 3.2 KB | view raw | |
| env.txt | β | 670 B | view raw |
DF-5575 β ppb_MS_loop() emits MS_DBRA(-1) whose branch lands on the DBRA itself β the loop body executes exactly once regardless of iter
Severity: Info Β· Confidence: certain Β· File: sys/bus/ppbus/ppb_msq.c (pass 2)
Reproduce
sh build.shβ compiles the verbatim productionppb_msq.cintomsqhx.kosh run.shβ case 0 (non-fatal)- Observe dmesg (
dmesg | grep MSQHX) / the serial console.
Expected
BASELINE (unfixed): 'DF-5575 iter=5 -> prolog=1 body=1 epilog=1 dbra_spins=4' β body ran ONCE. PATCHED: 'DF-5575 iter=5 -> prolog=1 body=5 epilog=1' β body runs iter times.
Harness (shared by DF-5573..DF-5577)
msqhx.c + Makefile compile the verbatim production
/usr/src/sys/bus/ppbus/ppb_msq.c (sha256 223e7f9184c81ab83f49228ae7d1f99a
6e3936b9a452b01c6bb6176b150dda67, identical to the repo sys/ copy) into a
KLD, together with a fake ppbus controller on nexus whose
ppbus_exec_microseq method implements the exact dispatch semantics of
ppc_exec_microseq() (sys/dev/misc/ppc/ppc.c:1332-1542) for the exercised
opcodes. The interpreter target device's softc is a real struct ppb_data
and its child's ivars are a real struct ppb_device, so mode2xfer(),
ppb_MS_init(), ppb_MS_exec(), ppb_MS_microseq(), ppb_MS_loop() and
ppb_MS_init_msq() all run their production code paths. Evidence class:
"KLD-harness-with-production-code" (no parallel port in the QEMU rig) β
family standard, cf. DF-5418.
Build
# inside the guest, from this directory sh build.sh # -> /tmp/msqhx-build/msqhx.ko
Run
sh run.sh # runs this pack's case via sysctl hw.msqhx_case sh run.sh <case> # 0=suite(5575+5573) 2=5574 4=5576a 5=5576b 6=5577
Cases 2/4/5 PANIC the guest on unfixed production code (that is the finding);
the panics land on the serial console (dfbsd-qemu/vm.sh log).
Fix validation
fix.diff (sha256 8e3dfc90e6049ce1704c3cef833cba0904b6404f11360bdd299e37980e9327bb) patches sys/bus/ppbus/ppb_msq.c +
sys/bus/ppbus/ppbconf.h (all five findings; never applied to the repo's
read-only sys/ tree). Validation procedure: copy the fixed files into the
guest's /usr/src/sys/bus/ppbus/, sh build.sh again (recompiles the
production source), re-run the same case. Baseline vs patched outputs are
in run.log / fix_run.log (plus panic*.txt / fix_run.log for the
fatal cases).
DF-5575 β verdict narrative
Finding
ppb_MS_loop() emits MS_DBRA(-1) whose branch lands on the DBRA itself β the loop body executes exactly once regardless of iter
Bottom line (status: reproduced, impact: none)
Reproduced on the QEMU guest (DragonFly 6.5-DEVELOPMENT x86_64). The adapter-level branch contract (ppc.c:1447-1451 'mi += offset; mi++') resolves a branch target to mi+offset+1, corroborated by every working in-tree sequence (nibble_inbyte DBRA(-7) at index 6 targets index 0; ps2_inbyte DBRA(-4) at index 4 targets index 1; vpoio.c:195/210/226/239). ppb_MS_loop()'s generated sequence places the body CALL at index 2 and MS_DBRA(-1) at index 3 (ppb_msq.c:160-170): -1 from index 3 lands on index 3 β the DBRA itself β so the branch spins in place decrementing the accumulator (harness counter dbra_spins=4 for iter=5) while the body CALL is never re-executed: prolog=1 body=1 epilog=1 regardless of iter. The documented API contract ('execute a microseq loop', body iter times) is broken; the correct offset is -2. ppb_MS_loop() has zero in-tree callers (grep across sys/ finds only the definition/declaration), so this is latent dead-API logic damage: severity Info. Fix validated: MS_DBRA(-2) makes the same run print body=5 with prolog/epilog unchanged.
Exploit chain
n/a
Evidence
run.log ('DF-5575 iter=5 -> prolog=1 body=1 epilog=1 dbra_spins=4 ... BODY EXECUTED ONCE (off-by-one CONFIRMED)') on pristine production code; fix_run.log ('observed body=5 -> loop works') on the fix.diff-patched source; the adapter-contract derivation is documented in VERDICT.md
Kernel references
sys/bus/ppbus/ppb_msq.c:160sys/bus/ppbus/ppb_msq.c:166sys/dev/misc/ppc/ppc.c:1447sys/dev/disk/vpo/vpoio.c:195sys/dev/disk/vpo/vpoio.c:210
Fix validation (fixed)
MS_DBRA(-1) -> MS_DBRA(-2): iter=5 now yields body=5 (prolog=1 epilog=1), matching the documented ppb_MS_loop() semantics; no other behavior changed.
fix.diff (sha256 8e3dfc90e6049ce1704c3cef833cba0904b6404f11360bdd299e37980e9327bb) carries all five ppb_msq.c fixes for this pass;
the hunks relevant to this finding are marked with its DF id in the comments.
Fix validation followed the family standard (cf. DF-5418): the fixed sources
were installed into the guest's /usr/src/sys/bus/ppbus/, the harness was
rebuilt (recompiling the production file), and the exact same case was
re-run β baseline vs patched outputs are in run.log / fix_run.log
(+ panic*.txt). The repo's sys/ tree was never modified; the guest was
vm.sh reset with-src afterwards (sources restored+verified by snapshot).
PoC changes vs seed
No seed PoC existed; written fresh. Non-fatal counters via MS_C_CALL hooks + SUBRET, executed by the fake adapter interpreter replicating ppc semantics.
Guest: DragonFly dfbsd 6.5-DEVELOPMENT DragonFly 6.5-DEVELOPMENT #0: Thu Jul 2 06:02:54 UTC 2026 root@dfbsd:/usr/obj/usr/src/sys/X86_64_GENERIC x86_64 Harness sha256: 4b728315b40157934f62c99e847f386b30202eef77083dc1a5c065b7b0448ded
Fix status (batch validation)
Applied with the 36 sibling fix.diffs of this batch in findings/poc/_batch41/all_fixes.patch; single in-guest make -j6 nativekernel KERNCONF=X86_64_GENERIC completed RC=0 with zero errors under -Werror (full log: findings/poc/_batch41/fix_build8.log). Behavior A/B intentionally skipped for this Info-tier item.
Fix verification
fixedMS_DBRA(-1) -> MS_DBRA(-2): iter=5 now yields body=5 (prolog=1 epilog=1), matching the documented ppb_MS_loop() semantics; no other behavior changed.
fix_run.log (patched production source, same case); run.log (baseline) vs fix_run.log (patched); fix.diff sha256 8e3dfc90e6049ce1704c3cef833cba0904b6404f11360bdd299e37980e9327bb
Confirmed kernel references
Detail
Evidence (decisive lines)
run.log ('DF-5575 iter=5 -> prolog=1 body=1 epilog=1 dbra_spins=4 ... BODY EXECUTED ONCE (off-by-one CONFIRMED)') on pristine production code; fix_run.log ('observed body=5 -> loop works') on the fix.diff-patched source; the adapter-contract derivation is documented in VERDICT.md
PoC changes
No seed PoC existed; written fresh. Non-fatal counters via MS_C_CALL hooks + SUBRET, executed by the fake adapter interpreter replicating ppc semantics.
Verified recommended fix
ppb_MS_loop(): MS_DBRA(-1) -> MS_DBRA(-2) so the branch target is the body CALL, not the DBRA itself
Verdict
Reproduced on the QEMU guest (DragonFly 6.5-DEVELOPMENT x86_64). The adapter-level branch contract (ppc.c:1447-1451 'mi += offset; mi++') resolves a branch target to mi+offset+1, corroborated by every working in-tree sequence (nibble_inbyte DBRA(-7) at index 6 targets index 0; ps2_inbyte DBRA(-4) at index 4 targets index 1; vpoio.c:195/210/226/239). ppb_MS_loop()'s generated sequence places the body CALL at index 2 and MS_DBRA(-1) at index 3 (ppb_msq.c:160-170): -1 from index 3 lands on index 3 β the DBRA itself β so the branch spins in place decrementing the accumulator (harness counter dbra_spins=4 for iter=5) while the body CALL is never re-executed: prolog=1 body=1 epilog=1 regardless of iter. The documented API contract ('execute a microseq loop', body iter times) is broken; the correct offset is -2. ppb_MS_loop() has zero in-tree callers (grep across sys/ finds only the definition/declaration), so this is latent dead-API logic damage: severity Info. Fix validated: MS_DBRA(-2) makes the same run print body=5 with prolog/epilog unchanged.
No comments yet.