ppb_release_bus() saves the device context after clearing ppb_owner and outside crit: cross-device chipset-mode context corruption race
Summary
The ctx snapshot happens after the ownership handoff and outside crit - a second child acquiring and set_mode'ing in that window has ITS mode written into the releasing device's context, later restored wrong for the first. Rig: 50M-iteration 2-thread race on 6 vCPUs, wrong-captures 2/1/1/6 across 4 boots; save-under-crit-before-clear fix -> 0/50M. Hardware-gated privileged; wrong-protocol transfers/corruption, no memory safety.
Discussion (0)
PoC verification
Evidence pack
findings/poc/DF-5511 Β· 12 files| File | Type | Description | Size | |
|---|---|---|---|---|
| Makefile | β | 479 B | β download | |
| README.md | β | 1012 B | β raw | |
| VERDICT.md | β | 1.8 KB | β raw | |
| build.sh | β | 74 B | view raw | |
| env.txt | β | 456 B | view raw | |
| fix.diff | β | 2.2 KB | view raw | |
| fix_build.log | β | 8.8 KB | view raw | |
| fix_run.log | β | 367 B | view raw | |
| harness.c | β | 21.5 KB | view raw | |
| run.log | β | 460 B | view raw | |
| run.sh | β | 98 B | view raw | |
| verdict.json | β | 3.8 KB | view raw |
DF-5511 -- ppb_release_bus() ctx-save race: context snapshotted after owner clear
What
ppbconf.c:528 clears ppb_owner, then :532 reads ppb_get_mode() into ppbdev->ctx.mode outside any crit section; a competing child acquiring the bus in the window gets its mode saved into the wrong device's context, which ppb_request_bus():491-492 later restores.
Build / run / expected (inside the DragonFly guest)
build (guest): sh build.sh run (guest): sh run.sh # F2 phase runs 10 s expected pristine: "DF-5511 releaser iterations=50000000 ... WRONG-mode-captured(B's EPP in A's ctx)=N -> REPRODUCED" (N = 2/1/1/6 across runs) expected patched : "... WRONG-mode-captured ... =0 -> race not hit this run"
Rig
ppbharness2.ko compiles the VERBATIM production sources
/usr/src/sys/bus/ppbus/{ppbconf.c,ppb_1284.c,ppb_base.c}; a fake "ppc"
controller on nexus provides a fully scripted (malicious) IEEE1284 peer.
Details in VERDICT.md; shared rig sources in the DF-5510 pack (identical
copies here).
DF-5511 VERDICT
ppb_release_bus() ctx-save race: context snapshotted after owner clear
REPRODUCED (rig) 4/4 fresh-boot runs
Deterministic attribution: only device A's own ppb_release_bus() writes A's ppbdev->ctx.mode, and the releaser thread checks it immediately after its release call returns; a capture of B's mode (PPB_EPP) can only have been read from ppb->mode AFTER B interposed between A's owner-clear (:528) and A's get_mode (:532).
Hits: 2, 1, 1, 6 wrong captures per 50M release iterations on the 6-vCPU guest (window is a handful of instructions).
Impact ceiling: the corrupted context is restored at A's next ppb_request_bus (:491-492), so device A afterwards operates the shared port in device B's IEEE1284 chipset mode (e.g. an lpt printer driven in EPP/ECP framing, or pcfclock's bit-banged protocol running in the wrong mode) -> data corruption / hung transfers on the parallel port. No memory-safety consequence. All ppbus children (lpt, ppi, pcfclock) share one bus, but their device nodes are 0600 root:wheel by default, so this needs privileged (or physically local) access plus real hardware.
FIX VALIDATED: saving ctx.{mode,valid} inside the crit section before clearing the owner yields 0 wrong captures in 50M iterations on two patched boots (fixrun1/fixrun2), vs 1-6 per run on pristine.
-- status=reproduced reproduced=True impact=none confidence=certain fix_status=fixed (baseline reproduced, patched behavior gone)
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
fixedfix.diff applied to the guest's /usr/src/sys/bus/ppbus/ppbconf.c, harness rebuilt from the same verbatim source (module-level rebuild; running kernel unchanged), full rig re-run on a fresh boot: the previously observed bad behavior is GONE (0 wrong captures in 50M iterations, verified on two patched boots). Behavior of the sibling known findings (DF-1093/2327/2328/2329) was not targeted by this diff.
fix_run.log / fixrun2.dmesg (patched) vs run.log / run4.dmesg (pristine) in this pack; fix.diff is the exact applied diff.
Confirmed kernel references
Detail
Evidence (decisive lines)
run.log ('DF-5511 releaser ... WRONG-mode-captured(B's EPP in A's ctx)=6 -> REPRODUCED' on the pristine decisive run); fix_run.log ('=0' on patched); harness.c hx_f2_releaser/hx_f2_stealer; fix.diff hunk 4.
PoC changes
No seed PoC existed (pass-2 finding); rig written from scratch following the DF-5418/5419 ppbharness class: verbatim-source KLD (ppbconf.c+ppb_1284.c+ppb_base.c), scripted IEEE1284 nibble-mode peer, fake ppc controller on nexus, real ppbus driver attach. Bring-up fixes: comment typo; the stack pre-dirtier initially collapsed to a single frame via -O2 tail-call optimization (made noinline with a post-call touch).
Verified recommended fix
Save ppbdev->ctx.{mode,valid} inside the crit section BEFORE clearing ppb_owner (fix.diff hunk 4).
Verdict
REPRODUCED 4/4 pristine runs. ppb_release_bus() clears ppb->ppb_owner at ppbconf.c:528 and only then reads ppb_get_mode() into ppbdev->ctx.mode at :532 -- outside any critical section -- so a second ppbus child (lpt/ppi/pcfclock all attach to the same bus) that acquires the bus and calls ppb_set_mode() inside that window has ITS mode snapshotted into the releasing device's context; ppb_request_bus() later restores that wrong mode for the first device (:491-492). Rig: releaser thread (device A, mode PS2) and stealer thread (device B, mode EPP) race request/set_mode/release for 50M iterations on the 6-vCPU guest: 2/1/1/6 wrong-mode captures across the four runs (A's ctx.mode == B's EPP), every capture provably written by A's own release call (no other writer of A's ctx). Impact ceiling: cross-device chipset-mode corruption on the shared port (wrong IEEE1284 protocol restored for lpt/pcfclock/ppi transfers -> printer data corruption / hung transfers), no memory-safety consequence; device nodes are 0600 root:wheel by default and the port is hardware-gated. Fix validated: with ctx saved under crit while still owner, 0 wrong captures in 50M iterations on two patched runs.
No comments yet.