Bluetooth: ATT: Fix not restoring buffer state when send fails

Since bt_l2cap_send_cb can fail returning its error is not enough as
the buffer has been modified to add the headers, so this save the state
before calling bt_conn_send_cb and takes a reference so it can be
restored its original state in case of error.

Fixes #27434

Signed-off-by: Luiz Augusto von Dentz <luiz.von.dentz@intel.com>
This commit is contained in:
Luiz Augusto von Dentz 2020-08-11 13:47:06 -07:00 committed by Johan Hedberg
commit 426fb82bd8

View file

@ -147,6 +147,8 @@ static int chan_send(struct bt_att_chan *chan, struct net_buf *buf,
bt_att_chan_sent_t cb)
{
struct bt_att_hdr *hdr;
struct net_buf_simple_state state;
int err;
hdr = (void *)buf->data;
@ -154,8 +156,6 @@ static int chan_send(struct bt_att_chan *chan, struct net_buf *buf,
if (IS_ENABLED(CONFIG_BT_EATT) &&
atomic_test_bit(chan->flags, ATT_ENHANCED)) {
int err;
/* Check if sent is pending already, if it does it cannot be
* modified so the operation will need to be queued.
*/
@ -198,10 +198,23 @@ static int chan_send(struct bt_att_chan *chan, struct net_buf *buf,
}
}
net_buf_simple_save(&buf->b, &state);
chan->sent = cb ? cb : chan_cb(buf);
return bt_l2cap_send_cb(chan->att->conn, BT_L2CAP_CID_ATT, buf,
att_cb(chan->sent), &chan->chan.chan);
/* Take a ref since bt_l2cap_send_cb takes ownership of the buffer */
err = bt_l2cap_send_cb(chan->att->conn, BT_L2CAP_CID_ATT,
net_buf_ref(buf), att_cb(chan->sent),
&chan->chan.chan);
if (!err) {
net_buf_unref(buf);
return 0;
}
net_buf_simple_restore(&buf->b, &state);
return err;
}
static int process_queue(struct bt_att_chan *chan, struct k_fifo *queue)
@ -241,11 +254,16 @@ static int chan_req_send(struct bt_att_chan *chan, struct bt_att_req *req)
/* Save request state so it can be resent */
net_buf_simple_save(&req->buf->b, &req->state);
/* Keep a reference for resending in case of an error */
/* Keep a reference for resending the req in case the security
* needs to be changed.
*/
err = chan_send(chan, net_buf_ref(req->buf), NULL);
if (err) {
/* Drop the extra reference if buffer could not be sent but
* don't reset the buffer as it will likelly be pushed back to
* request queue to be send later.
*/
net_buf_unref(req->buf);
chan->req = NULL;
}
return err;