]> git.ipfire.org Git - thirdparty/kernel/linux.git/commitdiff
Bluetooth: bnep: pin L2CAP connection during netdev registration
authorYousef Alhouseen <alhouseenyousef@gmail.com>
Sun, 28 Jun 2026 00:50:58 +0000 (02:50 +0200)
committerLuiz Augusto von Dentz <luiz.von.dentz@intel.com>
Mon, 6 Jul 2026 14:46:57 +0000 (10:46 -0400)
bnep_add_connection() reads the L2CAP connection without holding the
channel lock, then passes its HCI device to register_netdev(). Controller
teardown can clear and release that connection concurrently, leaving the
network device registration path to dereference a freed parent device.

Take a reference to the L2CAP connection while holding the channel lock.
Retain it until register_netdev() has taken the parent device reference.

Fixes: 65f53e9802db ("Bluetooth: Access BNEP session addresses through L2CAP channel")
Reported-by: syzbot+fed5dce4553262f3b35c@syzkaller.appspotmail.com
Closes: https://syzkaller.appspot.com/bug?extid=fed5dce4553262f3b35c
Cc: stable@vger.kernel.org
Signed-off-by: Yousef Alhouseen <alhouseenyousef@gmail.com>
Signed-off-by: Luiz Augusto von Dentz <luiz.von.dentz@intel.com>
net/bluetooth/bnep/core.c

index add9a8f7535d482381cced94bda6a6bd2a3c4e05..f7d88c33e23e4fea5d27726cd3eb3be44e5317c8 100644 (file)
@@ -559,14 +559,18 @@ static int bnep_session(void *arg)
        return 0;
 }
 
-static struct device *bnep_get_device(struct bnep_session *session)
+static struct l2cap_conn *bnep_get_conn(struct bnep_session *session)
 {
-       struct l2cap_conn *conn = l2cap_pi(session->sock->sk)->chan->conn;
+       struct l2cap_chan *chan = l2cap_pi(session->sock->sk)->chan;
+       struct l2cap_conn *conn;
 
-       if (!conn || !conn->hcon)
-               return NULL;
+       l2cap_chan_lock(chan);
+       conn = chan->conn;
+       if (conn)
+               l2cap_conn_get(conn);
+       l2cap_chan_unlock(chan);
 
-       return &conn->hcon->dev;
+       return conn;
 }
 
 static const struct device_type bnep_type = {
@@ -578,6 +582,7 @@ int bnep_add_connection(struct bnep_connadd_req *req, struct socket *sock)
        u32 valid_flags = BIT(BNEP_SETUP_RESPONSE);
        struct net_device *dev;
        struct bnep_session *s, *ss;
+       struct l2cap_conn *conn = NULL;
        u8 dst[ETH_ALEN], src[ETH_ALEN];
        int err;
 
@@ -637,10 +642,18 @@ int bnep_add_connection(struct bnep_connadd_req *req, struct socket *sock)
        bnep_set_default_proto_filter(s);
 #endif
 
-       SET_NETDEV_DEV(dev, bnep_get_device(s));
+       conn = bnep_get_conn(s);
+       if (!conn) {
+               err = -ENOTCONN;
+               goto failed;
+       }
+
+       SET_NETDEV_DEV(dev, &conn->hcon->dev);
        SET_NETDEV_DEVTYPE(dev, &bnep_type);
 
        err = register_netdev(dev);
+       l2cap_conn_put(conn);
+       conn = NULL;
        if (err)
                goto failed;
 
@@ -662,6 +675,8 @@ int bnep_add_connection(struct bnep_connadd_req *req, struct socket *sock)
        return 0;
 
 failed:
+       if (conn)
+               l2cap_conn_put(conn);
        up_write(&bnep_session_sem);
        free_netdev(dev);
        return err;