From: Oleh Konko <security@1seal.org> 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.rej | 195 ++++++++++++++++++++++++++++++++++ 1 file changed, 195 insertions(+) create mode 100644 net/bluetooth/hci_event.c.rej diff --git a/net/bluetooth/hci_event.c.rej b/net/bluetooth/hci_event.c.rej new file mode 100644 index 000000000000..99f9b2368f7d --- /dev/null +++ b/net/bluetooth/hci_event.c.rej @@ -0,0 +1,195 @@ +diff a/net/bluetooth/hci_event.c b/net/bluetooth/hci_event.c (rejected hunks) +@@ -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) { -- 2.34.1