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

hci_route_lookup walks hci_unit_list and unit->hci_links with no synchronization against attach/detach (crit_enter-only) and privileged hci_disable teardown β€” UAF reads and 6-byte freed-heap disclosure via getsockname

Summary

The route lookup TAILQ-walks the global unit list and each unit's links with no lock while attach/detach mutate under crit_enter only (current-CPU interrupt masking, not cross-CPU exclusion) and privileged hci_disable frees all links: a detach/disable racing an unprivileged connect reads freed memory; bdaddr_copy copies 6 bytes of freed heap into the socket address readable via getsockname (limited leak) and a stale link deref panics. Privileged mutator precondition - Low. unit-list lock fix.

Discussion (0)

No comments yet.

PoC verification

Evidence pack

findings/poc/DF-5781 Β· 2 files
FileTypeDescriptionSize
README.md file 1.5 KB ↓ raw
verdict.json file 1.6 KB view raw
README.md file
↓ download raw

DF-5781 β€” hci_route_lookup walks hci_unit_list / hci_links with no synchronization vs attach/detach/disable

Analysis-only pack (Low severity -> PoC skipped per audit contract).

hci_route_lookup() (sys/netbt/hci_misc.c:65-100) TAILQ-walks the global hci_unit_list and each unit's hci_links with no lock, while:

  • hci_attach/hci_detach mutate hci_unit_list under crit_enter() only (sys/netbt/hci_unit.c:113-115, 123-127) β€” crit_enter blocks interrupts on the current CPU only and does not stop a cross-CPU syscall-context walker;
  • hci_disable (privileged SIOCSBTFLAGS, hci_ioctl.c:223-231; device detach) tears down all links (hci_unit.c:238-245) and frees all memos (:247-248) while user threads may be mid-walk.

A detach/disable racing an unprivileged connect() yields UAF reads of struct hci_unit/struct hci_link; bdaddr_copy(src, &unit->hci_bdaddr) (hci_misc.c:105) copies 6 bytes of freed kernel heap into the socket's local address, readable back via getsockname(2) (limited info leak); a stale link deref panics.

Rated Low: one side of the race requires root (SIOCSBTFLAGS/DOWN is caps_priv_check_self-gated) or physical device removal; no unprivileged-only trigger identified. Not PoC-verified (Low). The memo-list half of this family is DF-5780 (reproduced, High). Suggested fix: a global netbt unit-list lock (or per-unit refcount + RCU-style removal) plus holding it across hci_route_lookup's walks; see DF-5780's fix.diff for the memo-half.

Fix verification

not_testable
baseline no→ patch + rebuild →patched clean
per-fix-DF-5781

Confirmed kernel references

Detail

Evidence (decisive lines)

source citations in README.md (this directory)

PoC changes

n/a (not built)

Verified recommended fix

global netbt unit-list lock (or per-unit refcount) held across hci_route_lookup walks and hci_attach/hci_detach/hci_disable

Verdict

Analysis-only (Low severity; PoC skipped per contract). hci_route_lookup walks hci_unit_list and unit->hci_links unlocked (hci_misc.c:65-100) against hci_attach/hci_detach's crit_enter-only list mutations (hci_unit.c:113-127) and hci_disable's teardown (hci_unit.c:238-248, privileged SIOCSBTFLAGS hci_ioctl.c:223-231). Detach/disable racing an unprivileged connect gives UAF reads and a 6-byte freed-heap read into the socket laddr (hci_misc.c:105, getsockname-readable). Root/physical precondition on one side -> Low, unverified on the guest (its memo-list sibling DF-5780 was reproduced).