All of lore.kernel.org
 help / color / mirror / Atom feed
* [RFC] Bluetooth: L2CAP: Fix to handling fragmented header
@ 2020-07-28  7:04 Luiz Augusto von Dentz
  2020-07-28  7:22 ` Marcel Holtmann
  0 siblings, 1 reply; 3+ messages in thread
From: Luiz Augusto von Dentz @ 2020-07-28  7:04 UTC (permalink / raw)
  To: linux-bluetooth

From: Luiz Augusto von Dentz <luiz.von.dentz@intel.com>

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).'

This text has been changed recently as it previously stated:

 'Start Fragments always begin with the Basic L2CAP header of a PDU.'

Apparently this was changed by the following errata:

https://www.bluetooth.org/tse/errata_view.cfm?errata_id=10216

In past this has not been a problem but it seems new controllers are
apparently doing it as it has been reported in Zephyr:

https://github.com/zephyrproject-rtos/zephyr/issues/26900

Signed-off-by: Luiz Augusto von Dentz <luiz.von.dentz@intel.com>
---
 net/bluetooth/l2cap_core.c | 104 +++++++++++++++++++++++++++++--------
 1 file changed, 83 insertions(+), 21 deletions(-)

diff --git a/net/bluetooth/l2cap_core.c b/net/bluetooth/l2cap_core.c
index ade83e224567..193bea314222 100644
--- a/net/bluetooth/l2cap_core.c
+++ b/net/bluetooth/l2cap_core.c
@@ -8269,6 +8269,63 @@ static void l2cap_security_cfm(struct hci_conn *hcon, u8 status, u8 encrypt)
 	mutex_unlock(&conn->chan_lock);
 }
 
+/* Append fragment into frame respecting the maximum len of rx_skb */
+static int l2cap_recv_frag(struct l2cap_conn *conn, struct sk_buff *skb,
+			   u16 len)
+{
+	if (!conn->rx_skb) {
+		/* Allocate skb for the complete frame (with header) */
+		conn->rx_skb = bt_skb_alloc(len, GFP_KERNEL);
+		if (!conn->rx_skb)
+			return -ENOMEM;
+		/* Init rx_len */
+		conn->rx_len = len;
+	}
+
+	/* Copy as much as the rx_skb can hold */
+	len = min_t(u16, len, skb->len);
+	skb_copy_from_linear_data(skb, skb_put(conn->rx_skb, len), len);
+	skb_pull(skb, len);
+	conn->rx_len -= len;
+
+	return len;
+}
+
+static int l2cap_recv_header(struct l2cap_conn *conn, struct sk_buff *skb)
+{
+	struct l2cap_hdr *hdr;
+	struct sk_buff *rx_skb;
+	int len;
+
+	/* Append just enough to complete the header */
+	len = l2cap_recv_frag(conn, skb, L2CAP_HDR_SIZE - conn->rx_skb->len);
+
+	/* If header could not be read just continue */
+	if (len < 0 || conn->rx_skb->len < L2CAP_HDR_SIZE)
+		return len;
+
+	rx_skb = conn->rx_skb;
+	conn->rx_skb = NULL;
+
+	hdr = (struct l2cap_hdr *) rx_skb->data;
+
+	/* Append existing rx_skb as that allocates only enough for the
+	 * headers.
+	 */
+	len = l2cap_recv_frag(conn, rx_skb,
+			      __le16_to_cpu(hdr->len) + L2CAP_HDR_SIZE);
+	kfree_skb(rx_skb);
+
+	return len;
+}
+
+static void l2cap_recv_reset(struct l2cap_conn *conn)
+{
+	kfree_skb(conn->rx_skb);
+	conn->rx_skb = NULL;
+	conn->rx_len = 0;
+}
+
 void l2cap_recv_acldata(struct hci_conn *hcon, struct sk_buff *skb, u16 flags)
 {
 	struct l2cap_conn *conn = hcon->l2cap_data;
@@ -8291,19 +8348,19 @@ void l2cap_recv_acldata(struct hci_conn *hcon, struct sk_buff *skb, u16 flags)
 	case ACL_START:
 	case ACL_START_NO_FLUSH:
 	case ACL_COMPLETE:
-		if (conn->rx_len) {
+		if (conn->rx_skb) {
 			BT_ERR("Unexpected start frame (len %d)", skb->len);
-			kfree_skb(conn->rx_skb);
-			conn->rx_skb = NULL;
-			conn->rx_len = 0;
+			l2cap_recv_reset(conn);
 			l2cap_conn_unreliable(conn, ECOMM);
 		}
 
-		/* Start fragment always begin with Basic L2CAP header */
+		/* Start fragment may not contain the L2CAP header so just
+		 * copy the initial byte when that happens.
+		 */
 		if (skb->len < L2CAP_HDR_SIZE) {
-			BT_ERR("Frame is too short (len %d)", skb->len);
-			l2cap_conn_unreliable(conn, ECOMM);
-			goto drop;
+			if (l2cap_recv_frag(conn, skb, L2CAP_HDR_SIZE) < 0)
+				goto drop;
+			return;
 		}
 
 		hdr = (struct l2cap_hdr *) skb->data;
@@ -8324,38 +8381,43 @@ void l2cap_recv_acldata(struct hci_conn *hcon, struct sk_buff *skb, u16 flags)
 			goto drop;
 		}
 
-		/* Allocate skb for the complete frame (with header) */
-		conn->rx_skb = bt_skb_alloc(len, GFP_KERNEL);
-		if (!conn->rx_skb)
+		/* Append fragment into frame (with header) */
+		if (l2cap_recv_frag(conn, skb, len) < 0)
 			goto drop;
 
-		skb_copy_from_linear_data(skb, skb_put(conn->rx_skb, skb->len),
-					  skb->len);
-		conn->rx_len = len - skb->len;
 		break;
 
 	case ACL_CONT:
 		BT_DBG("Cont: frag len %d (expecting %d)", skb->len, conn->rx_len);
 
-		if (!conn->rx_len) {
+		if (!conn->rx_skb) {
 			BT_ERR("Unexpected continuation frame (len %d)", skb->len);
 			l2cap_conn_unreliable(conn, ECOMM);
 			goto drop;
 		}
 
+		/* Complete the L2CAP header if it has not been read */
+		if (conn->rx_skb->len < L2CAP_HDR_SIZE) {
+			if (l2cap_recv_header(conn, skb) < 0) {
+				l2cap_conn_unreliable(conn, ECOMM);
+				goto drop;
+			}
+
+			/* Header still could not be read just continue */
+			if (conn->rx_skb->len < L2CAP_HDR_SIZE)
+				return;
+		}
+
 		if (skb->len > conn->rx_len) {
 			BT_ERR("Fragment is too long (len %d, expected %d)",
 			       skb->len, conn->rx_len);
-			kfree_skb(conn->rx_skb);
-			conn->rx_skb = NULL;
-			conn->rx_len = 0;
+			l2cap_recv_reset(conn);
 			l2cap_conn_unreliable(conn, ECOMM);
 			goto drop;
 		}
 
-		skb_copy_from_linear_data(skb, skb_put(conn->rx_skb, skb->len),
-					  skb->len);
-		conn->rx_len -= skb->len;
+		/* Append fragment into frame (with header) */
+		l2cap_recv_frag(conn, skb, skb->len);
 
 		if (!conn->rx_len) {
 			/* Complete frame received. l2cap_recv_frame
-- 
2.26.2


^ permalink raw reply related	[flat|nested] 3+ messages in thread

* Re: [RFC] Bluetooth: L2CAP: Fix to handling fragmented header
  2020-07-28  7:04 [RFC] Bluetooth: L2CAP: Fix to handling fragmented header Luiz Augusto von Dentz
@ 2020-07-28  7:22 ` Marcel Holtmann
  2020-07-28 16:39   ` Luiz Augusto von Dentz
  0 siblings, 1 reply; 3+ messages in thread
From: Marcel Holtmann @ 2020-07-28  7:22 UTC (permalink / raw)
  To: Luiz Augusto von Dentz; +Cc: linux-bluetooth

Hi Luiz,

> 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).'
> 
> This text has been changed recently as it previously stated:
> 
> 'Start Fragments always begin with the Basic L2CAP header of a PDU.'
> 
> Apparently this was changed by the following errata:
> 
> https://www.bluetooth.org/tse/errata_view.cfm?errata_id=10216
> 
> In past this has not been a problem but it seems new controllers are
> apparently doing it as it has been reported in Zephyr:
> 
> https://github.com/zephyrproject-rtos/zephyr/issues/26900
> 
> Signed-off-by: Luiz Augusto von Dentz <luiz.von.dentz@intel.com>
> ---
> net/bluetooth/l2cap_core.c | 104 +++++++++++++++++++++++++++++--------
> 1 file changed, 83 insertions(+), 21 deletions(-)
> 
> diff --git a/net/bluetooth/l2cap_core.c b/net/bluetooth/l2cap_core.c
> index ade83e224567..193bea314222 100644
> --- a/net/bluetooth/l2cap_core.c
> +++ b/net/bluetooth/l2cap_core.c
> @@ -8269,6 +8269,63 @@ static void l2cap_security_cfm(struct hci_conn *hcon, u8 status, u8 encrypt)
> 	mutex_unlock(&conn->chan_lock);
> }
> 
> +/* Append fragment into frame respecting the maximum len of rx_skb */
> +static int l2cap_recv_frag(struct l2cap_conn *conn, struct sk_buff *skb,
> +			   u16 len)
> +{
> +	if (!conn->rx_skb) {
> +		/* Allocate skb for the complete frame (with header) */
> +		conn->rx_skb = bt_skb_alloc(len, GFP_KERNEL);
> +		if (!conn->rx_skb)
> +			return -ENOMEM;
> +		/* Init rx_len */
> +		conn->rx_len = len;
> +	}
> +
> +	/* Copy as much as the rx_skb can hold */
> +	len = min_t(u16, len, skb->len);
> +	skb_copy_from_linear_data(skb, skb_put(conn->rx_skb, len), len);
> +	skb_pull(skb, len);
> +	conn->rx_len -= len;
> +
> +	return len;
> +}
> +
> +static int l2cap_recv_header(struct l2cap_conn *conn, struct sk_buff *skb)
> +{
> +	struct l2cap_hdr *hdr;
> +	struct sk_buff *rx_skb;
> +	int len;
> +
> +	/* Append just enough to complete the header */
> +	len = l2cap_recv_frag(conn, skb, L2CAP_HDR_SIZE - conn->rx_skb->len);
> +
> +	/* If header could not be read just continue */
> +	if (len < 0 || conn->rx_skb->len < L2CAP_HDR_SIZE)
> +		return len;
> +
> +	rx_skb = conn->rx_skb;
> +	conn->rx_skb = NULL;
> +
> +	hdr = (struct l2cap_hdr *) rx_skb->data;

so I think it is pointless to insist on getting the complete header. We really just need the first 2 octets.

struct l2cap_hdr {                                                               
        __le16     len;                                                          
        __le16     cid;                                                          
} __packed;

Once we have received at least 2 octets, we can get_unaligned_le16(rx_skb->data) and then just continue.

Regards

Marcel


^ permalink raw reply	[flat|nested] 3+ messages in thread

* Re: [RFC] Bluetooth: L2CAP: Fix to handling fragmented header
  2020-07-28  7:22 ` Marcel Holtmann
