From mboxrd@z Thu Jan 1 00:00:00 1970 Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.remote-ip=100.103.45.18 ARC-Seal: i=1; d=subspace.kernel.org; s=arc20261015; a=rsa-sha256; cv=none; t=1793211407; b=zvWT4IH9VwojVEK7DVgsCOdurK1xxgTxGOy771SuBbfrTL0FT1IZIQ9XWj/Tn39VLMLK PgrXbcK/lSYiDKxItMKOI6SRJBQIFwKA9T+cSt2IHdu2dV2wGXDNeJtG/NjNUOB3KoOnb vjHJ8QeGY07SdA/oGSHyrORiWyrwShWgiZOBruCK+pmjFL8OFwBaTBBxTLuNfcUGB0VRS D2VEvhNs1JvtPWks+rXsrgTnkkC5E45SZnjJnYrdj3GwwDWXHdNnzHRw62zmgYjN+BOmp 6FRWoadZZnTGib1jk8ihsDfSlmqm151fTo84a16mQNpPaSuFKnN8qmPCBTREAMnsl+w== ARC-Message-Signature: i=1; d=subspace.kernel.org; s=arc20261015; a=rsa-sha256; c=relaxed/relaxed; t=1793211407; h=Received:DKIM-Signature:From:To:Cc:Subject:Date:Message-ID:X-Mailer: In-Reply-To:References:User-Agent:X-stable:X-Patchwork-Hint: MIME-Version:Content-Transfer-Encoding; bh=x68vQlWjit/IIogwzw46D9feAzj94lTbisB24uaiF/I=; b=qOM2qoypTFUPkzoxAR6dzBJ2MZul/oJgKr0VOEiUpUy67Kf7uDd+O3bMIe0KMCypxAKs fB+6H452vtkOYGoBvwiSI1It+Js7/on4Hu9Gne0HUj8c79MSGAcsZ0+jj0s0Jr3S8mc6e ldtTSf2vDwiuSBV8VMAHY06QmylMju3Ocpc0NQ2KuPG+cCENsgtaa+x3fNyc03BlrJ8To kZR1/UuhppeAoBjAem8MJb78uSU90pGQszkkuuZS3oM4WOQP2qzk16hdMcqZ3vvKB3ZY+ VKQMivcnlwaPuq7nZOpH7UW52SXPZH5Mk+Amu2EgEFTWCIbZcRc35hvcnkUaLc6DXVw== ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dkim=pass header.d=linuxfoundation.org header.i=@linuxfoundation.org header.a=rsa-sha256 header.s=korg header.b=apEa2Vxi; arc=none smtp.remote-ip=100.103.45.18 Received: from smtp.kernel.org (quasi.space.kernel.org [100.103.45.18]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits) key-exchange X25519 server-signature ECDSA (prime256v1)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id 9B9CF606A321; Wed, 28 Oct 2026 18:16:47 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; dkim=pass (1024-bit key, unprotected) header.d=linuxfoundation.org header.i=@linuxfoundation.org header.a=rsa-sha256 header.s=korg header.b=apEa2Vxi Received: by smtp.kernel.org (Postfix) with ESMTPSA id EDE3D1F01825; Wed, 28 Oct 2026 18:16:46 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=linuxfoundation.org; s=korg; t=1793211407; bh=x68vQlWjit/IIogwzw46D9feAzj94lTbisB24uaiF/I=; h=From:To:Cc:Subject:Date:In-Reply-To:References; b=apEa2Vxi0594TKjgRJNLTyT90JYeCj//iN9o/1ANuYBnuIu8kJGUWoz4kSnRBAdL9 uTmWx5hO3nUPFXyto0xmSv1LO8hRb1vsIlFFggDaF8Xjk+3DOG8BcGyCGbmZ/3db0r OP+IXFNARnYt/y9daQLzp/cz4qch1gw9QUwXqE8A= From: Greg Kroah-Hartman To: stable@vger.kernel.org Cc: Greg Kroah-Hartman , patches@lists.linux.dev, Alan Borzeszkowski , Mika Westerberg , Sasha Levin Subject: [PATCH 5.15 321/571] thunderbolt: Fix KASAN reported use-after-free when request is canceled Date: Wed, 28 Oct 2026 17:57:48 +0100 Message-ID: <20261028170015.618913258@linuxfoundation.org> X-Mailer: git-send-email 2.56.0 In-Reply-To: <20261028170008.009143722@linuxfoundation.org> References: <20261028170008.009143722@linuxfoundation.org> User-Agent: quilt/0.69 X-stable: review X-Patchwork-Hint: ignore X-Mailing-List: patches@lists.linux.dev List-Id: List-Post: List-Help: List-Subscribe: List-Unsubscribe: List-Owner: Precedence: list MIME-Version: 1.0 Content-Transfer-Encoding: 8bit 5.15-stable review patch. If anyone has any objections, please let me know. ------------------ From: Mika Westerberg [ Upstream commit a02188ddc24b7872f0c5e0c5873f827318717803 ] Alan reported that when doing stress testing sometimes KASAN notices use-after-free during control channel operation (stripped down keeping the relevant parts): BUG: KASAN: slab-use-after-free in tb_cfg_request_sync+0x240/0x250 [thunderbolt] Read of size 24 at addr ffff88811067f290 by task kworker/u40:2/1760 tb_cfg_request_sync+0x240/0x250 [thunderbolt] tb_cfg_read_raw+0x367/0x510 [thunderbolt] tb_cfg_read+0xec/0x240 [thunderbolt] tb_port_get_link_generation+0x258/0x420 [thunderbolt] tb_usb3_consumed_bandwidth+0x1c1/0x2c0 [thunderbolt] tb_tunnel_consumed_bandwidth+0xfd/0x910 [thunderbolt] tb_available_bandwidth+0x5f2/0xeb0 [thunderbolt] tb_recalc_estimated_bandwidth+0x2a0/0x1bc0 [thunderbolt] tb_handle_dp_bandwidth_request+0x1897/0x5e20 [thunderbolt] process_one_work+0x675/0x1230 worker_thread+0x5e6/0xf70 kthread+0x365/0x470 ret_from_fork+0x54d/0x710 ret_from_fork_asm+0x1a/0x30 Allocated by task 1760: __kmalloc_cache_noprof+0x1ee/0x550 tb_cfg_read_raw+0x1d3/0x510 [thunderbolt] tb_cfg_read+0xec/0x240 [thunderbolt] tb_port_get_link_generation+0x258/0x420 [thunderbolt] tb_usb3_consumed_bandwidth+0x1c1/0x2c0 [thunderbolt] tb_tunnel_consumed_bandwidth+0xfd/0x910 [thunderbolt] tb_available_bandwidth+0x5f2/0xeb0 [thunderbolt] tb_recalc_estimated_bandwidth+0x2a0/0x1bc0 [thunderbolt] tb_handle_dp_bandwidth_request+0x1897/0x5e20 [thunderbolt] process_one_work+0x675/0x1230 worker_thread+0x5e6/0xf70 kthread+0x365/0x470 ret_from_fork+0x54d/0x710 ret_from_fork_asm+0x1a/0x30 Freed by task 926: kfree+0x18f/0x4a0 tb_cfg_request_put+0xb7/0xe0 [thunderbolt] tb_cfg_request_work+0x82/0x120 [thunderbolt] process_one_work+0x675/0x1230 worker_thread+0x5e6/0xf70 kthread+0x365/0x470 ret_from_fork+0x54d/0x710 ret_from_fork_asm+0x1a/0x30 Second to last potentially related work creation: __queue_work+0x575/0xd00 queue_work_on+0x77/0x80 tb_cfg_request_cancel+0xc7/0x260 [thunderbolt] tb_cfg_request_sync+0x1f6/0x250 [thunderbolt] tb_cfg_read_raw+0x367/0x510 [thunderbolt] tb_cfg_read+0xec/0x240 [thunderbolt] tb_port_get_link_generation+0x258/0x420 [thunderbolt] tb_usb3_consumed_bandwidth+0x1c1/0x2c0 [thunderbolt] tb_tunnel_consumed_bandwidth+0xfd/0x910 [thunderbolt] tb_available_bandwidth+0x5f2/0xeb0 [thunderbolt] tb_recalc_estimated_bandwidth+0x2a0/0x1bc0 [thunderbolt] tb_handle_dp_bandwidth_request+0x1897/0x5e20 [thunderbolt] process_one_work+0x675/0x1230 worker_thread+0x5e6/0xf70 kthread+0x365/0x470 ret_from_fork+0x54d/0x710 ret_from_fork_asm+0x1a/0x30 The last stack trace is helpful because it shows that we are cancelling a request and looking at tb_cfg_request_cancel() what might happen is that tb_cfg_request_work() completes right before tb_cfg_request_cancel() starts and because of this it will call schedule_work() queueing the same work to run again. However, it is already removed from the request_queue and reference count is dropped so when tb_cfg_request_work() triggers again it will access memory that is already released. Fix this so that we first make sure a cancelled request is not handed away from tb_cfg_request_find() or scheduled to run. Then instead of relying on the worker to clean up the request we will do it in tb_cfg_request_cancel() after the work is canceled from running. Make tb_cfg_request_dequeue() release the request only if it was actually removed from the queue. Reported-by: Alan Borzeszkowski Fixes: d7f781bfdbf4 ("thunderbolt: Rework control channel to be more reliable") Cc: stable@vger.kernel.org Signed-off-by: Mika Westerberg [ sashal: Replaced guard()/scoped_guard() with explicit mutex locking for GNU89 compatibility. ] Signed-off-by: Sasha Levin --- drivers/thunderbolt/ctl.c | 47 +++++++++++++++++++-------------------- 1 file changed, 23 insertions(+), 24 deletions(-) diff --git a/drivers/thunderbolt/ctl.c b/drivers/thunderbolt/ctl.c index 7c070a58f4b95..36c64ed467d55 100644 --- a/drivers/thunderbolt/ctl.c +++ b/drivers/thunderbolt/ctl.c @@ -65,7 +65,6 @@ struct tb_ctl { #define tb_ctl_dbg(ctl, format, arg...) \ dev_dbg(&(ctl)->nhi->pdev->dev, format, ## arg) -static DECLARE_WAIT_QUEUE_HEAD(tb_cfg_request_cancel_queue); /* Serializes access to request kref_get/put */ static DEFINE_MUTEX(tb_cfg_request_lock); @@ -123,11 +122,14 @@ void tb_cfg_request_put(struct tb_cfg_request *req) static int tb_cfg_request_enqueue(struct tb_ctl *ctl, struct tb_cfg_request *req) { + tb_cfg_request_get(req); + WARN_ON(test_bit(TB_CFG_REQUEST_ACTIVE, &req->flags)); WARN_ON(req->ctl); mutex_lock(&ctl->request_queue_lock); if (!ctl->running) { + tb_cfg_request_put(req); mutex_unlock(&ctl->request_queue_lock); return -ENOTCONN; } @@ -138,28 +140,24 @@ static int tb_cfg_request_enqueue(struct tb_ctl *ctl, return 0; } +static bool tb_cfg_request_is_active(struct tb_cfg_request *req) +{ + return test_bit(TB_CFG_REQUEST_ACTIVE, &req->flags); +} + static void tb_cfg_request_dequeue(struct tb_cfg_request *req) { struct tb_ctl *ctl = req->ctl; mutex_lock(&ctl->request_queue_lock); - if (!test_bit(TB_CFG_REQUEST_ACTIVE, &req->flags)) { - mutex_unlock(&ctl->request_queue_lock); - return; + if (tb_cfg_request_is_active(req)) { + list_del(&req->list); + clear_bit(TB_CFG_REQUEST_ACTIVE, &req->flags); + tb_cfg_request_put(req); } - - list_del(&req->list); - clear_bit(TB_CFG_REQUEST_ACTIVE, &req->flags); - if (test_bit(TB_CFG_REQUEST_CANCELED, &req->flags)) - wake_up(&tb_cfg_request_cancel_queue); mutex_unlock(&ctl->request_queue_lock); } -static bool tb_cfg_request_is_active(struct tb_cfg_request *req) -{ - return test_bit(TB_CFG_REQUEST_ACTIVE, &req->flags); -} - static struct tb_cfg_request * tb_cfg_request_find(struct tb_ctl *ctl, struct ctl_pkg *pkg) { @@ -169,7 +167,8 @@ tb_cfg_request_find(struct tb_ctl *ctl, struct ctl_pkg *pkg) mutex_lock(&pkg->ctl->request_queue_lock); list_for_each_entry(req, &pkg->ctl->request_queue, list) { tb_cfg_request_get(req); - if (req->match(req, pkg)) { + if (!test_bit(TB_CFG_REQUEST_CANCELED, &req->flags) && + req->match(req, pkg)) { found = true; break; } @@ -496,8 +495,11 @@ static void tb_ctl_rx_callback(struct tb_ring *ring, struct ring_frame *frame, */ req = tb_cfg_request_find(pkg->ctl, pkg); if (req) { - if (req->copy(req, pkg)) + mutex_lock(&pkg->ctl->request_queue_lock); + if (!test_bit(TB_CFG_REQUEST_CANCELED, &req->flags) && + req->copy(req, pkg)) schedule_work(&req->work); + mutex_unlock(&pkg->ctl->request_queue_lock); tb_cfg_request_put(req); } @@ -513,7 +515,6 @@ static void tb_cfg_request_work(struct work_struct *work) req->callback(req->callback_data); tb_cfg_request_dequeue(req); - tb_cfg_request_put(req); } /** @@ -537,10 +538,9 @@ int tb_cfg_request(struct tb_ctl *ctl, struct tb_cfg_request *req, INIT_WORK(&req->work, tb_cfg_request_work); INIT_LIST_HEAD(&req->list); - tb_cfg_request_get(req); ret = tb_cfg_request_enqueue(ctl, req); if (ret) - goto err_put; + return ret; ret = tb_ctl_tx(ctl, req->request, req->request_size, req->request_type); @@ -554,9 +554,6 @@ int tb_cfg_request(struct tb_ctl *ctl, struct tb_cfg_request *req, err_dequeue: tb_cfg_request_dequeue(req); -err_put: - tb_cfg_request_put(req); - return ret; } @@ -570,9 +567,11 @@ int tb_cfg_request(struct tb_ctl *ctl, struct tb_cfg_request *req, */ void tb_cfg_request_cancel(struct tb_cfg_request *req, int err) { + mutex_lock(&req->ctl->request_queue_lock); set_bit(TB_CFG_REQUEST_CANCELED, &req->flags); - schedule_work(&req->work); - wait_event(tb_cfg_request_cancel_queue, !tb_cfg_request_is_active(req)); + mutex_unlock(&req->ctl->request_queue_lock); + cancel_work_sync(&req->work); + tb_cfg_request_dequeue(req); req->result.err = err; } -- 2.53.0