forked from mirrors/linux
		
	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 <luiz.von.dentz@intel.com> Signed-off-by: Marcel Holtmann <marcel@holtmann.org>
This commit is contained in:
		
							parent
							
								
									5ff20cbe67
								
							
						
					
					
						commit
						4d7ea8ee90
					
				
					 2 changed files with 94 additions and 25 deletions
				
			
		|  | @ -207,6 +207,7 @@ struct l2cap_hdr { | ||||||
| 	__le16     len; | 	__le16     len; | ||||||
| 	__le16     cid; | 	__le16     cid; | ||||||
| } __packed; | } __packed; | ||||||
|  | #define L2CAP_LEN_SIZE		2 | ||||||
| #define L2CAP_HDR_SIZE		4 | #define L2CAP_HDR_SIZE		4 | ||||||
| #define L2CAP_ENH_HDR_SIZE	6 | #define L2CAP_ENH_HDR_SIZE	6 | ||||||
| #define L2CAP_EXT_HDR_SIZE	8 | #define L2CAP_EXT_HDR_SIZE	8 | ||||||
|  |  | ||||||
|  | @ -8276,10 +8276,73 @@ static void l2cap_security_cfm(struct hci_conn *hcon, u8 status, u8 encrypt) | ||||||
| 	mutex_unlock(&conn->chan_lock); | 	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_len(struct l2cap_conn *conn, struct sk_buff *skb) | ||||||
|  | { | ||||||
|  | 	struct sk_buff *rx_skb; | ||||||
|  | 	int len; | ||||||
|  | 
 | ||||||
|  | 	/* Append just enough to complete the header */ | ||||||
|  | 	len = l2cap_recv_frag(conn, skb, L2CAP_LEN_SIZE - conn->rx_skb->len); | ||||||
|  | 
 | ||||||
|  | 	/* If header could not be read just continue */ | ||||||
|  | 	if (len < 0 || conn->rx_skb->len < L2CAP_LEN_SIZE) | ||||||
|  | 		return len; | ||||||
|  | 
 | ||||||
|  | 	rx_skb = conn->rx_skb; | ||||||
|  | 	len = get_unaligned_le16(rx_skb->data); | ||||||
|  | 
 | ||||||
|  | 	/* Check if rx_skb has enough space to received all fragments */ | ||||||
|  | 	if (len + (L2CAP_HDR_SIZE - L2CAP_LEN_SIZE) <= skb_tailroom(rx_skb)) { | ||||||
|  | 		/* Update expected len */ | ||||||
|  | 		conn->rx_len = len + (L2CAP_HDR_SIZE - L2CAP_LEN_SIZE); | ||||||
|  | 		return L2CAP_LEN_SIZE; | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	/* Reset conn->rx_skb since it will need to be reallocated in order to
 | ||||||
|  | 	 * fit all fragments. | ||||||
|  | 	 */ | ||||||
|  | 	conn->rx_skb = NULL; | ||||||
|  | 
 | ||||||
|  | 	/* Reallocates rx_skb using the exact expected length */ | ||||||
|  | 	len = l2cap_recv_frag(conn, rx_skb, | ||||||
|  | 			      len + (L2CAP_HDR_SIZE - L2CAP_LEN_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) | void l2cap_recv_acldata(struct hci_conn *hcon, struct sk_buff *skb, u16 flags) | ||||||
| { | { | ||||||
| 	struct l2cap_conn *conn = hcon->l2cap_data; | 	struct l2cap_conn *conn = hcon->l2cap_data; | ||||||
| 	struct l2cap_hdr *hdr; |  | ||||||
| 	int len; | 	int len; | ||||||
| 
 | 
 | ||||||
| 	/* For AMP controller do not create l2cap conn */ | 	/* 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) | ||||||
| 	case ACL_START: | 	case ACL_START: | ||||||
| 	case ACL_START_NO_FLUSH: | 	case ACL_START_NO_FLUSH: | ||||||
| 	case ACL_COMPLETE: | 	case ACL_COMPLETE: | ||||||
| 		if (conn->rx_len) { | 		if (conn->rx_skb) { | ||||||
| 			BT_ERR("Unexpected start frame (len %d)", skb->len); | 			BT_ERR("Unexpected start frame (len %d)", skb->len); | ||||||
| 			kfree_skb(conn->rx_skb); | 			l2cap_recv_reset(conn); | ||||||
| 			conn->rx_skb = NULL; |  | ||||||
| 			conn->rx_len = 0; |  | ||||||
| 			l2cap_conn_unreliable(conn, ECOMM); | 			l2cap_conn_unreliable(conn, ECOMM); | ||||||
| 		} | 		} | ||||||
| 
 | 
 | ||||||
| 		/* Start fragment always begin with Basic L2CAP header */ | 		/* Start fragment may not contain the L2CAP length so just
 | ||||||
| 		if (skb->len < L2CAP_HDR_SIZE) { | 		 * copy the initial byte when that happens and use conn->mtu as | ||||||
| 			BT_ERR("Frame is too short (len %d)", skb->len); | 		 * expected length. | ||||||
| 			l2cap_conn_unreliable(conn, ECOMM); | 		 */ | ||||||
|  | 		if (skb->len < L2CAP_LEN_SIZE) { | ||||||
|  | 			if (l2cap_recv_frag(conn, skb, conn->mtu) < 0) | ||||||
| 				goto drop; | 				goto drop; | ||||||
|  | 			return; | ||||||
| 		} | 		} | ||||||
| 
 | 
 | ||||||
| 		hdr = (struct l2cap_hdr *) skb->data; | 		len = get_unaligned_le16(skb->data) + L2CAP_HDR_SIZE; | ||||||
| 		len = __le16_to_cpu(hdr->len) + L2CAP_HDR_SIZE; |  | ||||||
| 
 | 
 | ||||||
| 		if (len == skb->len) { | 		if (len == skb->len) { | ||||||
| 			/* Complete frame received */ | 			/* Complete frame received */ | ||||||
|  | @ -8331,38 +8394,43 @@ void l2cap_recv_acldata(struct hci_conn *hcon, struct sk_buff *skb, u16 flags) | ||||||
| 			goto drop; | 			goto drop; | ||||||
| 		} | 		} | ||||||
| 
 | 
 | ||||||
| 		/* Allocate skb for the complete frame (with header) */ | 		/* Append fragment into frame (with header) */ | ||||||
| 		conn->rx_skb = bt_skb_alloc(len, GFP_KERNEL); | 		if (l2cap_recv_frag(conn, skb, len) < 0) | ||||||
| 		if (!conn->rx_skb) |  | ||||||
| 			goto drop; | 			goto drop; | ||||||
| 
 | 
 | ||||||
| 		skb_copy_from_linear_data(skb, skb_put(conn->rx_skb, skb->len), |  | ||||||
| 					  skb->len); |  | ||||||
| 		conn->rx_len = len - skb->len; |  | ||||||
| 		break; | 		break; | ||||||
| 
 | 
 | ||||||
| 	case ACL_CONT: | 	case ACL_CONT: | ||||||
| 		BT_DBG("Cont: frag len %d (expecting %d)", skb->len, conn->rx_len); | 		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); | 			BT_ERR("Unexpected continuation frame (len %d)", skb->len); | ||||||
| 			l2cap_conn_unreliable(conn, ECOMM); | 			l2cap_conn_unreliable(conn, ECOMM); | ||||||
| 			goto drop; | 			goto drop; | ||||||
| 		} | 		} | ||||||
| 
 | 
 | ||||||
| 		if (skb->len > conn->rx_len) { | 		/* Complete the L2CAP length if it has not been read */ | ||||||
| 			BT_ERR("Fragment is too long (len %d, expected %d)", | 		if (conn->rx_skb->len < L2CAP_LEN_SIZE) { | ||||||
| 			       skb->len, conn->rx_len); | 			if (l2cap_recv_len(conn, skb) < 0) { | ||||||
| 			kfree_skb(conn->rx_skb); |  | ||||||
| 			conn->rx_skb = NULL; |  | ||||||
| 			conn->rx_len = 0; |  | ||||||
| 				l2cap_conn_unreliable(conn, ECOMM); | 				l2cap_conn_unreliable(conn, ECOMM); | ||||||
| 				goto drop; | 				goto drop; | ||||||
| 			} | 			} | ||||||
| 
 | 
 | ||||||
| 		skb_copy_from_linear_data(skb, skb_put(conn->rx_skb, skb->len), | 			/* Header still could not be read just continue */ | ||||||
| 					  skb->len); | 			if (conn->rx_skb->len < L2CAP_LEN_SIZE) | ||||||
| 		conn->rx_len -= skb->len; | 				return; | ||||||
|  | 		} | ||||||
|  | 
 | ||||||
|  | 		if (skb->len > conn->rx_len) { | ||||||
|  | 			BT_ERR("Fragment is too long (len %d, expected %d)", | ||||||
|  | 			       skb->len, conn->rx_len); | ||||||
|  | 			l2cap_recv_reset(conn); | ||||||
|  | 			l2cap_conn_unreliable(conn, ECOMM); | ||||||
|  | 			goto drop; | ||||||
|  | 		} | ||||||
|  | 
 | ||||||
|  | 		/* Append fragment into frame (with header) */ | ||||||
|  | 		l2cap_recv_frag(conn, skb, skb->len); | ||||||
| 
 | 
 | ||||||
| 		if (!conn->rx_len) { | 		if (!conn->rx_len) { | ||||||
| 			/* Complete frame received. l2cap_recv_frame
 | 			/* Complete frame received. l2cap_recv_frame
 | ||||||
|  |  | ||||||
		Loading…
	
		Reference in a new issue
	
	 Luiz Augusto von Dentz
						Luiz Augusto von Dentz