diff options
Diffstat (limited to 'net/bluetooth/l2cap.c')
| -rw-r--r-- | net/bluetooth/l2cap.c | 60 | 
1 files changed, 49 insertions, 11 deletions
diff --git a/net/bluetooth/l2cap.c b/net/bluetooth/l2cap.c index 0b54b7dd840..daa7a988d9a 100644 --- a/net/bluetooth/l2cap.c +++ b/net/bluetooth/l2cap.c @@ -1008,10 +1008,20 @@ static int l2cap_sock_bind(struct socket *sock, struct sockaddr *addr, int alen)  		goto done;  	} -	if (la.l2_psm && __le16_to_cpu(la.l2_psm) < 0x1001 && -				!capable(CAP_NET_BIND_SERVICE)) { -		err = -EACCES; -		goto done; +	if (la.l2_psm) { +		__u16 psm = __le16_to_cpu(la.l2_psm); + +		/* PSM must be odd and lsb of upper byte must be 0 */ +		if ((psm & 0x0101) != 0x0001) { +			err = -EINVAL; +			goto done; +		} + +		/* Restrict usage of well-known PSMs */ +		if (psm < 0x1001 && !capable(CAP_NET_BIND_SERVICE)) { +			err = -EACCES; +			goto done; +		}  	}  	write_lock_bh(&l2cap_sk_list.lock); @@ -1190,6 +1200,13 @@ static int l2cap_sock_connect(struct socket *sock, struct sockaddr *addr, int al  		goto done;  	} +	/* PSM must be odd and lsb of upper byte must be 0 */ +	if ((__le16_to_cpu(la.l2_psm) & 0x0101) != 0x0001 && +		sk->sk_type != SOCK_RAW) { +		err = -EINVAL; +		goto done; +	} +  	/* Set destination address and psm */  	bacpy(&bt_sk(sk)->dst, &la.l2_bdaddr);  	l2cap_pi(sk)->psm = la.l2_psm; @@ -1635,7 +1652,7 @@ static inline int l2cap_skbuff_fromiovec(struct sock *sk, struct msghdr *msg, in  		*frag = bt_skb_send_alloc(sk, count, msg->msg_flags & MSG_DONTWAIT, &err);  		if (!*frag) -			return -EFAULT; +			return err;  		if (memcpy_fromiovec(skb_put(*frag, count), msg->msg_iov, count))  			return -EFAULT; @@ -1661,7 +1678,7 @@ static struct sk_buff *l2cap_create_connless_pdu(struct sock *sk, struct msghdr  	skb = bt_skb_send_alloc(sk, count + hlen,  			msg->msg_flags & MSG_DONTWAIT, &err);  	if (!skb) -		return ERR_PTR(-ENOMEM); +		return ERR_PTR(err);  	/* Create L2CAP header */  	lh = (struct l2cap_hdr *) skb_put(skb, L2CAP_HDR_SIZE); @@ -1690,7 +1707,7 @@ static struct sk_buff *l2cap_create_basic_pdu(struct sock *sk, struct msghdr *ms  	skb = bt_skb_send_alloc(sk, count + hlen,  			msg->msg_flags & MSG_DONTWAIT, &err);  	if (!skb) -		return ERR_PTR(-ENOMEM); +		return ERR_PTR(err);  	/* Create L2CAP header */  	lh = (struct l2cap_hdr *) skb_put(skb, L2CAP_HDR_SIZE); @@ -1727,7 +1744,7 @@ static struct sk_buff *l2cap_create_iframe_pdu(struct sock *sk, struct msghdr *m  	skb = bt_skb_send_alloc(sk, count + hlen,  			msg->msg_flags & MSG_DONTWAIT, &err);  	if (!skb) -		return ERR_PTR(-ENOMEM); +		return ERR_PTR(err);  	/* Create L2CAP header */  	lh = (struct l2cap_hdr *) skb_put(skb, L2CAP_HDR_SIZE); @@ -1934,6 +1951,9 @@ static int l2cap_sock_recvmsg(struct kiocb *iocb, struct socket *sock, struct ms  	release_sock(sk); +	if (sock->type == SOCK_STREAM) +		return bt_sock_stream_recvmsg(iocb, sock, msg, len, flags); +  	return bt_sock_recvmsg(iocb, sock, msg, len, flags);  } @@ -2891,7 +2911,7 @@ static inline int l2cap_connect_req(struct l2cap_conn *conn, struct l2cap_cmd_hd  	struct l2cap_chan_list *list = &conn->chan_list;  	struct l2cap_conn_req *req = (struct l2cap_conn_req *) data;  	struct l2cap_conn_rsp rsp; -	struct sock *parent, *uninitialized_var(sk); +	struct sock *parent, *sk = NULL;  	int result, status = L2CAP_CS_NO_INFO;  	u16 dcid = 0, scid = __le16_to_cpu(req->scid); @@ -3000,7 +3020,7 @@ sendresp:  					L2CAP_INFO_REQ, sizeof(info), &info);  	} -	if (!(l2cap_pi(sk)->conf_state & L2CAP_CONF_REQ_SENT) && +	if (sk && !(l2cap_pi(sk)->conf_state & L2CAP_CONF_REQ_SENT) &&  				result == L2CAP_CR_SUCCESS) {  		u8 buf[128];  		l2cap_pi(sk)->conf_state |= L2CAP_CONF_REQ_SENT; @@ -3151,6 +3171,7 @@ static inline int l2cap_config_req(struct l2cap_conn *conn, struct l2cap_cmd_hdr  	if (!(l2cap_pi(sk)->conf_state & L2CAP_CONF_REQ_SENT)) {  		u8 buf[64]; +		l2cap_pi(sk)->conf_state |= L2CAP_CONF_REQ_SENT;  		l2cap_send_cmd(conn, l2cap_get_ident(conn), L2CAP_CONF_REQ,  					l2cap_build_conf_req(sk, buf), buf);  		l2cap_pi(sk)->num_conf_req++; @@ -4643,6 +4664,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) { @@ -4653,7 +4676,8 @@ static int l2cap_recv_acldata(struct hci_conn *hcon, struct sk_buff *skb, u16 fl  			l2cap_conn_unreliable(conn, ECOMM);  		} -		if (skb->len < 2) { +		/* Start fragment always begin with Basic L2CAP header */ +		if (skb->len < L2CAP_HDR_SIZE) {  			BT_ERR("Frame is too short (len %d)", skb->len);  			l2cap_conn_unreliable(conn, ECOMM);  			goto drop; @@ -4661,6 +4685,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 */ @@ -4677,6 +4702,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)  |