⬢ DragonFlyBSD Kernel Audit
DF-3476 / fix.diff
← back to finding ↓ download raw
--- a/sys/netgraph7/bluetooth/socket/ng_btsocket_rfcomm.c
+++ b/sys/netgraph7/bluetooth/socket/ng_btsocket_rfcomm.c
@@ -3549,29 +3549,26 @@
 static void
 ng_btsocket_rfcomm_process_timeout(void *xpcb)
 {
 	ng_btsocket_rfcomm_pcb_p	pcb = (ng_btsocket_rfcomm_pcb_p) xpcb;
 
-	lockmgr(&pcb->pcb_lock, LK_EXCLUSIVE);
+	/*
+	 * DF-3476: do NOT block on pcb_lock here. This is a callout
+	 * callback: while we would be blocked waiting for pcb_lock,
+	 * CALLOUT_INPROG stays set, and any pcb_lock holder calling
+	 * ng_btsocket_rfcomm_untimeout() -> callout_stop() (synchronous)
+	 * sleeps forever waiting for this callback to return. That is an
+	 * AB-BA deadlock (softclock thread vs task/user thread).
+	 *
+	 * Instead, only set the TIMEDOUT flag (u_int16_t store) and let
+	 * the RFCOMM task do all state processing under pcb_lock - it
+	 * already handles TIMEDOUT in ng_btsocket_rfcomm_session_process_pcb()
+	 * for every state that arms a timeout. If untimeout() won the race
+	 * (TIMO already cleared), do nothing: the DLC was killed/stopped
+	 * before we fired.
+	 */
+	if (!(pcb->flags & NG_BTSOCKET_RFCOMM_DLC_TIMO))
+		return;
 
 	pcb->flags &= ~NG_BTSOCKET_RFCOMM_DLC_TIMO;
 	pcb->flags |= NG_BTSOCKET_RFCOMM_DLC_TIMEDOUT;
 
-	switch (pcb->state) {
-	case NG_BTSOCKET_RFCOMM_DLC_CONFIGURING:
-	case NG_BTSOCKET_RFCOMM_DLC_CONNECTING:
-		pcb->state = NG_BTSOCKET_RFCOMM_DLC_DISCONNECTING;
-		break;
-
-	case NG_BTSOCKET_RFCOMM_DLC_W4_CONNECT:
-	case NG_BTSOCKET_RFCOMM_DLC_DISCONNECTING:
-		break;
-
-	default:
-		panic(
- "%s: DLC timeout in invalid state, dlci=%d, state=%d, flags=%#x\n",
-			__func__, pcb->dlci, pcb->state, pcb->flags);
-		break;
-	}
+	/* CONFIGURING/CONNECTING -> DISCONNECTING transition is done by
+	 * the task (process_pcb) when it sees TIMEDOUT under pcb_lock;
+	 * add the transition there to preserve behaviour. */
 
 	ng_btsocket_rfcomm_task_wakeup();
-
-	lockmgr(&pcb->pcb_lock, LK_RELEASE);
 }
*** NOTE: the matching task-side addition (in process_pcb, under pcb_lock):
***	case NG_BTSOCKET_RFCOMM_DLC_CONFIGURING:
***	case NG_BTSOCKET_RFCOMM_DLC_CONNECTING:
***		if (pcb->flags & TIMEDOUT)
***			pcb->state = NG_BTSOCKET_RFCOMM_DLC_DISCONNECTING; (or kill)
***		break;