From f4291901c65e5daa56277f5ea0ce9ba81f549f91 Mon Sep 17 00:00:00 2001 From: Guy Mishol Date: Fri, 17 Jan 2025 15:21:16 +0200 Subject: [PATCH] host: l2cap: avoid nested ble_hs_lock when disconnecting due to invalid packet When receiving L2CAP packet which checked to be invalid and a disconnection is required we get into a nested lock (the first is in ble_hs_hci_evt_acl_process which wraps ble_l2cap_rx and the second is inside ble_l2cap_sig_tx which is part of the ble_l2cap_disconnect API). Update the lock sequence to avoid it. host: l2cap: avoid nested ble_hs_lock when disconnecting due to invalid packet When receiving L2CAP packet which checked to be invalid and a disconnection is required we get into a nested lock (the first is in ble_hs_hci_evt_acl_process which wraps ble_l2cap_rx and the second is inside ble_l2cap_sig_tx which is part of the ble_l2cap_disconnect API). Update the lock sequence to avoid it. --- nimble/host/src/ble_hs_hci_evt.c | 5 +++-- nimble/host/src/ble_l2cap.c | 12 +++++++++++- 2 files changed, 14 insertions(+), 3 deletions(-) diff --git a/nimble/host/src/ble_hs_hci_evt.c b/nimble/host/src/ble_hs_hci_evt.c index 47430423be..3c2bf1bd0d 100644 --- a/nimble/host/src/ble_hs_hci_evt.c +++ b/nimble/host/src/ble_hs_hci_evt.c @@ -1131,6 +1131,9 @@ ble_hs_hci_evt_acl_process(struct os_mbuf *om) ble_hs_lock(); conn = ble_hs_conn_find(conn_handle); + + ble_hs_unlock(); + if (conn == NULL) { /* Peer not connected; quietly discard packet. */ rc = BLE_HS_ENOTCONN; @@ -1141,8 +1144,6 @@ ble_hs_hci_evt_acl_process(struct os_mbuf *om) om = NULL; } - ble_hs_unlock(); - switch (rc) { case 0: /* Final fragment received. */ diff --git a/nimble/host/src/ble_l2cap.c b/nimble/host/src/ble_l2cap.c index 50fe18c651..0bcaa75155 100644 --- a/nimble/host/src/ble_l2cap.c +++ b/nimble/host/src/ble_l2cap.c @@ -354,6 +354,8 @@ ble_l2cap_rx(struct ble_hs_conn *conn, uint8_t pb; int rc; + ble_hs_lock(); + *out_reject_cid = -1; pb = BLE_HCI_DATA_PB(hci_hdr->hdh_handle_pb_bc); @@ -362,6 +364,7 @@ ble_l2cap_rx(struct ble_hs_conn *conn, /* First fragment. */ rc = ble_l2cap_parse_hdr(om, 0, &l2cap_hdr); if (rc != 0) { + ble_hs_unlock(); goto err; } @@ -381,6 +384,7 @@ ble_l2cap_rx(struct ble_hs_conn *conn, l2cap_hdr.cid); *out_reject_cid = l2cap_hdr.cid; } + ble_hs_unlock(); goto err; } @@ -389,8 +393,9 @@ ble_l2cap_rx(struct ble_hs_conn *conn, /* Data exceeds MPS */ BLE_HS_LOG(ERROR, "error: sdu_len > chan->my_coc_mps (%d>%d)\n", l2cap_hdr.len, chan->my_coc_mps); - ble_l2cap_disconnect(chan); rc = BLE_HS_EBADDATA; + ble_hs_unlock(); + ble_l2cap_disconnect(chan); goto err; } @@ -404,6 +409,7 @@ ble_l2cap_rx(struct ble_hs_conn *conn, * Disconnect peer with invalid behaviour */ rc = BLE_HS_EBADDATA; + ble_hs_unlock(); ble_l2cap_disconnect(chan); goto err; } @@ -418,21 +424,25 @@ ble_l2cap_rx(struct ble_hs_conn *conn, if (chan == NULL || chan->rx_buf == NULL) { /* Middle fragment without the start. Discard new packet. */ rc = BLE_HS_EBADDATA; + ble_hs_unlock(); goto err; } break; default: rc = BLE_HS_EBADDATA; + ble_hs_unlock(); goto err; } rc = ble_l2cap_rx_payload(conn, chan, om, out_rx_cb); om = NULL; if (rc != 0) { + ble_hs_unlock(); goto err; } + ble_hs_unlock(); return 0; err: