]> git.ipfire.org Git - thirdparty/kernel/linux.git/commitdiff
Bluetooth: hci_event: move wake reason storage into validated event handlers
authorOleh Konko <security@1seal.org>
Thu, 26 Mar 2026 17:31:24 +0000 (17:31 +0000)
committerLuiz Augusto von Dentz <luiz.von.dentz@intel.com>
Wed, 1 Apr 2026 20:44:15 +0000 (16:44 -0400)
hci_store_wake_reason() is called from hci_event_packet() immediately
after stripping the HCI event header but before hci_event_func()
enforces the per-event minimum payload length from hci_ev_table.
This means a short HCI event frame can reach bacpy() before any bounds
check runs.

Rather than duplicating skb parsing and per-event length checks inside
hci_store_wake_reason(), move wake-address storage into the individual
event handlers after their existing event-length validation has
succeeded. Convert hci_store_wake_reason() into a small helper that only
stores an already-validated bdaddr while the caller holds hci_dev_lock().
Use the same helper after hci_event_func() with a NULL address to
preserve the existing unexpected-wake fallback semantics when no
validated event handler records a wake address.

Annotate the helper with __must_hold(&hdev->lock) and add
lockdep_assert_held(&hdev->lock) so future call paths keep the lock
contract explicit.

Call the helper from hci_conn_request_evt(), hci_conn_complete_evt(),
hci_sync_conn_complete_evt(), le_conn_complete_evt(),
hci_le_adv_report_evt(), hci_le_ext_adv_report_evt(),
hci_le_direct_adv_report_evt(), hci_le_pa_sync_established_evt(), and
hci_le_past_received_evt().

Fixes: 2f20216c1d6f ("Bluetooth: Emit controller suspend and resume events")
Cc: stable@vger.kernel.org
Signed-off-by: Oleh Konko <security@1seal.org>
Signed-off-by: Luiz Augusto von Dentz <luiz.von.dentz@intel.com>
net/bluetooth/hci_event.c

index 286529d2e554a45a5cebc2b30cbfb1cb9ae10895..81d2f9a3eec96711757d3dfb38b9bf56200e0c07 100644 (file)
@@ -80,6 +80,10 @@ static void *hci_le_ev_skb_pull(struct hci_dev *hdev, struct sk_buff *skb,
        return data;
 }
 
