Skip to content

Commit d0cbd28

Browse files
Vudentzkakra
authored andcommitted
Bluetooth: L2CAP: Fix handling fragmented length
Bluetooth Core Specification v5.2, Vol. 3, Part A, section 1.4, table 1.1: 'Start Fragments always either begin with the first octet of the Basic L2CAP header of a PDU or they have a length of zero (see [Vol 2] Part B, Section 6.6.2).' Apparently this was changed by the following errata: https://www.bluetooth.org/tse/errata_view.cfm?errata_id=10216 Signed-off-by: Luiz Augusto von Dentz <[email protected]> Signed-off-by: Marcel Holtmann <[email protected]>
1 parent 2c85ebc commit d0cbd28

File tree

2 files changed

+94
-25
lines changed

2 files changed

+94
-25
lines changed

include/net/bluetooth/l2cap.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -207,6 +207,7 @@ struct l2cap_hdr {
207207
__le16 len;
208208
__le16 cid;
209209
} __packed;
210+
#define L2CAP_LEN_SIZE 2
210211
#define L2CAP_HDR_SIZE 4
211212
#define L2CAP_ENH_HDR_SIZE 6
212213
#define L2CAP_EXT_HDR_SIZE 8

net/bluetooth/l2cap_core.c

Lines changed: 93 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -8270,10 +8270,73 @@ static void l2cap_security_cfm(struct hci_conn *hcon, u8 status, u8 encrypt)
82708270
mutex_unlock(&conn->chan_lock);
82718271
}
82728272

