Bluetooth: L2CAP: Fix potential user-after-free
authorLuiz Augusto von Dentz <luiz.von.dentz@intel.com>
Wed, 1 Feb 2023 22:01:11 +0000 (14:01 -0800)
committerGreg Kroah-Hartman <gregkh@linuxfoundation.org>
Fri, 10 Mar 2023 08:33:01 +0000 (09:33 +0100)
[ Upstream commit df5703348813235874d851934e957c3723d71644 ]

This fixes all instances of which requires to allocate a buffer calling
alloc_skb which may release the chan lock and reacquire later which
makes it possible that the chan is disconnected in the meantime.

Fixes: a6a5568c03c4 ("Bluetooth: Lock the L2CAP channel when sending")
Reported-by: Alexander Coffin <alex.coffin@matician.com>
Signed-off-by: Luiz Augusto von Dentz <luiz.von.dentz@intel.com>
Signed-off-by: Sasha Levin <sashal@kernel.org>
net/bluetooth/l2cap_core.c
net/bluetooth/l2cap_sock.c

index 9fdede5fe71c7b9e7746057653bc2c3a0c2a112b..da85768b04b7689b685d5ef8a8a8475c6e355d5a 100644 (file)
@@ -2683,14 +2683,6 @@ int l2cap_chan_send(struct l2cap_chan *chan, struct msghdr *msg, size_t len)
                if (IS_ERR(skb))
                        return PTR_ERR(skb);
 
-               /* Channel lock is released before requesting new skb and then
-                * reacquired thus we need to recheck channel state.
-                */
-               if (chan->state != BT_CONNECTED) {
-                       kfree_skb(skb);
-                       return -ENOTCONN;
-               }
-
                l2cap_do_send(chan, skb);
                return len;
        }
@@ -2735,14 +2727,6 @@ int l2cap_chan_send(struct l2cap_chan *chan, struct msghdr *msg, size_t len)
                if (IS_ERR(skb))
                        return PTR_ERR(skb);
 
-               /* Channel lock is released before requesting new skb and then
-                * reacquired thus we need to recheck channel state.
-                */
-               if (chan->state != BT_CONNECTED) {
-                       kfree_skb(skb);
-                       return -ENOTCONN;
-               }
-
                l2cap_do_send(chan, skb);
                err = len;
                break;
@@ -2763,14 +2747,6 @@ int l2cap_chan_send(struct l2cap_chan *chan, struct msghdr *msg, size_t len)
                 */
                err = l2cap_segment_sdu(chan, &seg_queue, msg, len);
 
-               /* The channel could have been closed while segmenting,
-                * check that it is still connected.
-                */
-               if (chan->state != BT_CONNECTED) {
-                       __skb_queue_purge(&seg_queue);
-                       err = -ENOTCONN;
-               }
-
                if (err)
                        break;
 
index ca8f07f3542b824facc811a3bfc13bc6ecbc1ad2..eebe256104bc03d0a3ecee31721f0885aeed7192 100644 (file)
@@ -1624,6 +1624,14 @@ static struct sk_buff *l2cap_sock_alloc_skb_cb(struct l2cap_chan *chan,
        if (!skb)
                return ERR_PTR(err);
 
+       /* Channel lock is released before requesting new skb and then
+        * reacquired thus we need to recheck channel state.
+        */
+       if (chan->state != BT_CONNECTED) {
+               kfree_skb(skb);
+               return ERR_PTR(-ENOTCONN);
+       }
+
        skb->priority = sk->sk_priority;
 
        bt_cb(skb)->l2cap.chan = chan;