From 7f727ac322ab99084c3444bc2546e5777e2e5a3c Mon Sep 17 00:00:00 2001 From: Vinayak Kariappa Chettimada Date: Sat, 13 Mar 2021 10:16:40 +0530 Subject: [PATCH] Bluetooth: controller: Fix regression in ctrl tx queue handling Fix control Tx buffer leak into data Tx pool that happens after a cross-over control procedure response was paused due to currently active encryption setup procedure, and a new control Tx PDU in addition to the paused one is enqueued thereafter. When the control tx PDUs is resumed but not yet enqueued towards the radio, if there is a new control Tx PDU enqueued then the paused control Tx PDU is not set as the head of the control PDUs in the Tx queue. This caused the paused control Tx PDU to be associated with data Tx pool, hence causing the incorrect release into data Tx pool. Relates to the commit bff76b4cceff ("Bluetooth: controller: split: Fix control tx queue handling") and to the commit 6991d09977ba ("Bluetooth: controller: Fix control tx queue handling"). Fixes #32898. Signed-off-by: Vinayak Kariappa Chettimada --- subsys/bluetooth/controller/ll_sw/ull_conn.c | 38 +++++++++++++------- 1 file changed, 26 insertions(+), 12 deletions(-) diff --git a/subsys/bluetooth/controller/ll_sw/ull_conn.c b/subsys/bluetooth/controller/ll_sw/ull_conn.c index 0772ab3ee1e..85a0b573ba5 100644 --- a/subsys/bluetooth/controller/ll_sw/ull_conn.c +++ b/subsys/bluetooth/controller/ll_sw/ull_conn.c @@ -91,6 +91,7 @@ static inline void event_ch_map_prep(struct ll_conn *conn, uint16_t event_counter); #if defined(CONFIG_BT_CTLR_LE_ENC) +static inline void ctrl_tx_check_and_resume(struct ll_conn *conn); static bool is_enc_req_pause_tx(struct ll_conn *conn); static inline void event_enc_prep(struct ll_conn *conn); #if defined(CONFIG_BT_PERIPHERAL) @@ -1868,18 +1869,11 @@ static void tx_demux(void *param) static struct node_tx *tx_ull_dequeue(struct ll_conn *conn, struct node_tx *tx) { +#if defined(CONFIG_BT_CTLR_LE_ENC) if (!conn->tx_ctrl && (conn->tx_head != conn->tx_data)) { - struct pdu_data *pdu_data_tx; - - pdu_data_tx = (void *)conn->tx_head->pdu; - if ((pdu_data_tx->ll_id != PDU_DATA_LLID_CTRL) || - ((pdu_data_tx->llctrl.opcode != - PDU_DATA_LLCTRL_TYPE_ENC_REQ) && - (pdu_data_tx->llctrl.opcode != - PDU_DATA_LLCTRL_TYPE_PAUSE_ENC_REQ))) { - conn->tx_ctrl = conn->tx_ctrl_last = conn->tx_head; - } + ctrl_tx_check_and_resume(conn); } +#endif /* CONFIG_BT_CTLR_LE_ENC */ if (conn->tx_head == conn->tx_ctrl) { conn->tx_head = conn->tx_head->next; @@ -1990,8 +1984,24 @@ static int empty_data_start_release(struct ll_conn *conn, struct node_tx *tx) } #endif /* CONFIG_BT_CTLR_LLID_DATA_START_EMPTY */ -static void ctrl_tx_last_enqueue(struct ll_conn *conn, - struct node_tx *tx) +#if defined(CONFIG_BT_CTLR_LE_ENC) +static inline void ctrl_tx_check_and_resume(struct ll_conn *conn) +{ + struct pdu_data *pdu_data_tx; + + pdu_data_tx = (void *)conn->tx_head->pdu; + if ((pdu_data_tx->ll_id != PDU_DATA_LLID_CTRL) || + ((pdu_data_tx->llctrl.opcode != + PDU_DATA_LLCTRL_TYPE_ENC_REQ) && + (pdu_data_tx->llctrl.opcode != + PDU_DATA_LLCTRL_TYPE_PAUSE_ENC_REQ))) { + conn->tx_ctrl = conn->tx_ctrl_last = conn->tx_head; + } +} +#endif /* CONFIG_BT_CTLR_LE_ENC */ + +static inline void ctrl_tx_last_enqueue(struct ll_conn *conn, + struct node_tx *tx) { tx->next = conn->tx_ctrl_last->next; conn->tx_ctrl_last->next = tx; @@ -2022,6 +2032,10 @@ static inline void ctrl_tx_pause_enqueue(struct ll_conn *conn, */ if (conn->tx_head == conn->tx_data) { conn->tx_data = conn->tx_data->next; +#if defined(CONFIG_BT_CTLR_LE_ENC) + } else if (!conn->tx_ctrl) { + ctrl_tx_check_and_resume(conn); +#endif /* CONFIG_BT_CTLR_LE_ENC */ } /* if no ctrl packet already queued, new ctrl added will be