8273+
/* Append fragment into frame respecting the maximum len of rx_skb */
8274+
static int l2cap_recv_frag(struct l2cap_conn *conn, struct sk_buff *skb,
8275+
u16 len)
8276+
{
8277+
if (!conn->rx_skb) {
8278+
/* Allocate skb for the complete frame (with header) */
8279+
conn->rx_skb = bt_skb_alloc(len, GFP_KERNEL);
8280+
if (!conn->rx_skb)
8281+
return -ENOMEM;
8282+
/* Init rx_len */
8283+
conn->rx_len = len;
8284+
}
8285+
8286+
/* Copy as much as the rx_skb can hold */
8287+
len = min_t(u16, len, skb->len);
8288+
skb_copy_from_linear_data(skb, skb_put(conn->rx_skb, len), len);
8289+
skb_pull(skb, len);
8290+
conn->rx_len -= len;
8291+
8292+
return len;
8293+
}
8294+
8295+
static int l2cap_recv_len(struct l2cap_conn *conn, struct sk_buff *skb)
8296+
{
8297+
struct sk_buff *rx_skb;
8298+
int len;
8299+
8300+
/* Append just enough to complete the header */
8301+
len = l2cap_recv_frag(conn, skb, L2CAP_LEN_SIZE - conn->rx_skb->len);
8302+
8303+
/* If header could not be read just continue */
8304+
if (len < 0 || conn->rx_skb->len < L2CAP_LEN_SIZE)
8305+
return len;
8306+
8307+
rx_skb = conn->rx_skb;
8308+
len = get_unaligned_le16(rx_skb->data);
8309+
8310+
/* Check if rx_skb has enough space to received all fragments */
8311+
if (len + (L2CAP_HDR_SIZE - L2CAP_LEN_SIZE) <= skb_tailroom(rx_skb)) {
8312+
/* Update expected len */
8313+
conn->rx_len = len + (L2CAP_HDR_SIZE - L2CAP_LEN_SIZE);
8314+
return L2CAP_LEN_SIZE;
8315+
}
8316+
8317+
/* Reset conn->rx_skb since it will need to be reallocated in order to
8318+
* fit all fragments.
8319+
*/
8320+
conn->rx_skb = NULL;
8321+
8322+
/* Reallocates rx_skb using the exact expected length */
8323+
len = l2cap_recv_frag(conn, rx_skb,
8324+
len + (L2CAP_HDR_SIZE - L2CAP_LEN_SIZE));
8325+
kfree_skb(rx_skb);
8326+
8327+
return len;
8328+
}
8329+
8330+
static void l2cap_recv_reset(struct l2cap_conn *conn)
8331+
{
8332+
kfree_skb(conn->rx_skb);
8333+
conn->rx_skb = NULL;
8334+
conn->rx_len = 0;
8335+
}
8336+
82738337
void l2cap_recv_acldata(struct hci_conn *hcon, struct sk_buff *skb, u16 flags)
82748338
{
82758339
struct l2cap_conn *conn = hcon->l2cap_data;
8276-
struct l2cap_hdr *hdr;
82778340
int len;
82788341

82798342
/* For AMP controller do not create l2cap conn */
@@ -8292,23 +8355,23 @@ void l2cap_recv_acldata(struct hci_conn *hcon, struct sk_buff *skb, u16 flags)
82928355
case ACL_START:
82938356
case ACL_START_NO_FLUSH:
82948357
case ACL_COMPLETE:
8295-
if (conn->rx_len) {
8358+
if (conn->rx_skb) {
82968359
BT_ERR("Unexpected start frame (len %d)", skb->len);
8297-
kfree_skb(conn->rx_skb);
8298-
conn->rx_skb = NULL;
8299-
conn->rx_len = 0;
8360+
l2cap_recv_reset(conn);
83008361
l2cap_conn_unreliable(conn, ECOMM);
83018362
}
83028363

8303-
/* Start fragment always begin with Basic L2CAP header */
8304-
if (skb->len < L2CAP_HDR_SIZE) {
8305-
BT_ERR("Frame is too short (len %d)", skb->len);
8306-
l2cap_conn_unreliable(conn, ECOMM);
8307-
goto drop;
8364+
/* Start fragment may not contain the L2CAP length so just
8365+
* copy the initial byte when that happens and use conn->mtu as
8366+
* expected length.
8367+
*/
8368+
if (skb->len < L2CAP_LEN_SIZE) {
8369+
if (l2cap_recv_frag(conn, skb, conn->mtu) < 0)
8370+
goto drop;
8371+
return;
83088372
}
83098373

8310-
hdr = (struct l2cap_hdr *) skb->data;
8311-
len = __le16_to_cpu(hdr->len) + L2CAP_HDR_SIZE;
8374+
len = get_unaligned_le16(skb->data) + L2CAP_HDR_SIZE;
83128375

83138376
if (len == skb->len) {
83148377
/* Complete frame received */
@@ -8325,38 +8388,43 @@ void l2cap_recv_acldata(struct hci_conn *hcon, struct sk_buff *skb, u16 flags)
83258388
goto drop;
83268389
}
83278390

8328-
/* Allocate skb for the complete frame (with header) */
8329-
conn->rx_skb = bt_skb_alloc(len, GFP_KERNEL);
8330-
if (!conn->rx_skb)
8391+
/* Append fragment into frame (with header) */
8392+
if (l2cap_recv_frag(conn, skb, len) < 0)
83318393
goto drop;
83328394

8333-
skb_copy_from_linear_data(skb, skb_put(conn->rx_skb, skb->len),
8334-
skb->len);
8335-
conn->rx_len = len - skb->len;
83368395
break;
83378396

83388397
case ACL_CONT:
83398398
BT_DBG("Cont: frag len %d (expecting %d)", skb->len, conn->rx_len);
83408399

8341-
if (!conn->rx_len) {
8400+
if (!conn->rx_skb) {
83428401
BT_ERR("Unexpected continuation frame (len %d)", skb->len);
83438402
l2cap_conn_unreliable(conn, ECOMM);
83448403
goto drop;
83458404
}
83468405

8406+
/* Complete the L2CAP length if it has not been read */
8407+
if (conn->rx_skb->len < L2CAP_LEN_SIZE) {
8408+
if (l2cap_recv_len(conn, skb) < 0) {
8409+
l2cap_conn_unreliable(conn, ECOMM);
8410+
goto drop;
8411+
}
8412+
8413+
/* Header still could not be read just continue */
8414+
if (conn->rx_skb->len < L2CAP_LEN_SIZE)
8415+
return;
8416+
}
8417+
83478418
if (skb->len > conn->rx_len) {
83488419
BT_ERR("Fragment is too long (len %d, expected %d)",
83498420
skb->len, conn->rx_len);
8350-
kfree_skb(conn->rx_skb);
8351-
conn->rx_skb = NULL;
8352-
conn->rx_len = 0;
8421+
l2cap_recv_reset(conn);
83538422
l2cap_conn_unreliable(conn, ECOMM);
83548423
goto drop;
83558424
}
83568425

8357-
skb_copy_from_linear_data(skb, skb_put(conn->rx_skb, skb->len),
8358-
skb->len);
8359-
conn->rx_len -= skb->len;
8426+
/* Append fragment into frame (with header) */
8427+
l2cap_recv_frag(conn, skb, skb->len);
83608428

83618429
if (!conn->rx_len) {
83628430
/* Complete frame received. l2cap_recv_frame

0 commit comments

Comments
 (0)