Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 10bacb89 authored by Luiz Augusto von Dentz's avatar Luiz Augusto von Dentz Committed by Greg Kroah-Hartman
Browse files

Bluetooth: RFCOMM: Replace use of memcpy_from_msg with bt_skb_sendmmsg



commit 81be03e026dc0c16dc1c64e088b2a53b73caa895 upstream.

This makes use of bt_skb_sendmmsg instead using memcpy_from_msg which
is not considered safe to be used when lock_sock is held.

Also make rfcomm_dlc_send handle skb with fragments and queue them all
atomically.

Signed-off-by: default avatarLuiz Augusto von Dentz <luiz.von.dentz@intel.com>
Signed-off-by: default avatarMarcel Holtmann <marcel@holtmann.org>
Cc: Harshit Mogalapalli <harshit.m.mogalapalli@oracle.com>
Signed-off-by: default avatarGreg Kroah-Hartman <gregkh@linuxfoundation.org>
parent bf46574d
Loading
Loading
Loading
Loading
+43 −7
Original line number Diff line number Diff line
@@ -553,22 +553,58 @@ struct rfcomm_dlc *rfcomm_dlc_exists(bdaddr_t *src, bdaddr_t *dst, u8 channel)
	return dlc;
}

static int rfcomm_dlc_send_frag(struct rfcomm_dlc *d, struct sk_buff *frag)
{
	int len = frag->len;

	BT_DBG("dlc %p mtu %d len %d", d, d->mtu, len);

	if (len > d->mtu)
		return -EINVAL;

	rfcomm_make_uih(frag, d->addr);
	__skb_queue_tail(&d->tx_queue, frag);

	return len;
}

int rfcomm_dlc_send(struct rfcomm_dlc *d, struct sk_buff *skb)
{
	int len = skb->len;
	unsigned long flags;
	struct sk_buff *frag, *next;
	int len;

	if (d->state != BT_CONNECTED)
		return -ENOTCONN;

	BT_DBG("dlc %p mtu %d len %d", d, d->mtu, len);
	frag = skb_shinfo(skb)->frag_list;
	skb_shinfo(skb)->frag_list = NULL;

	if (len > d->mtu)
		return -EINVAL;
	/* Queue all fragments atomically. */
	spin_lock_irqsave(&d->tx_queue.lock, flags);

	rfcomm_make_uih(skb, d->addr);
	skb_queue_tail(&d->tx_queue, skb);
	len = rfcomm_dlc_send_frag(d, skb);
	if (len < 0 || !frag)
		goto unlock;

	for (; frag; frag = next) {
		int ret;

		next = frag->next;

		ret = rfcomm_dlc_send_frag(d, frag);
		if (ret < 0) {
			kfree_skb(frag);
			goto unlock;
		}

		len += ret;
	}

unlock:
	spin_unlock_irqrestore(&d->tx_queue.lock, flags);

	if (!test_bit(RFCOMM_TX_THROTTLED, &d->flags))
	if (len > 0 && !test_bit(RFCOMM_TX_THROTTLED, &d->flags))
		rfcomm_schedule();
	return len;
}
+10 −36
Original line number Diff line number Diff line
@@ -578,46 +578,20 @@ static int rfcomm_sock_sendmsg(struct socket *sock, struct msghdr *msg,
	lock_sock(sk);

	sent = bt_sock_wait_ready(sk, msg->msg_flags);
	if (sent)
		goto done;

	while (len) {
		size_t size = min_t(size_t, len, d->mtu);
		int err;

		skb = sock_alloc_send_skb(sk, size + RFCOMM_SKB_RESERVE,
				msg->msg_flags & MSG_DONTWAIT, &err);
		if (!skb) {
			if (sent == 0)
				sent = err;
			break;
		}
		skb_reserve(skb, RFCOMM_SKB_HEAD_RESERVE);
	release_sock(sk);

		err = memcpy_from_msg(skb_put(skb, size), msg, size);
		if (err) {
			kfree_skb(skb);
			if (sent == 0)
				sent = err;
			break;
		}
	if (sent)
		return sent;

		skb->priority = sk->sk_priority;
	skb = bt_skb_sendmmsg(sk, msg, len, d->mtu, RFCOMM_SKB_HEAD_RESERVE,
			      RFCOMM_SKB_TAIL_RESERVE);
	if (IS_ERR_OR_NULL(skb))
		return PTR_ERR(skb);

		err = rfcomm_dlc_send(d, skb);
		if (err < 0) {
	sent = rfcomm_dlc_send(d, skb);
	if (sent < 0)
		kfree_skb(skb);
			if (sent == 0)
				sent = err;
			break;
		}

		sent += size;
		len  -= size;
	}

done:
	release_sock(sk);

	return sent;
}