β¬’ DragonFlyBSD Kernel Audit
← triage Β· dashboard
DF-5513

ppb_request_bus() is not idempotent for the current owner: an owner re-request sleeps uninterruptibly forever on itself

Summary

Only 'is any owner present' is tested: an owner re-requesting falls into the same no-PCATCH no-timeout tsleep as any waiter - waiting for itself forever. pcfclock_open is the unguarded production caller (world-readable 0444 node; DF-5029 proved the unpriv userland hang on hardware) - this pins the bus-side semantics; the owner==dev fast path protects any future unguarded driver. Rig: owner re-request stuck in 'ppbreq' sleep 4/4, revived only by release; fast-path fix returns 0 immediately.

Discussion (0)

No comments yet.

PoC verification

Evidence pack

findings/poc/DF-5513 Β· 12 files
FileTypeDescriptionSize
Makefile β€” 479 B ↓ download
README.md β€” 1.1 KB ↓ raw
VERDICT.md β€” 1.4 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 β€” 350 B view raw
harness.c β€” 21.5 KB view raw
run.log β€” 723 B view raw
run.sh β€” 98 B view raw
verdict.json β€” 3.7 KB view raw

DF-5513 -- ppb_request_bus() owner self-request sleeps uninterruptibly forever

What

ppbconf.c:466 only tests 'any owner present'; the current owner that re-requests falls into tsleep(ppb, 0, "ppbreq", 0) -- no PCATCH, no timeout; it waits for itself forever. pcfclock_open() (no HAVEBUS guard, bare PPB_WAIT on the 0444 /dev/pcfclock0) is the production trigger (see DF-5029 for the verified end-user path).

Build / run / expected (inside the DragonFly guest)

build (guest): sh build.sh
run   (guest): sh run.sh   # deterministic F4 section (3 s wait)
expected pristine: "DF-5513 REPRODUCED: owner re-requested with PPB_WAIT|PPB_NOINTR is STILL ASLEEP after 3s ... only an explicit release ever wakes it"
expected patched : "DF-5513 not-reproduced: request returned spontaneously ret=0"

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).

VERDICT.md
↓ download raw

DF-5513 VERDICT

ppb_request_bus() owner self-request sleeps uninterruptibly forever

REPRODUCED (rig) 4/4 fresh-boot runs (deterministic test)

A kernel thread re-requests the bus for the device that already owns it with PPB_WAIT|PPB_NOINTR (what pcfclock_open() passes, since PPB_NOINTR==0): the thread is still in tsleep("ppbreq") after 3 s in every run, and only a ppb_release_bus() from another thread revives it. In production nobody releases on the sleeper's behalf: the owner waits for itself forever, unkillable (no PCATCH), same D-state wedge DF-5029 demonstrated end-to-end on real hardware from userland via the world-readable /dev/pcfclock0. This finding pins the ppbus-side defect and the bus-level fix (idempotency), which also protects any future driver that forgets a HAVEBUS-style guard.

FIX VALIDATED: with the owner==dev early-out the re-request returns 0 immediately (no sleep) on both patched boots.

-- 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

fixed
baseline reproduced→ patch + rebuild →patched clean

fix.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 (the owner re-request returns 0 immediately (done=1, no sleep), 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.
↓ fix.diffDragonFly 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

Confirmed kernel references

Detail

Evidence (decisive lines)

run.log ('DF-5513 REPRODUCED: owner re-requested ... STILL ASLEEP after 3s ... only ppb_release_bus() ever wakes it'); fix_run.log ('not-reproduced: request returned spontaneously ret=0'); harness.c hx_f4_selfreq_test/hx_f4_sleeper; fix.diff hunk 3.

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

Early-out for the current owner inside the crit section: if (ppb->ppb_owner == dev) { crit_exit(); return (0); } (fix.diff hunk 3).

Verdict

REPRODUCED 4/4 pristine runs. ppb_request_bus() tests only 'is ANY owner present' (ppbconf.c:466); a device that already owns the bus and re-requests falls into the same sleep as any other waiter -- for PPB_WAIT|PPB_NOINTR that is tsleep(ppb, 0, "ppbreq", 0) with no PCATCH and no timeout (:473-475), i.e. the owner waits for itself forever. Rig: an owning child re-requests on a kernel thread and is still asleep after 3 s in every run; only an explicit ppb_release_bus() from another thread revives it. Production trigger: pcfclock_open() (pcfclock.c:152-153) has no HAVEBUS-style guard and passes bare PPB_WAIT, so a second open of the world-readable (0444) /dev/pcfclock0 hangs the opener unkillably (kill -9 impossible) -- the unprivileged end-user path was fully verified on real hardware by sibling finding DF-5029 against pcfclock.c; the present finding pins the ppbus-side semantics (any driver that re-requests while owning) and the bus-level fix. Fix validated: with an owner==dev early-out the re-request returns 0 immediately.