Skip to content

Commit

Permalink
Bluetooth: check L2CAP length in first ACL fragment
Browse files Browse the repository at this point in the history
Current Bluetooth code assembles fragments of big L2CAP packets
in l2cap_recv_acldata and then checks allowed L2CAP size in
assemled L2CAP packet (pi->imtu < skb->len).

The patch moves allowed L2CAP size check to the early stage when
we receive the first fragment of L2CAP packet. We do not need to
reserve and keep L2CAP fragments for bad packets.

Updated version after comments from Mat Martineau <mathewm@codeaurora.org>
and Gustavo Padovan <padovan@profusion.mobi>.

Trace below is received when using stress tools sending big
fragmented L2CAP packets.
...
[ 1712.798492] swapper: page allocation failure. order:4, mode:0x4020
[ 1712.804809] [<c0031870>] (unwind_backtrace+0x0/0xdc) from [<c00a1f70>]
(__alloc_pages_nodemask+0x4)
[ 1712.814666] [<c00a1f70>] (__alloc_pages_nodemask+0x47c/0x4d4) from
[<c00a1fd8>] (__get_free_pages+)
[ 1712.824645] [<c00a1fd8>] (__get_free_pages+0x10/0x3c) from [<c026eb5c>]
(__alloc_skb+0x4c/0xfc)
[ 1712.833465] [<c026eb5c>] (__alloc_skb+0x4c/0xfc) from [<bf28c738>]
(l2cap_recv_acldata+0xf0/0x1f8 )
[ 1712.843322] [<bf28c738>] (l2cap_recv_acldata+0xf0/0x1f8 [l2cap]) from
[<bf0094ac>] (hci_rx_task+0x)
...

Signed-off-by: Andrei Emeltchenko <andrei.emeltchenko@nokia.com>
Acked-by: Marcel Holtmann <marcel@holtmann.org>
Signed-off-by: Gustavo F. Padovan <padovan@profusion.mobi>
  • Loading branch information
Andrei Emeltchenko authored and Gustavo F. Padovan committed Oct 12, 2010
1 parent 3cd0197 commit 8979481
Showing 1 changed file with 16 additions and 0 deletions.
16 changes: 16 additions & 0 deletions net/bluetooth/l2cap.c
Original file line number Diff line number Diff line change
Expand Up @@ -4663,6 +4663,8 @@ static int l2cap_recv_acldata(struct hci_conn *hcon, struct sk_buff *skb, u16 fl

if (flags & ACL_START) {
struct l2cap_hdr *hdr;
struct sock *sk;
u16 cid;
int len;

if (conn->rx_len) {
Expand All @@ -4681,6 +4683,7 @@ static int l2cap_recv_acldata(struct hci_conn *hcon, struct sk_buff *skb, u16 fl

hdr = (struct l2cap_hdr *) skb->data;
len = __le16_to_cpu(hdr->len) + L2CAP_HDR_SIZE;
cid = __le16_to_cpu(hdr->cid);

if (len == skb->len) {
/* Complete frame received */
Expand All @@ -4697,6 +4700,19 @@ static int l2cap_recv_acldata(struct hci_conn *hcon, struct sk_buff *skb, u16 fl
goto drop;
}

sk = l2cap_get_chan_by_scid(&conn->chan_list, cid);

if (sk && l2cap_pi(sk)->imtu < len - L2CAP_HDR_SIZE) {
BT_ERR("Frame exceeding recv MTU (len %d, MTU %d)",
len, l2cap_pi(sk)->imtu);
bh_unlock_sock(sk);
l2cap_conn_unreliable(conn, ECOMM);
goto drop;
}

if (sk)
bh_unlock_sock(sk);

/* Allocate skb for the complete frame (with header) */
conn->rx_skb = bt_skb_alloc(len, GFP_ATOMIC);
if (!conn->rx_skb)
Expand Down

0 comments on commit 8979481

Please sign in to comment.