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

Deadlock: callout_stop under pcb_lock while timeout callback requires pcb_lock

Summary

ng_btsocket_l2cap_untimeout(:2765-2775) asserts pcb_lock held, calls callout_stop under it. Timeout callback process_timeout(:2782-2786) acquires pcb_lock as first action. callout_init_mp(:1982) NOT callout_init_lk -> DragonFly callout_stop sync=1 blocks until callback done. Cycle: thread A holds pcb_lock sleeps in callout_stop waiting callback; softclock callback blocked on pcb_lock waiting A. Uninterruptible deadlock. Every untimeout caller under pcb_lock affected: detach(:2355) disconnect(:2411) all process_l2ca_* paths. Local user: AF_BLUETOOTH socket + close racing 300s ERTX timer.

Discussion (0)

No comments yet.

PoC verification

Evidence pack

findings/poc/DF-0325 Β· 5 files
FileTypeDescriptionSize
VERDICT.md verdict source-trace confirmed deadlock, no BT HW 2.3 KB ↓ raw
README.md readme status explanation 750 B ↓ raw
fix.diff suggested-fix use callout_stop_async instead of callout_stop 850 B view raw
../fix_build_combined.log build-log Combined 41-finding kernel build (rc=0, -Werror clean) 5.6 MB ↓ download
../fix_build_summary.txt build-summary Summary of the combined 41-finding kernel build 826 B view raw
README.md readme status explanation
↓ download raw

DF-0325 PoC β€” Bluetooth L2CAP callout deadlock

Status: REAL (source-trace confirmed), not testable on this guest

The deadlock is confirmed by tracing: - ng_btsocket_l2cap_untimeout() holds pcb_lock then calls callout_stop() (sync=1) which blocks until the callback finishes - The callback ng_btsocket_l2cap_process_timeout() needs pcb_lock as its first action - Classic AB-BA deadlock

Not testable because: - ng_btsocket_l2cap module is not loaded by default - Not compiled into GENERIC kernel - Requires Bluetooth hardware (none on guest) - Loading requires kldload (root-only, invalidates unpriv chain)

Fix

See fix.diff β€” replaces callout_stop() with callout_stop_async() to avoid the synchronous wait.

VERDICT.md verdict source-trace confirmed deadlock, no BT HW
↓ download raw

DF-0325 β€” Deadlock: callout_stop under pcb_lock while timeout callback needs pcb_lock

Verdict: REAL (source-trace confirmed) β€” not testable on this guest (no Bluetooth HW/module)

Mechanism

ng_btsocket_l2cap_untimeout() at sys/netgraph7/bluetooth/socket/ng_btsocket_l2cap.c:2765-2775:

2767: KKASSERT(lockowned(&pcb->pcb_lock) != 0);   // holds pcb_lock
2770: callout_stop(&pcb->timo);                     // blocks until callback done

ng_btsocket_l2cap_process_timeout() (the timeout callback) at :2782-2786:

2786: lockmgr(&pcb->pcb_lock, LK_EXCLUSIVE);        // needs pcb_lock

callout_init_mp(&pcb->timo) at :1982 uses the MP (non-lock) variant. DragonFly's callout_stop() calls _callout_cancel_or_stop(cc, CALLOUT_STOP, 1) with sync=1 (sys/kern/kern_timeout.c:1093), which at :910-921 enters a ssleep() loop waiting for the callback to finish:

910: ++c->waiters;
911: for (;;) {
912:     cpu_ccfence();
913:     if ((c->flags & flags) == 0) break;
...
920:     ssleep(c, &c->spin, 0, "costp", 0);
921: }

Deadlock cycle: - Thread A: holds pcb_lock β†’ sleeps in callout_stop waiting for callback to finish - Callback (softclock): tries to acquire pcb_lock β†’ blocked waiting for Thread A

Neither can make progress. The system deadlocks (one CPU stuck spinning on pcb_lock, another sleeping in callout_stop).

Privilege / testability

  • The ng_btsocket_l2cap module is not loaded by default and is not compiled into the GENERIC kernel.
  • Loading it requires kldload (root-only).
  • Even loaded, it requires Bluetooth hardware/stack to create L2CAP sockets.
  • No Bluetooth hardware on this guest.

This is a valid hard blocker: the code path is dead code on this guest (no Bluetooth). The deadlock is confirmed via source trace.

Fix

fix.diff β€” use callout_stop_async() instead of callout_stop() in ng_btsocket_l2cap_untimeout(). The async variant requests cancellation without blocking, so the pcb_lock holder doesn't sleep. The pcb->flags &= ~NG_BTSOCKET_L2CAP_TIMO immediately after makes any in-flight callback a no-op when it eventually acquires the lock.

Impact

Local DoS (system deadlock/hang) if Bluetooth L2CAP sockets are available. Requires the bluetooth netgraph stack to be loaded and operational. No memory corruption β€” pure DoS.

Fix verification

not_testable

compile validated

see evidence pack

Confirmed kernel references

β€”

Detail

Exploit chain

none

Evidence (decisive lines)

β€”

Verdict

Source-confirmed. callout_stop(sync) under pcb_lock vs timeout callback LK_EXCLUSIVE -> AB-BA deadlock. ng_btsocket not in GENERIC.