From 099a418305eb3d7227d4e2124322a7fe353d8a1e Mon Sep 17 00:00:00 2001 From: Emil Gydesen Date: Thu, 19 Dec 2024 15:18:32 +0100 Subject: [PATCH] Bluetooth: ATT: Disconnect ATT and ACL when receiving inval rsp When we receive a response from a server we do not have an outstanding request with, we disconnect the connection rather than just ignoring it. The reason for this is that the remote server is not ensuring correct ATT flow control, which means that we cannot trust future responses from the server. Signed-off-by: Emil Gydesen --- subsys/bluetooth/host/att.c | 37 ++++++++++++++++++++++++++++--------- 1 file changed, 28 insertions(+), 9 deletions(-) diff --git a/subsys/bluetooth/host/att.c b/subsys/bluetooth/host/att.c index 2773177ac542c..1ae38457255ba 100644 --- a/subsys/bluetooth/host/att.c +++ b/subsys/bluetooth/host/att.c @@ -6,6 +6,8 @@ * SPDX-License-Identifier: Apache-2.0 */ +#include +#include #include #include #include @@ -295,6 +297,28 @@ struct net_buf *bt_att_create_rsp_pdu(struct bt_att_chan *chan, uint8_t op); static void bt_att_sent(struct bt_l2cap_chan *ch); +static void att_disconnect(struct bt_att_chan *chan) +{ + char addr[BT_ADDR_LE_STR_LEN]; + int err; + + /* In rare circumstances we are "forced" to disconnect the ATT bearer and the ACL. + * Examples of when this is right course of action is when there is an ATT timeout, we + * receive an unexpected response from the server, or the response from the server is + * invalid + */ + + bt_addr_le_to_str(bt_conn_get_dst(chan->att->conn), addr, sizeof(addr)); + LOG_DBG("ATT disconnecting device %s", addr); + + bt_att_disconnected(&chan->chan.chan); + + err = bt_conn_disconnect(chan->chan.chan.conn, BT_HCI_ERR_REMOTE_USER_TERM_CONN); + if (err) { + LOG_ERR("Disconnecting failed (err %d)", err); + } +} + static void att_sent(void *user_data) { struct bt_att_tx_meta_data *data = user_data; @@ -930,7 +954,8 @@ static uint8_t att_handle_rsp(struct bt_att_chan *chan, void *pdu, uint16_t len, if (!chan->req) { LOG_WRN("No pending ATT request"); - goto process; + att_disconnect(chan); + return 0; /* Returning a non-0 value would attempt to send an error response */ } /* Check if request has been cancelled */ @@ -3139,9 +3164,7 @@ static void att_timeout(struct k_work *work) { char addr[BT_ADDR_LE_STR_LEN]; struct k_work_delayable *dwork = k_work_delayable_from_work(work); - struct bt_att_chan *chan = CONTAINER_OF(dwork, struct bt_att_chan, - timeout_work); - int err; + struct bt_att_chan *chan = CONTAINER_OF(dwork, struct bt_att_chan, timeout_work); bt_addr_le_to_str(bt_conn_get_dst(chan->att->conn), addr, sizeof(addr)); LOG_ERR("ATT Timeout for device %s. Disconnecting...", addr); @@ -3154,17 +3177,13 @@ static void att_timeout(struct k_work *work) * requests, commands, indications or notifications shall be sent to the * target device on this ATT Bearer. */ - bt_att_disconnected(&chan->chan.chan); /* The timeout state is local and can block new ATT operations, but does not affect the * remote side. Disconnecting the GATT connection upon ATT timeout simplifies error handling * for developers. This reduces rare failure conditions to a common one, allowing developers * to handle unexpected disconnections without needing special cases for ATT timeouts. */ - err = bt_conn_disconnect(chan->chan.chan.conn, BT_HCI_ERR_REMOTE_USER_TERM_CONN); - if (err) { - LOG_ERR("Disconnecting failed (err %d)", err); - } + att_disconnect(chan); } static struct bt_att_chan *att_get_fixed_chan(struct bt_conn *conn)