Skip to content

Commit

Permalink
Bluetooth: RFCOMM: Replace use of memcpy_from_msg with bt_skb_sendmmsg
Browse files Browse the repository at this point in the history
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>
  • Loading branch information
Vudentz authored and intel-lab-lkp committed Aug 31, 2021
1 parent 08b74ac commit a246841
Show file tree
Hide file tree
Showing 2 changed files with 49 additions and 43 deletions.
44 changes: 37 additions & 7 deletions net/bluetooth/rfcomm/core.c
Expand Up @@ -549,22 +549,52 @@ 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;
struct sk_buff *frag;
int len;

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

BT_DBG("dlc %p mtu %d len %d", d, d->mtu, len);
/* Queue all fragments atomically. */
spin_lock_bh(&d->tx_queue.lock);

if (len > d->mtu)
return -EINVAL;
len = rfcomm_dlc_send_frag(d, skb);
if (len < 0)
goto unlock;

rfcomm_make_uih(skb, d->addr);
skb_queue_tail(&d->tx_queue, skb);
skb_walk_frags(skb, frag) {
int ret;

ret = rfcomm_dlc_send_frag(d, frag);
if (ret < 0)
break;

len += ret;
}

skb_shinfo(skb)->frag_list = NULL;

unlock:
spin_unlock_bh(&d->tx_queue.lock);

if (!test_bit(RFCOMM_TX_THROTTLED, &d->flags))
if (len > 0 && !test_bit(RFCOMM_TX_THROTTLED, &d->flags))
rfcomm_schedule();
return len;
}
Expand Down
48 changes: 12 additions & 36 deletions net/bluetooth/rfcomm/sock.c
Expand Up @@ -558,6 +558,7 @@ static int rfcomm_sock_sendmsg(struct socket *sock, struct msghdr *msg,
{
struct sock *sk = sock->sk;
struct rfcomm_dlc *d = rfcomm_pi(sk)->dlc;
size_t size;
struct sk_buff *skb;
int sent;

Expand All @@ -574,47 +575,22 @@ 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;
size = min_t(size_t, len, d->mtu);

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;
}
sent = bt_sock_wait_ready(sk, msg->msg_flags);

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_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;
}
Expand Down

0 comments on commit a246841

Please sign in to comment.