--- a/sys/netbt/hci_unit.c 2026-10-11 08:02:59.057438200 +0000 +++ b/sys/netbt/hci_unit.c 2026-10-11 08:36:08.935790902 +0000 @@ -392,12 +392,19 @@ DPRINTFN(11, "(%s) complete SCO\n", device_get_nameunit(unit->hci_dev)); + /* + * Dispatch under hci_devlock: cannot race a syscall-context + * teardown (sco_disconnect: hl_sco = NULL; hci_link_free) + * on the same link list. + */ + lockmgr(&unit->hci_devlock, LK_EXCLUSIVE); TAILQ_FOREACH(link, &unit->hci_links, hl_next) { if (link == M_GETCTX(m, struct hci_link *)) { hci_sco_complete(link, 1); break; } } + lockmgr(&unit->hci_devlock, LK_RELEASE); unit->hci_num_sco_pkts++; m_freem(m); --- a/sys/netbt/sco_upper.c 2026-10-11 08:02:59.061438148 +0000 +++ b/sys/netbt/sco_upper.c 2026-10-11 08:36:08.919791108 +0000 @@ -212,9 +212,20 @@ err = hci_send_cmd(sco->hl_unit, HCI_CMD_DISCONNECT, &cp, sizeof(cp)); if (err || linger == 0) { + struct hci_unit *unit = sco->hl_unit; + + /* + * Tear the pcb out of the link and free the link under + * hci_devlock: the netisr scodone dispatch in hci_intr() + * walks hci_links and calls hci_sco_complete() under the + * same lock, so hl_sco can no longer be observed NULL + * (or the link freed) mid-dispatch from this path. + */ + lockmgr(&unit->hci_devlock, LK_EXCLUSIVE); sco->hl_sco = NULL; pcb->sp_link = NULL; hci_link_free(sco, err); + lockmgr(&unit->hci_devlock, LK_RELEASE); } return err;