+static void hci_store_wake_reason(struct hci_dev *hdev,
+                                 const bdaddr_t *bdaddr, u8 addr_type)
+       __must_hold(&hdev->lock);
+
 static u8 hci_cc_inquiry_cancel(struct hci_dev *hdev, void *data,
                                struct sk_buff *skb)
 {
@@ -3111,6 +3115,7 @@ static void hci_conn_complete_evt(struct hci_dev *hdev, void *data,
        bt_dev_dbg(hdev, "status 0x%2.2x", status);
 
        hci_dev_lock(hdev);
+       hci_store_wake_reason(hdev, &ev->bdaddr, BDADDR_BREDR);
 
        /* Check for existing connection:
         *
@@ -3274,6 +3279,10 @@ static void hci_conn_request_evt(struct hci_dev *hdev, void *data,
 
        bt_dev_dbg(hdev, "bdaddr %pMR type 0x%x", &ev->bdaddr, ev->link_type);
 
+       hci_dev_lock(hdev);
+       hci_store_wake_reason(hdev, &ev->bdaddr, BDADDR_BREDR);
+       hci_dev_unlock(hdev);
+
        /* Reject incoming connection from device with same BD ADDR against
         * CVE-2020-26555
         */
@@ -5021,6 +5030,7 @@ static void hci_sync_conn_complete_evt(struct hci_dev *hdev, void *data,
        bt_dev_dbg(hdev, "status 0x%2.2x", status);
 
        hci_dev_lock(hdev);
+       hci_store_wake_reason(hdev, &ev->bdaddr, BDADDR_BREDR);
 
        conn = hci_conn_hash_lookup_ba(hdev, ev->link_type, &ev->bdaddr);
        if (!conn) {
@@ -5713,6 +5723,7 @@ static void le_conn_complete_evt(struct hci_dev *hdev, u8 status,
        int err;
 
        hci_dev_lock(hdev);
+       hci_store_wake_reason(hdev, bdaddr, bdaddr_type);
 
        /* All controllers implicitly stop advertising in the event of a
         * connection, so ensure that the state bit is cleared.
@@ -6005,6 +6016,7 @@ static void hci_le_past_received_evt(struct hci_dev *hdev, void *data,
        bt_dev_dbg(hdev, "status 0x%2.2x", ev->status);
 
        hci_dev_lock(hdev);
+       hci_store_wake_reason(hdev, &ev->bdaddr, ev->bdaddr_type);
 
        hci_dev_clear_flag(hdev, HCI_PA_SYNC);
 
@@ -6403,6 +6415,8 @@ static void hci_le_adv_report_evt(struct hci_dev *hdev, void *data,
                                        info->length + 1))
                        break;
 
+               hci_store_wake_reason(hdev, &info->bdaddr, info->bdaddr_type);
+
                if (info->length <= max_adv_len(hdev)) {
                        rssi = info->data[info->length];
                        process_adv_report(hdev, info->type, &info->bdaddr,
@@ -6491,6 +6505,8 @@ static void hci_le_ext_adv_report_evt(struct hci_dev *hdev, void *data,
                                        info->length))
                        break;
 
+               hci_store_wake_reason(hdev, &info->bdaddr, info->bdaddr_type);
+
                evt_type = __le16_to_cpu(info->type) & LE_EXT_ADV_EVT_TYPE_MASK;
                legacy_evt_type = ext_evt_type_to_legacy(hdev, evt_type);
 
@@ -6536,6 +6552,7 @@ static void hci_le_pa_sync_established_evt(struct hci_dev *hdev, void *data,
        bt_dev_dbg(hdev, "status 0x%2.2x", ev->status);
 
        hci_dev_lock(hdev);
+       hci_store_wake_reason(hdev, &ev->bdaddr, ev->bdaddr_type);
 
        hci_dev_clear_flag(hdev, HCI_PA_SYNC);
 
@@ -6834,6 +6851,8 @@ static void hci_le_direct_adv_report_evt(struct hci_dev *hdev, void *data,
        for (i = 0; i < ev->num; i++) {
                struct hci_ev_le_direct_adv_info *info = &ev->info[i];
 
+               hci_store_wake_reason(hdev, &info->bdaddr, info->bdaddr_type);
+
                process_adv_report(hdev, info->type, &info->bdaddr,
                                   info->bdaddr_type, &info->direct_addr,
                                   info->direct_addr_type, HCI_ADV_PHY_1M, 0,
@@ -7517,73 +7536,29 @@ static bool hci_get_cmd_complete(struct hci_dev *hdev, u16 opcode,
        return true;
 }
 
-static void hci_store_wake_reason(struct hci_dev *hdev, u8 event,
-                                 struct sk_buff *skb)
+static void hci_store_wake_reason(struct hci_dev *hdev,
+                                 const bdaddr_t *bdaddr, u8 addr_type)
+       __must_hold(&hdev->lock)
 {
-       struct hci_ev_le_advertising_info *adv;
-       struct hci_ev_le_direct_adv_info *direct_adv;
-       struct hci_ev_le_ext_adv_info *ext_adv;
-       const struct hci_ev_conn_complete *conn_complete = (void *)skb->data;
-       const struct hci_ev_conn_request *conn_request = (void *)skb->data;
-
-       hci_dev_lock(hdev);
+       lockdep_assert_held(&hdev->lock);
 
        /* If we are currently suspended and this is the first BT event seen,
         * save the wake reason associated with the event.
         */
        if (!hdev->suspended || hdev->wake_reason)
-               goto unlock;
+               return;
+
+       if (!bdaddr) {
+               hdev->wake_reason = MGMT_WAKE_REASON_UNEXPECTED;
+               return;
+       }
 
        /* Default to remote wake. Values for wake_reason are documented in the
         * Bluez mgmt api docs.
         */
        hdev->wake_reason = MGMT_WAKE_REASON_REMOTE_WAKE;
-
-       /* Once configured for remote wakeup, we should only wake up for
-        * reconnections. It's useful to see which device is waking us up so
-        * keep track of the bdaddr of the connection event that woke us up.
-        */
-       if (event == HCI_EV_CONN_REQUEST) {
-               bacpy(&hdev->wake_addr, &conn_request->bdaddr);
-               hdev->wake_addr_type = BDADDR_BREDR;
-       } else if (event == HCI_EV_CONN_COMPLETE) {
-               bacpy(&hdev->wake_addr, &conn_complete->bdaddr);
-               hdev->wake_addr_type = BDADDR_BREDR;
-       } else if (event == HCI_EV_LE_META) {
-               struct hci_ev_le_meta *le_ev = (void *)skb->data;
-               u8 subevent = le_ev->subevent;
-               u8 *ptr = &skb->data[sizeof(*le_ev)];
-               u8 num_reports = *ptr;
-
-               if ((subevent == HCI_EV_LE_ADVERTISING_REPORT ||
-                    subevent == HCI_EV_LE_DIRECT_ADV_REPORT ||
-                    subevent == HCI_EV_LE_EXT_ADV_REPORT) &&
-                   num_reports) {
-                       adv = (void *)(ptr + 1);
-                       direct_adv = (void *)(ptr + 1);
-                       ext_adv = (void *)(ptr + 1);
-
-                       switch (subevent) {
-                       case HCI_EV_LE_ADVERTISING_REPORT:
-                               bacpy(&hdev->wake_addr, &adv->bdaddr);
-                               hdev->wake_addr_type = adv->bdaddr_type;
-                               break;
-                       case HCI_EV_LE_DIRECT_ADV_REPORT:
-                               bacpy(&hdev->wake_addr, &direct_adv->bdaddr);
-                               hdev->wake_addr_type = direct_adv->bdaddr_type;
-                               break;
-                       case HCI_EV_LE_EXT_ADV_REPORT:
-                               bacpy(&hdev->wake_addr, &ext_adv->bdaddr);
-                               hdev->wake_addr_type = ext_adv->bdaddr_type;
-                               break;
-                       }
-               }
-       } else {
-               hdev->wake_reason = MGMT_WAKE_REASON_UNEXPECTED;
-       }
-
-unlock:
-       hci_dev_unlock(hdev);
+       bacpy(&hdev->wake_addr, bdaddr);
+       hdev->wake_addr_type = addr_type;
 }
 
 #define HCI_EV_VL(_op, _func, _min_len, _max_len) \
@@ -7830,14 +7805,15 @@ void hci_event_packet(struct hci_dev *hdev, struct sk_buff *skb)
 
        skb_pull(skb, HCI_EVENT_HDR_SIZE);
 
-       /* Store wake reason if we're suspended */
-       hci_store_wake_reason(hdev, event, skb);
-
        bt_dev_dbg(hdev, "event 0x%2.2x", event);
 
        hci_event_func(hdev, event, skb, &opcode, &status, &req_complete,
                       &req_complete_skb);
 
+       hci_dev_lock(hdev);
+       hci_store_wake_reason(hdev, NULL, 0);
+       hci_dev_unlock(hdev);
+
        if (req_complete) {
                req_complete(hdev, status, opcode);
        } else if (req_complete_skb) {