Skip to content

Commit 4d7ea8e

Browse files
Vudentzholtmann
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 5ff20cb commit 4d7ea8e

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
@@ -8276,10 +8276,73 @@ static void l2cap_security_cfm(struct hci_conn *hcon, u8 status, u8 encrypt)
82768276
mutex_unlock(&conn->chan_lock);
82778277
}
82788278

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

82858348
/* For AMP controller do not create l2cap conn */
@@ -8298,23 +8361,23 @@ void l2cap_recv_acldata(struct hci_conn *hcon, struct sk_buff *skb, u16 flags)
82988361
case ACL_START:
82998362
case ACL_START_NO_FLUSH:
83008363
case ACL_COMPLETE:
8301-
if (conn->rx_len) {
8364+
if (conn->rx_skb) {
83028365
BT_ERR("Unexpected start frame (len %d)", skb->len);
8303-
kfree_skb(conn->rx_skb);
8304-
conn->rx_skb = NULL;
8305-
conn->rx_len = 0;
8366+
l2cap_recv_reset(conn);
83068367
l2cap_conn_unreliable(conn, ECOMM);
83078368
}
83088369

8309-
/* Start fragment always begin with Basic L2CAP header */
8310-
if (skb->len < L2CAP_HDR_SIZE) {
8311-
BT_ERR("Frame is too short (len %d)", skb->len);
8312-
l2cap_conn_unreliable(conn, ECOMM);
8313-
goto drop;
8370+
/* Start fragment may not contain the L2CAP length so just
8371+
* copy the initial byte when that happens and use conn->mtu as
8372+
* expected length.
8373+
*/
8374+
if (skb->len < L2CAP_LEN_SIZE) {
8375+
if (l2cap_recv_frag(conn, skb, conn->mtu) < 0)
8376+
goto drop;
8377+
return;
83148378
}
83158379

8316-
hdr = (struct l2cap_hdr *) skb->data;
8317-
len = __le16_to_cpu(hdr->len) + L2CAP_HDR_SIZE;
8380+
len = get_unaligned_le16(skb->data) + L2CAP_HDR_SIZE;
83188381

83198382
if (len == skb->len) {
83208383
/* Complete frame received */
@@ -8331,38 +8394,43 @@ void l2cap_recv_acldata(struct hci_conn *hcon, struct sk_buff *skb, u16 flags)
83318394
goto drop;
83328395
}
83338396

8334-
/* Allocate skb for the complete frame (with header) */
8335-
conn->rx_skb = bt_skb_alloc(len, GFP_KERNEL);
8336-
if (!conn->rx_skb)
8397+
/* Append fragment into frame (with header) */
8398+
if (l2cap_recv_frag(conn, skb, len) < 0)
83378399
goto drop;
83388400

8339-
skb_copy_from_linear_data(skb, skb_put(conn->rx_skb, skb->len),
8340-
skb->len);
8341-
conn->rx_len = len - skb->len;
83428401
break;
83438402

83448403
case ACL_CONT:
83458404
BT_DBG("Cont: frag len %d (expecting %d)", skb->len, conn->rx_len);
83468405

8347-
if (!conn->rx_len) {
8406+
if (!conn->rx_skb) {
83488407
BT_ERR("Unexpected continuation frame (len %d)", skb->len);
83498408
l2cap_conn_unreliable(conn, ECOMM);
83508409
goto drop;
83518410
}
83528411

8412+
/* Complete the L2CAP length if it has not been read */
8413+
if (conn->rx_skb->len < L2CAP_LEN_SIZE) {
8414+
if (l2cap_recv_len(conn, skb) < 0) {
8415+
l2cap_conn_unreliable(conn, ECOMM);
8416+
goto drop;
8417+
}
8418+
8419+
/* Header still could not be read just continue */
8420+
if (conn->rx_skb->len < L2CAP_LEN_SIZE)
8421+
return;
8422+
}
8423+
83538424
if (skb->len > conn->rx_len) {
83548425
BT_ERR("Fragment is too long (len %d, expected %d)",
83558426
skb->len, conn->rx_len);
8356-
kfree_skb(conn->rx_skb);
8357-
conn->rx_skb = NULL;
8358-
conn->rx_len = 0;
8427+
l2cap_recv_reset(conn);
83598428
l2cap_conn_unreliable(conn, ECOMM);
83608429
goto drop;
83618430
}
83628431

8363-
skb_copy_from_linear_data(skb, skb_put(conn->rx_skb, skb->len),
8364-
skb->len);
8365-
conn->rx_len -= skb->len;
8432+
/* Append fragment into frame (with header) */
8433+
l2cap_recv_frag(conn, skb, skb->len);
83668434

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

0 commit comments

Comments
 (0)