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

smbus_release_bus tears down the parent-bridge exclusion BEFORE validating ownership and wakes waiters on the EACCES path (DF-5566 twin) β€” non-owner release frees the iicbus under the real owner and can wedge the bus permanently

Summary

SMBUS_CALLBACK(SMB_RELEASE_BUS) runs BEFORE the sc->owner==dev check - and since every smbus requester funnels through the same iicsmb token the iicbus-level check trivially passes, so a non-owner drops the underlying iicbus out from under the official owner and is then told EACCES; the unconditional wakeup on the EACCES path wakes parked requesters into the DF-5636 phantom grant with no release at all. Reproduced: non-owner release=13 then iicbus torn down while devA owns; grant with no release; live wedge observed (real owner's subsequent release EACCES'd, sc->owner wedged, every later requester parked). validate-before-callback + wakeup-on-change-only fix validated.

Discussion (0)

No comments yet.

PoC verification

Evidence pack

findings/poc/DF-5637 Β· 14 files
FileTypeDescriptionSize
df5636_rig.c β€” 22.5 KB view raw
df5636ctl.c β€” 1.5 KB view raw
iicbb_reg.c β€” 491 B view raw
Makefile.rig β€” 279 B ↓ download
Makefile.reg β€” 228 B ↓ download
build.sh β€” 82 B view raw
run.sh β€” 112 B view raw
build.log β€” 1.1 KB view raw
run_baseline.log β€” 5.2 KB view raw
run_fixed.log β€” 2.9 KB view raw
env.txt β€” 512 B view raw
fix.diff β€” 2.2 KB view raw
VERDICT.md β€” 2.8 KB ↓ raw
verdict.json β€” 3.7 KB view raw
VERDICT.md
↓ download raw

DF-5637 VERDICT β€” REPRODUCED

Root cause: smbus_release_bus() (sys/bus/smbus/smbconf.c:151-180) performs the parent teardown SMBUS_CALLBACK(parent, SMB_RELEASE_BUS, NULL) at smbconf.c:160 before validating sc->owner == dev at smbconf.c:166, then broadcasts wakeup(sc) at smbconf.c:177 even when validation fails (EACCES). Under the iicsmb bridge the parent call is iicbus_release_bus(parent, iicsmb_dev) (iicsmb.c:263-266), and because every smbus requester funnels through the same iicsmb device token, the iicbus ownership check trivially passes: any smbus child device_t β€” owner or not β€” succeeds in releasing the underlying iicbus.

Reproduced (stock kernel, same rig/chain as DF-5636): F3: with devA the recorded owner, a non-owner devB's smbus_release_bus returned EACCES yet the iicbus had been released β€” the rig's direct iicbus_request_bus(iicbus0, devC, IIC_DONTWAIT) probe succeeded (=0), proving the bridge exclusion under the still-official owner was gone (summary=0x1, run_baseline.log). F3B: the same non-owner release woke a parked requester into a success return while sc->owner still equaled devA β€” a grant with no release at all (compounding with the DF-5636 loop bug). Wedge consequence observed live: after a non-owner release tore the bridge, the real owner's own release failed EACCES at the callback and left sc->owner set forever, parking all subsequent requesters.

Fixed kernel (fix.diff, kernel #3): release validates ownership first (EACCES without touching the bridge), and the EACCES path no longer wakes anyone: F3 probe refused with EWOULDBLOCK (bridge exclusion intact), F3B waiter stays parked until the real release (GOOD on fixed code), no wedges across the whole suite.

Not separately demonstrated: the cross-CPU double-acquire via the per-CPU-only crit_enter() sections (smbconf.c:165-174, and smbconf.c:127-136 in request) β€” structurally the DF-5566 MP twin; the window is a few instructions and the deterministic protocol bugs above were the priority. The fix keeps the crit sections (matching the iiconf twin's scope); a lock-based rework belongs to a family-wide cleanup.

Classification: status=reproduced, reproduced=1, impact=dos (protocol teardown / permanent bus wedge / spurious wake), confidence certain. Severity Low: requires a caller passing a wrong/non-owner device_t (driver bug, error-path misuse, or the wedge chain above) β€” root/HW-config gated, same band as the DF-5566 twin it mirrors.

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

On the rebuilt kernel the non-owner release still correctly returns EACCES but the iicbus stays held (probe EWOULDBLOCK, 'bridge exclusion intact'), the EACCES path wakes nobody (F3B 'GOOD on fixed code'), and no wedge occurred across the full suite.

run_fixed.log F3.2/F3.3 and F3B.3/F3B.3w lines vs run_baseline.log; fix.diff sha256 346fe6893056aecf467e64d12285a0de337461aee150737964c0c3f5f06fbc2a
↓ fix.diffper-fix-DF-5637

Confirmed kernel references

Detail

Evidence (decisive lines)

run_baseline.log F3 lines 'NON-owner release(devB)=13' + 'iicbus probe ... =0 <<< iicbus TORN DOWN by the non-owner release while devA owns smbus'; F3B 'GRANTED while another device verifiably OWNS the bus (no release ever happened)'; wedge: F1.5/F1.6 + f2 parked in smbreq; run_fixed.log 'EWOULDBLOCK: bridge exclusion intact' and 'no bogus wakeup: GOOD on fixed code'

PoC changes

shared rig with DF-5636 (see its poc_changes); F3 adds the direct iicbus DONTWAIT probe as the teardown oracle and bridge-restore cleanup; F3B isolates the EACCES-wakeup leg

Verified recommended fix

Validate sc->owner == dev (crit section) and return EACCES before any SMBUS_CALLBACK parent release; release the bridge only after ownership is confirmed; wakeup(sc) only on actual ownership change (pack fix.diff, shared with DF-5636).

Verdict

smbus_release_bus (sys/bus/smbus/smbconf.c:151-180) releases the underlying bridge (SMBUS_CALLBACK at :160 -> iicbus_release_bus via iicsmb.c:263-266) BEFORE validating sc->owner == dev (:166), then broadcasts wakeup(sc) (:177) even on the EACCES path. Reproduced with the DF-5636 rig on the unmodified production stack: a non-owner's release returned EACCES while the iicbus exclusion under the still-official owner was verifiably gone (a direct DONTWAIT iicbus request succeeded); the same non-owner release woke a parked requester into a success return with the owner unchanged (no release at all); and in the first baseline session the teardown chain wedged the bus permanently (the real owner's subsequent release failed EACCES at the callback, leaving sc->owner set and parking every later requester). fix.diff moves the ownership validation ahead of any parent callback, returns EACCES without side effects, and wakes only on actual ownership change: validated on the rebuilt kernel (probe refused EWOULDBLOCK 'bridge exclusion intact'; 'no bogus wakeup: GOOD on fixed code'). The crit_enter-only ownership update (per-CPU, no cross-CPU exclusion) is the DF-5566 MP twin and is noted, not separately demonstrated.