CVE-2026-19935
Beschrijving NL
De Bluetooth LE-hostwachtrijen ontvingen L2CAP-verbindingsgeoriënteerde kanaal (CoC) -gegevens voor uitgestelde verwerking via een struct k_work ingebed in het kanaalobject (le_chan- >rx_work, handler l2cap_rx_process()) wanneer het kanaal een dynamische PSM gebruikt (0x0080-0x00FF). Kanaal teardown in l2cap_chan_destroy() in subsys/bluetooth/host/l2cap.c annuleert de heruitzending-time-out werk en voert de RX FIFO af, maar annuleert nooit rx_work. Omdat dat werkitem is ingediend bij de systeemwerkwachtrij terwijl HCI-ontvangstverwerking wordt uitgevoerd op de speciale Bluetooth RX-werkwachtrij (CONFIG_BT_RECV_WORKQ_BT, de standaard), kan een in de wachtrij geplaatst rx_work-item het kanaal waarnaar het verwijst overleven. Een externe, niet-geverifieerde peer met een gevestigd CoC-kanaal activeert dit door het verzenden van een data K-frame onmiddellijk gevolgd door een L2CAP Disconnect Request. Het K-frame legt rx_work voor aan de werkvoorraad van het systeem; omdat beide werkvoorraad threads coöperatief zijn en de Bluetooth RX-werkvoorraad op de hogere prioriteit draait (K_PRIO_COOP(CONFIG_BT_RX_PRIO) versus CONFIG_SYSTEM_WORKQUEUE_PRIORITY), kan het openstaande item niet worden uitgevoerd voordat het volgende verbrekingsverzoek is verwerkt in le_disconn_req() -> l2cap_chan_del() -> l2cap_chan_destroy(). De stack roept vervolgens de vrijgegeven() callback aan, die de API documenteert, wat betekent dat de stack alle referenties heeft laten vallen en dat de applicatie het kanaalgeheugen kan vrijmaken. De applicatie maakt daarom een object vrij of accepteert het opnieuw in een object dat de systeemwerkwachtrij nog steeds in zijn wachtlijst heeft staan. Als het geheugen wordt vrijgegeven en opnieuw toegewezen, verwijdert de werkvoorraad later een lijstknooppunt en een handler-functieaanwijzer uit het hergebruikte geheugen; als het object opnieuw wordt gebruikt voor een latere verbinding, roept l2cap_chan_add() k_work_init() aan op een werkitem dat nog steeds in de wachtrij staat, waardoor de wachtlijst van de werkvoorraad wordt beschadigd, zodat niet-gerelateerde werkitems worden verwijderd of de wachtrij in een luslijst draait. Een verwante variant laat l2cap_rx_process() gelijktijdig met de afbraak lopen, waarbij de net_buf unref van le_chan- >_sdu en de clearing van chan- >conn worden geracet. De fix leidt het kanaal RX naar de Bluetooth-werkwachtrij - dezelfde context waarin elk demontagepad wordt uitgevoerd - en voegt een expliciete k_work_cancel() van le_chan- >rx_work toe in l2cap_chan_destroy(), dus geen verwijzing naar het kanaal overleeft de vrijgegeven() callback. Configuraties zonder CONFIG_BT_L2CAP_DYNAMIC_CHANNEL, of die alleen SIG-toegewezen PSM's gebruiken (EATT 0x0027, OTS 0x0025) die het inline ontvangstpad nemen, worden niet beïnvloed.
Origineel (Engels) tonen
The Bluetooth LE host queues received L2CAP connection-oriented channel (CoC) data for deferred processing through a struct k_work embedded in the channel object (le_chan->rx_work, handler l2cap_rx_process()) whenever the channel uses a dynamic PSM (0x0080-0x00FF). Channel teardown in l2cap_chan_destroy() in subsys/bluetooth/host/l2cap.c cancels the retransmission-timeout work and drains the RX FIFO, but never cancels rx_work. Because that work item was submitted to the system workqueue while HCI receive processing runs on the dedicated Bluetooth RX workqueue (CONFIG_BT_RECV_WORKQ_BT, the default), a queued rx_work item can outlive the channel it points into.
A remote, unauthenticated peer with an established CoC channel triggers this by sending a data K-frame immediately followed by an L2CAP Disconnect Request. The K-frame submits rx_work to the system workqueue; because both workqueue threads are cooperative and the Bluetooth RX workqueue runs at the higher priority (K_PRIO_COOP(CONFIG_BT_RX_PRIO) versus CONFIG_SYSTEM_WORKQUEUE_PRIORITY), the pending item cannot run before the following Disconnect Request is processed in le_disconn_req() -> l2cap_chan_del() -> l2cap_chan_destroy(). The stack then invokes the released() callback, which the API documents as meaning the stack has dropped all references and the application may free the channel memory.
The application therefore frees or re-accepts into an object that the system workqueue still holds in its pending list. If the memory is freed and reallocated, the workqueue later dereferences a list node and a handler function pointer read from reused memory; if the object is re-used for a later connection, l2cap_chan_add() calls k_work_init() on a still-enqueued work item, corrupting the workqueue's pending list so that unrelated work items are dropped or the queue spins on a looped list. A related variant lets l2cap_rx_process() run concurrently with teardown, racing the net_buf unref of le_chan->_sdu and the clearing of chan->conn.
The fix routes the channel RX work to the Bluetooth workqueue - the same context in which every teardown path runs - and adds an explicit k_work_cancel() of le_chan->rx_work in l2cap_chan_destroy(), so no reference to the channel survives the released() callback. Configurations without CONFIG_BT_L2CAP_DYNAMIC_CHANNEL, or that only use SIG-assigned PSMs (EATT 0x0027, OTS 0x0025) which take the inline receive path, are not affected.