hci_free_adv_monitor() removes a monitor from hdev->adv_monitors_idr and frees it, and its comment says the caller must hold hdev->lock. Readers such as read_adv_mon_features() walk adv_monitors_idr and dereference each monitor under hdev->lock. Since commit 7cf5c2978f23 ("Bluetooth: hci_sync: Refactor remove Adv Monitor"), MGMT_OP_REMOVE_ADV_MONITOR runs from the cmd_sync work, which holds only hci_req_sync_lock. On the HCI_ADV_MONITOR_EXT_NONE path, which is also taken while the controller is powered off, hci_remove_adv_monitor() then calls hci_free_adv_monitor() without hdev->lock. A concurrent MGMT_OP_READ_ADV_MONITOR_FEATURES can load a monitor pointer from the IDR and read monitor->handle after the monitor has been freed. The MSFT path already frees the monitor under hdev->lock, in msft_le_cancel_monitor_advertisement_cb(). Take hdev->lock around hci_free_adv_monitor() in hci_remove_adv_monitor() too. None of its callers hold hdev->lock. The missing lock was found with an LLM-based tool that checks callers against the locking rules stated in kernel comments. Fixes: 7cf5c2978f23 ("Bluetooth: hci_sync: Refactor remove Adv Monitor") Cc: stable@vger.kernel.org Signed-off-by: Praneeth Balasubramanian --- Notes: This was found by an LLM-based static analysis (GPT-5.6) that checks kernel code against the rules stated in its comments, as part of research at UCSB SecLab. The patch and changelog were drafted with Claude Opus 5.5. I reviewed the patch and take responsibility for it. MGMT commands need CAP_NET_ADMIN in the initial user namespace, so I am sending this as a regular fix rather than a security report. Testing: builds without new warnings with W=1 (x86_64, KASAN and lockdep config). Not runtime-tested. The patch applies to mainline 9ba46df34488 and to next-20261109. net/bluetooth/hci_core.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/net/bluetooth/hci_core.c b/net/bluetooth/hci_core.c index 74d9865e08c2..b66d3c295e5e 100644 --- a/net/bluetooth/hci_core.c +++ b/net/bluetooth/hci_core.c @@ -1966,7 +1966,10 @@ static int hci_remove_adv_monitor(struct hci_dev *hdev, if (status == -ENOENT) bt_dev_warn(hdev, "Removing monitor with no matching handle %d", monitor->handle); + + hci_dev_lock(hdev); hci_free_adv_monitor(hdev, monitor); + hci_dev_unlock(hdev); return status; } -- 2.25.1