Bluetooth: RFCOMM: Replace use of memcpy_from_msg with bt_skb_sendmmsg
authorLuiz Augusto von Dentz <luiz.von.dentz@intel.com>
Fri, 3 Sep 2021 22:27:32 +0000 (15:27 -0700)
committerGreg Kroah-Hartman <gregkh@linuxfoundation.org>
Fri, 29 Jul 2022 15:25:30 +0000 (17:25 +0200)
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: Luiz Augusto von Dentz <luiz.von.dentz@intel.com>
Signed-off-by: Marcel Holtmann <marcel@holtmann.org>
Cc: Harshit Mogalapalli <harshit.m.mogalapalli@oracle.com>
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
net/bluetooth/rfcomm/core.c
net/bluetooth/rfcomm/sock.c

index f2bacb4..7324764 100644 (file)
@@ -549,22 +549,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;
 }
index 2c95bb5..5938af3 100644 (file)
@@ -575,46 +575,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);
-
-               err = memcpy_from_msg(skb_put(skb, size), msg, size);
-               if (err) {
-                       kfree_skb(skb);
-                       if (sent == 0)
-                               sent = err;
-                       break;
-               }
 
-               skb->priority = sk->sk_priority;
+       release_sock(sk);
 
-               err = rfcomm_dlc_send(d, skb);
-               if (err < 0) {
-                       kfree_skb(skb);
-                       if (sent == 0)
-                               sent = err;
-                       break;
-               }
+       if (sent)
+               return sent;
 
-               sent += size;
-               len  -= size;
-       }
+       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);
 
-done:
-       release_sock(sk);
+       sent = rfcomm_dlc_send(d, skb);
+       if (sent < 0)
+               kfree_skb(skb);
 
        return sent;
 }