FAILED: patch "[PATCH] Bluetooth: L2CAP: Fix use-after-free in" failed to apply to 6.12-stable tree
Subsystems (2)
[email protected]
maintainerThe patch below does not apply to the 6.12-stable tree.
If someone wants it applied there, or to any other stable or longterm tree, then please email the backport, including the original git commit id to <[email protected]>.
To reproduce the conflict and resubmit, you may use the following commands:
git fetch https://git.kernel.org/pub/scm/linux/kernel/git/stable/linux.git/ linux-6.12.y
git checkout FETCH_HEAD
git cherry-pick -x 6fef032af0092ed5ccb767239a9ac1bc38c08a40
# <resolve conflicts, build, test, etc.>
git commit -s
git send-email --to '<[email protected]>' --in-reply-to '2026071307-propose-retold-c250@gregkh' --subject-prefix 'PATCH 6.12.y' 'HEAD^..'
Possible dependencies:
thanks,
greg k-h
------------------ original commit in Linus's tree ------------------
From 6fef032af0092ed5ccb767239a9ac1bc38c08a40 Mon Sep 17 00:00:00 2001
From: Siwei Zhang <[email protected]>
Date: Mon, 29 Jun 2026 09:49:58 -0400
Subject: [PATCH] Bluetooth: L2CAP: Fix use-after-free in
l2cap_sock_new_connection_cb()
l2cap_sock_new_connection_cb() returned l2cap_pi(sk)->chan after release_sock(parent). Once the parent lock is dropped the newly enqueued child socket sk is reachable via the accept queue, so another task can accept and free it before the callback dereferences sk, resulting in a use-after-free.
Rework the ->new_connection() op so the core, rather than the callback, owns the child channel's lifetime. The op now receives a pre-allocated new_chan and returns an errno instead of allocating and returning a channel. l2cap_new_connection() allocates the child channel and links it into the conn list via __l2cap_chan_add() before invoking the callback, so the conn-list reference keeps the channel alive once release_sock(parent) exposes the socket to other tasks.
Channel configuration that was duplicated in l2cap_sock_init() and the various new_connection callbacks is consolidated into l2cap_chan_set_defaults(), which now inherits from the parent channel when one is supplied.
include/net/bluetooth/l2cap.h +6 -4
@@ -617,7 +617,8 @@ struct l2cap_chan {struct l2cap_ops {char *name; -struct l2cap_chan *(*new_connection) (struct l2cap_chan *chan); +int (*new_connection)(struct l2cap_chan *chan, +struct l2cap_chan *new_chan);int (*recv) (struct l2cap_chan * chan,struct sk_buff *skb);void (*teardown) (struct l2cap_chan *chan,int err);
@@ -882,9 +883,10 @@ static inline __u16 __next_seq(struct l2cap_chan *chan, __u16 seq)return (seq +1 ) % (chan->tx_win_max +1 ); } -static inline struct l2cap_chan *l2cap_chan_no_new_connection (struct l2cap_chan *chan) +static inline int l2cap_chan_no_new_connection (struct l2cap_chan *chan, +struct l2cap_chan *new_chan) { -return NULL ; +return -EOPNOTSUPP ; }static inline int l2cap_chan_no_recv (struct l2cap_chan *chan,struct sk_buff *skb)
@@ -961,7 +963,7 @@ int l2cap_chan_send(struct l2cap_chan *chan, struct msghdr *msg, size_t len,void l2cap_chan_busy (struct l2cap_chan *chan,int busy);void l2cap_chan_rx_avail (struct l2cap_chan *chan,ssize_t rx_avail);int l2cap_chan_check_security (struct l2cap_chan *chan,bool initiator); -void l2cap_chan_set_defaults (struct l2cap_chan *chan); +void l2cap_chan_set_defaults (struct l2cap_chan *chan,struct l2cap_chan *pchan);int l2cap_ertm_init (struct l2cap_chan *chan);void l2cap_chan_add (struct l2cap_conn *conn,struct l2cap_chan *chan);void __l2cap_chan_add (struct l2cap_conn *conn,struct l2cap_chan *chan);
net/bluetooth/6lowpan.c +1 -17
@@ -632,7 +632,7 @@ static struct l2cap_chan *chan_create(void)if (!chan)return NULL ; -l2cap_chan_set_defaults (chan); +l2cap_chan_set_defaults (chan,NULL ); chan->chan_type =L2CAP_CHAN_CONN_ORIENTED ; chan->mode =L2CAP_MODE_LE_FLOWCTL ;
@@ -745,21 +745,6 @@ static inline void chan_ready_cb(struct l2cap_chan *chan)ifup (dev->netdev); } -static inline struct l2cap_chan *chan_new_conn_cb (struct l2cap_chan *pchan) -{ -struct l2cap_chan *chan; - - chan =chan_create (); -if (!chan) -return NULL ; - - chan->ops = pchan->ops; - -BT_DBG ("chan %p pchan %p" , chan, pchan); - -return chan; -} -static void unregister_dev (struct lowpan_btle_dev *dev) {struct hci_dev *hdev =READ_ONCE (dev->hdev);
@@ -889,7 +874,6 @@ static long chan_get_sndtimeo_cb(struct l2cap_chan *chan)static const struct l2cap_ops bt_6lowpan_chan_ops = { .name ="L2CAP 6LoWPAN channel" , - .new_connection = chan_new_conn_cb, .recv = chan_recv_cb, .close = chan_close_cb, .state_change = chan_state_change_cb,
net/bluetooth/l2cap_core.c +65 -13
143 lines not on this page · raw message
net/bluetooth/l2cap_sock.c +49 -54
201 lines not on this page · raw message
net/bluetooth/smp.c +6 -21
@@ -3201,34 +3201,19 @@ static const struct l2cap_ops smp_chan_ops = { .get_sndtimeo = l2cap_chan_no_get_sndtimeo, }; -static inline struct l2cap_chan *smp_new_conn_cb (struct l2cap_chan *pchan) +static inline int smp_new_conn_cb (struct l2cap_chan *chan, +struct l2cap_chan *new_chan) { -struct l2cap_chan *chan; - -BT_DBG ("pchan %p" , pchan); - - chan =l2cap_chan_create (); -if (!chan) -return NULL ; - - chan->chan_type = pchan->chan_type; - chan->ops = &smp_chan_ops; - chan->scid = pchan->scid; - chan->dcid = chan->scid; - chan->imtu = pchan->imtu; - chan->omtu = pchan->omtu; - chan->mode = pchan->mode; + new_chan->ops = &smp_chan_ops;/* Other L2CAP channels may request SMP routines in order to * change the security level. This means that the SMP channel * lock must be considered in its own category to avoid lockdep * warnings. */ -atomic_set (&chan->nesting,L2CAP_NESTING_SMP ); +atomic_set (&new_chan->nesting,L2CAP_NESTING_SMP ); -BT_DBG ("created chan %p" , chan); - -return chan; +return 0 ; }static const struct l2cap_ops smp_root_chan_ops = {
@@ -3288,7 +3273,7 @@ static struct l2cap_chan *smp_add_cid(struct hci_dev *hdev, u16 cid)l2cap_add_scid (chan, cid); -l2cap_chan_set_defaults (chan); +l2cap_chan_set_defaults (chan,NULL );if (cid ==L2CAP_CID_SMP ) {u8 bdaddr_type;
Sasha Levin
maintainerPATCH 1/2 Bluetooth: 6lowpan: fix cyclic locking warning on netdev unregister
From: Pauli Virtanen <[email protected]>
[ Upstream commit 9707a015fe8f3ba8ec7c270f3b2b8efb38823d6b ]
6lowpan.c has theoretically conflicting lock orderings, which lockdep complains about:
a) rtnl_lock > hdev->workqueue
from 6lowpan.c:delete_netdev -> rtnl_lock -> device_del
-> put_device(parent) -> hci_release_dev -> destroy_workqueue
b) hdev->workqueue > l2cap_conn->lock > chan->lock > rtnl_lock
from hci_rx_work -> 6lowpan.c:chan_ready_cb
-> lowpan_register_netdev, ifup -> rtnl_lockActual deadlock appears not possible, as hci_rx_work is disabled and l2cap_conn flushed already on hdev unregister. Hence, do minimal thing to make lockdep happy by breaking chain a) by holding hdev refcount until after netdev put in 6lowpan.c.
Fixes the lockdep complaint:
WARNING: possible circular locking dependency detected.
kworker/0:1/11 is trying to acquire lock:ffff8880023b3940 ((wq_completion)hci0#2){+.+.}-{0:0}, at: touch_wq_lockdep_map+0x8b/0x130 but task is already holding lock:ffffffff95e4f9c0 (rtnl_mutex){+.+.}-{4:4}, at: lowpan_unregister_netdev+0xd/0x30
Workqueue: events delete_netdev
Stable-dep-of: 6fef032af009 ("Bluetooth: L2CAP: Fix use-after-free in l2cap_sock_new_connection_cb()")
--- net/bluetooth/6lowpan.c | 25 +++++++++++++++++++++++-- 1 file changed, 23 insertions(+), 2 deletions(-)
net/bluetooth/6lowpan.c +23 -2
@@ -759,13 +759,33 @@ static inline struct l2cap_chan *chan_new_conn_cb(struct l2cap_chan *pchan)return chan; } +static void unregister_dev (struct lowpan_btle_dev *dev) +{ +struct hci_dev *hdev =READ_ONCE (dev->hdev); + +/* If netdev holds last reference to hci_dev (its parent device), this +* leads to theoretical cyclic locking on lowpan_unregister_netdev: +* +* rtnl_lock -> put_device(parent) -> hci_release_dev -> +* destroy_workqueue -> hci_rx_work -> l2cap_recv_acldata -> +* chan_ready_cb -> ifup -> rtnl_lock +* +* However, hci_rx_work is disabled in hci_unregister_dev, so this +* should not occur. Make lockdep happy by postponing hdev release after +* netdev put. +*/ +hci_dev_hold (hdev); +lowpan_unregister_netdev (dev->netdev); +hci_dev_put (hdev); +} +static void delete_netdev (struct work_struct *work) {struct lowpan_btle_dev *entry =container_of (work, struct lowpan_btle_dev, delete_netdev); -lowpan_unregister_netdev (entry->netdev); +unregister_dev (entry);/* The entry pointer is deleted by the netdev destructor. */ }
@@ -1245,6 +1265,7 @@ static void disconnect_devices(void)break ; new_dev->netdev = entry->netdev; + new_dev->hdev = entry->hdev;INIT_LIST_HEAD (&new_dev->list);list_add_rcu (&new_dev->list, &devices);
@@ -1256,7 +1277,7 @@ static void disconnect_devices(void)ifdown (entry->netdev);BT_DBG ("Unregistering netdev %s %p" , entry->netdev->name, entry->netdev); -lowpan_unregister_netdev (entry->netdev); +unregister_dev (entry);kfree (entry); } }
Signature · 1 line
2.53.0
Sasha Levin
maintainerPATCH 2/2 Bluetooth: L2CAP: Fix use-after-free in l2cap_sock_new_connection_cb()
From: Siwei Zhang <[email protected]>
[ Upstream commit 6fef032af0092ed5ccb767239a9ac1bc38c08a40 ]
l2cap_sock_new_connection_cb() returned l2cap_pi(sk)->chan after release_sock(parent). Once the parent lock is dropped the newly enqueued child socket sk is reachable via the accept queue, so another task can accept and free it before the callback dereferences sk, resulting in a use-after-free.
Rework the ->new_connection() op so the core, rather than the callback, owns the child channel's lifetime. The op now receives a pre-allocated new_chan and returns an errno instead of allocating and returning a channel. l2cap_new_connection() allocates the child channel and links it into the conn list via __l2cap_chan_add() before invoking the callback, so the conn-list reference keeps the channel alive once release_sock(parent) exposes the socket to other tasks.
Channel configuration that was duplicated in l2cap_sock_init() and the various new_connection callbacks is consolidated into l2cap_chan_set_defaults(), which now inherits from the parent channel when one is supplied.
--- include/net/bluetooth/l2cap.h | 10 ++-- net/bluetooth/6lowpan.c | 18 +----- net/bluetooth/l2cap_core.c | 78 ++++++++++++++++++++----- net/bluetooth/l2cap_sock.c | 103 ++++++++++++++++------------------ net/bluetooth/smp.c | 27 ++------- 5 files changed, 127 insertions(+), 109 deletions(-)
include/net/bluetooth/l2cap.h +6 -4
@@ -620,7 +620,8 @@ struct l2cap_chan {struct l2cap_ops {char *name; -struct l2cap_chan *(*new_connection) (struct l2cap_chan *chan); +int (*new_connection)(struct l2cap_chan *chan, +struct l2cap_chan *new_chan);int (*recv) (struct l2cap_chan * chan,struct sk_buff *skb);void (*teardown) (struct l2cap_chan *chan,int err);
@@ -884,9 +885,10 @@ static inline __u16 __next_seq(struct l2cap_chan *chan, __u16 seq)return (seq +1 ) % (chan->tx_win_max +1 ); } -static inline struct l2cap_chan *l2cap_chan_no_new_connection (struct l2cap_chan *chan) +static inline int l2cap_chan_no_new_connection (struct l2cap_chan *chan, +struct l2cap_chan *new_chan) { -return NULL ; +return -EOPNOTSUPP ; }static inline int l2cap_chan_no_recv (struct l2cap_chan *chan,struct sk_buff *skb)
@@ -962,7 +964,7 @@ int l2cap_chan_send(struct l2cap_chan *chan, struct msghdr *msg, size_t len);void l2cap_chan_busy (struct l2cap_chan *chan,int busy);void l2cap_chan_rx_avail (struct l2cap_chan *chan,ssize_t rx_avail);int l2cap_chan_check_security (struct l2cap_chan *chan,bool initiator); -void l2cap_chan_set_defaults (struct l2cap_chan *chan); +void l2cap_chan_set_defaults (struct l2cap_chan *chan,struct l2cap_chan *pchan);int l2cap_ertm_init (struct l2cap_chan *chan);void l2cap_chan_add (struct l2cap_conn *conn,struct l2cap_chan *chan);void __l2cap_chan_add (struct l2cap_conn *conn,struct l2cap_chan *chan);
net/bluetooth/6lowpan.c +1 -17
@@ -631,7 +631,7 @@ static struct l2cap_chan *chan_create(void)if (!chan)return NULL ; -l2cap_chan_set_defaults (chan); +l2cap_chan_set_defaults (chan,NULL ); chan->chan_type =L2CAP_CHAN_CONN_ORIENTED ; chan->mode =L2CAP_MODE_LE_FLOWCTL ;
@@ -744,21 +744,6 @@ static inline void chan_ready_cb(struct l2cap_chan *chan)ifup (dev->netdev); } -static inline struct l2cap_chan *chan_new_conn_cb (struct l2cap_chan *pchan) -{ -struct l2cap_chan *chan; - - chan =chan_create (); -if (!chan) -return NULL ; - - chan->ops = pchan->ops; - -BT_DBG ("chan %p pchan %p" , chan, pchan); - -return chan; -} -static void unregister_dev (struct lowpan_btle_dev *dev) {struct hci_dev *hdev =READ_ONCE (dev->hdev);
@@ -900,7 +885,6 @@ static long chan_get_sndtimeo_cb(struct l2cap_chan *chan)static const struct l2cap_ops bt_6lowpan_chan_ops = { .name ="L2CAP 6LoWPAN channel" , - .new_connection = chan_new_conn_cb, .recv = chan_recv_cb, .close = chan_close_cb, .state_change = chan_state_change_cb,
net/bluetooth/l2cap_core.c +65 -13
143 lines not on this page · raw message
net/bluetooth/l2cap_sock.c +49 -54
201 lines not on this page · raw message
net/bluetooth/smp.c +6 -21
@@ -3234,34 +3234,19 @@ static const struct l2cap_ops smp_chan_ops = { .get_sndtimeo = l2cap_chan_no_get_sndtimeo, }; -static inline struct l2cap_chan *smp_new_conn_cb (struct l2cap_chan *pchan) +static inline int smp_new_conn_cb (struct l2cap_chan *chan, +struct l2cap_chan *new_chan) { -struct l2cap_chan *chan; - -BT_DBG ("pchan %p" , pchan); - - chan =l2cap_chan_create (); -if (!chan) -return NULL ; - - chan->chan_type = pchan->chan_type; - chan->ops = &smp_chan_ops; - chan->scid = pchan->scid; - chan->dcid = chan->scid; - chan->imtu = pchan->imtu; - chan->omtu = pchan->omtu; - chan->mode = pchan->mode; + new_chan->ops = &smp_chan_ops;/* Other L2CAP channels may request SMP routines in order to * change the security level. This means that the SMP channel * lock must be considered in its own category to avoid lockdep * warnings. */ -atomic_set (&chan->nesting,L2CAP_NESTING_SMP ); - -BT_DBG ("created chan %p" , chan); +atomic_set (&new_chan->nesting,L2CAP_NESTING_SMP ); -return chan; +return 0 ; }static const struct l2cap_ops smp_root_chan_ops = {
@@ -3332,7 +3317,7 @@ static struct l2cap_chan *smp_add_cid(struct hci_dev *hdev, u16 cid)l2cap_add_scid (chan, cid); -l2cap_chan_set_defaults (chan); +l2cap_chan_set_defaults (chan,NULL );if (cid ==L2CAP_CID_SMP ) {u8 bdaddr_type;
Signature · 1 line
2.53.0