@ 2020-07-28 16:39   ` Luiz Augusto von Dentz
  0 siblings, 0 replies; 3+ messages in thread
From: Luiz Augusto von Dentz @ 2020-07-28 16:39 UTC (permalink / raw)
  To: Marcel Holtmann; +Cc: linux-bluetooth

Hi Marcel,

On Tue, Jul 28, 2020 at 12:22 AM Marcel Holtmann <marcel@holtmann.org> wrote:
>
> Hi Luiz,
>
> > 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).'
> >
> > This text has been changed recently as it previously stated:
> >
> > 'Start Fragments always begin with the Basic L2CAP header of a PDU.'
> >
> > Apparently this was changed by the following errata:
> >
> > https://www.bluetooth.org/tse/errata_view.cfm?errata_id=10216
> >
> > In past this has not been a problem but it seems new controllers are
> > apparently doing it as it has been reported in Zephyr:
> >
> > https://github.com/zephyrproject-rtos/zephyr/issues/26900
> >
> > Signed-off-by: Luiz Augusto von Dentz <luiz.von.dentz@intel.com>
> > ---
> > net/bluetooth/l2cap_core.c | 104 +++++++++++++++++++++++++++++--------
> > 1 file changed, 83 insertions(+), 21 deletions(-)
> >
> > diff --git a/net/bluetooth/l2cap_core.c b/net/bluetooth/l2cap_core.c
> > index ade83e224567..193bea314222 100644
> > --- a/net/bluetooth/l2cap_core.c
> > +++ b/net/bluetooth/l2cap_core.c
> > @@ -8269,6 +8269,63 @@ static void l2cap_security_cfm(struct hci_conn *hcon, u8 status, u8 encrypt)
> >       mutex_unlock(&conn->chan_lock);
> > }
> >
> > +/* Append fragment into frame respecting the maximum len of rx_skb */
> > +static int l2cap_recv_frag(struct l2cap_conn *conn, struct sk_buff *skb,
> > +                        u16 len)
> > +{
> > +     if (!conn->rx_skb) {
> > +             /* Allocate skb for the complete frame (with header) */
> > +             conn->rx_skb = bt_skb_alloc(len, GFP_KERNEL);
> > +             if (!conn->rx_skb)
> > +                     return -ENOMEM;
> > +             /* Init rx_len */
> > +             conn->rx_len = len;
> > +     }
> > +
> > +     /* Copy as much as the rx_skb can hold */
> > +     len = min_t(u16, len, skb->len);
> > +     skb_copy_from_linear_data(skb, skb_put(conn->rx_skb, len), len);
> > +     skb_pull(skb, len);
> > +     conn->rx_len -= len;
> > +
> > +     return len;
> > +}
> > +
> > +static int l2cap_recv_header(struct l2cap_conn *conn, struct sk_buff *skb)
> > +{
> > +     struct l2cap_hdr *hdr;
> > +     struct sk_buff *rx_skb;
> > +     int len;
> > +
> > +     /* Append just enough to complete the header */
> > +     len = l2cap_recv_frag(conn, skb, L2CAP_HDR_SIZE - conn->rx_skb->len);
> > +
> > +     /* If header could not be read just continue */
> > +     if (len < 0 || conn->rx_skb->len < L2CAP_HDR_SIZE)
> > +             return len;
> > +
> > +     rx_skb = conn->rx_skb;
> > +     conn->rx_skb = NULL;
> > +
> > +     hdr = (struct l2cap_hdr *) rx_skb->data;
>
> so I think it is pointless to insist on getting the complete header. We really just need the first 2 octets.
>
> struct l2cap_hdr {
>         __le16     len;
>         __le16     cid;
> } __packed;

Indeed, I've totally forgotten about the cid so I will change this to
not use L2CAP_HDR_SIZE but 2 instead.

> Once we have received at least 2 octets, we can get_unaligned_le16(rx_skb->data) and then just continue.

Sure, I was trying to figure out if there is any way to grow the the
rx_skb since I will be just allocating 2 bytes for it if the header is
not available, we could perhaps take a different approach and always
allocate based on the conn->mtu that way we don't have to wait the
length to received to allocate a second skb and copy over the length
into it, obviously that would only be done if length was fragmented.

> Regards
>
> Marcel
>


-- 
Luiz Augusto von Dentz

^ permalink raw reply	[flat|nested] 3+ messages in thread

end of thread, other threads:[~2020-07-28 16:39 UTC | newest]

Thread overview: 3+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2020-07-28  7:04 [RFC] Bluetooth: L2CAP: Fix to handling fragmented header Luiz Augusto von Dentz
2020-07-28  7:22 ` Marcel Holtmann
2020-07-28 16:39   ` Luiz Augusto von Dentz

This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.