⬢ DragonFlyBSD Kernel Audit
DF-5736 / fix.diff
← back to finding ↓ download raw
--- a/sys/netbt/hci_link.c	2026-10-10 22:45:15.649479095 +0000
+++ b/sys/netbt/hci_link.c	2026-10-10 22:54:55.077917309 +0000
@@ -723,6 +723,21 @@
 			return;
 		}
 
+		/*
+		 * DF-5734: if the head PDU has no fragments pending at the
+		 * controller but still holds unsent data, no amount of
+		 * completion credit can make progress on it -- without this
+		 * guard the loop iterates forever (num is never decremented
+		 * and the queue never changes), wedging the bluetooth
+		 * NETISR thread permanently while it holds the mplock.
+		 */
+		if (pdu->lp_pending == 0) {
+			kprintf("hci_acl_complete: completion credit for "
+				"handle %#x with no fragments pending\n",
+				link->hl_handle);
+			return;
+		}
+
 		if (num >= pdu->lp_pending) {
 			num -= pdu->lp_pending;
 			pdu->lp_pending = 0;
@@ -731,7 +746,8 @@
 				TAILQ_REMOVE(&link->hl_txq, pdu, lp_next);
 				chan = pdu->lp_chan;
 				if (chan != NULL) {
-					chan->lc_pending--;
+					if (chan->lc_pending > 0)
+						chan->lc_pending--;
 					(*chan->lc_proto->complete)
 							(chan->lc_upper, 1);
 
--- a/sys/netbt/hci_event.c	2026-10-10 22:45:15.649479095 +0000
+++ b/sys/netbt/hci_event.c	2026-10-10 22:45:23.829371871 +0000
@@ -878,6 +878,16 @@
 	if (rp.status > 0)
 		return;
 
+	/*
+	 * DF-5735: a Max ACL Data Packet Length of zero is unusable (and
+	 * unspec-conformant): hci_acl_send() would fragment every outgoing
+	 * PDU into zero-length chunks forever (m_split(m, 0) hands all the
+	 * data to the tail and plen never decreases), draining the mbuf
+	 * pool and blackholing the link.  Ignore the broken report.
+	 */
+	if (letoh16(rp.max_acl_size) == 0)
+		return;
+
 	if ((unit->hci_flags & BTF_INIT_BUFFER_SIZE) == 0)
 		return;
 
--- a/sys/netbt/hci_misc.c	2026-10-10 22:45:15.649479095 +0000
+++ b/sys/netbt/hci_misc.c	2026-10-10 22:45:31.789267549 +0000
@@ -49,6 +49,11 @@
 int hci_memo_expiry = 600;
 
 /*
+ * upper bound on cached inquiry memos per unit [sysctl]
+ */
+int hci_memo_max = 1024;
+
+/*
  * set 'src' address for routing to 'dest'
  */
 int
@@ -145,9 +150,25 @@
 hci_memo_new(struct hci_unit *unit, bdaddr_t *bdaddr)
 {
 	struct hci_memo *memo;
+	int count;
 
 	memo = hci_memo_find(unit, bdaddr);
 	if (memo == NULL) {
+		/*
+		 * DF-5736: inquiry/RSSI result events (hci_event.c) can
+		 * manufacture memos without bound; expired entries are
+		 * only reaped inside hci_memo_find lookups.  Cap the cache
+		 * so a hostile controller (or forged result flood) cannot
+		 * exhaust kernel memory.
+		 */
+		count = 0;
+		LIST_FOREACH(memo, &unit->hci_memos, next)
+			count++;
+		if (count >= hci_memo_max) {
+			DPRINTFN(0, "memo cache full (%d)\n", count);
+			return NULL;
+		}
+
 		memo = kmalloc(sizeof(struct hci_memo),
 			M_BLUETOOTH, M_NOWAIT | M_ZERO);