From: Luiz Augusto von Dentz <luiz.von.dentz@xxxxxxxxx> Even though l2cap_chan_del calls __clear_chan_timer that uses cancel_delayed_work which doesn't prevent the work to run as it could be already pending. So instead this check if chan->conn has been set to NULL, which indicates that l2cap_conn_del might have run, and proceed to release its reference since the channel has already been cleanup. Reported-by: syzbot+008cdbf7a9044c2c2f99@xxxxxxxxxxxxxxxxxxxxxxxxx Signed-off-by: Luiz Augusto von Dentz <luiz.von.dentz@xxxxxxxxx> --- net/bluetooth/l2cap_core.c | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/net/bluetooth/l2cap_core.c b/net/bluetooth/l2cap_core.c index 9ebb85df4db4..4408e07a00d1 100644 --- a/net/bluetooth/l2cap_core.c +++ b/net/bluetooth/l2cap_core.c @@ -414,11 +414,21 @@ static void l2cap_chan_timeout(struct work_struct *work) { struct l2cap_chan *chan = container_of(work, struct l2cap_chan, chan_timer.work); - struct l2cap_conn *conn = chan->conn; + struct l2cap_conn *conn; int reason; BT_DBG("chan %p state %s", chan, state_to_string(chan->state)); + conn = chan->conn; + if (!conn) { + /* Channel is no longer attached to a connection so + * l2cap_conn_del might have run, just release reference + * acquired via __set_chan_timer. + */ + l2cap_chan_put(chan); + return; + } + mutex_lock(&conn->chan_lock); /* __set_chan_timer() calls l2cap_chan_hold(chan) while scheduling * this work. No need to call l2cap_chan_hold(chan) here again. -- 2.31.1