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=1794061862; b=Ukewz33Xp+wZ2C4Aamn/VK4zKon6/PG6rPo92vNgzHPjWYf0mQQ7zKSTLjkCUk0bzCHE pJNnt8OLJeHxo3w9dA3+uR7fu5v8RWnz1kwFObmIH/4w5d0OaNg53QR7qFiGhMVcKgZv7 4K7fiXNyxaH/ECCLaR4qX/US002yWwJ85p33RF6l+2135xrNFIMEi5pwsAXzY+2VJp1YQ iPnqTSiDuMmfv4GPBCiLg8bkZM/7fpVnvlZxcE5DozuXkMgbfEPHUiDUiV9BAltFT9TLK DMA2YYWU+7wAX983rqkJVh8eZkIE3Dgw7Jner11D2vHPNnVcvLS8BM5483wQ6P8zDEQ== ARC-Message-Signature: i=1; d=subspace.kernel.org; s=arc20261015; a=rsa-sha256; c=relaxed/relaxed; t=1794061862; 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-Type:Content-Transfer-Encoding; bh=5YoB3phqmle/tZ9zySMgRCp+ll5ecjAMaGfXf1kM6l4=; b=5X5+kBJ+E63obx5uv9fCs/0M+anKeyEp4RzUVqTSD7yLztBBag/rsyQ8MaWF0jD4Yd7P 00vnKFoKo3+ITYYdH/rUzZSOobXUQMQ7XddsStmD7qOQHwmMPv++OOYf516KfZZf2il3h iwC90KuhJA6Om5Liydc+mKWd4IL+BJILpjDhE0QVj/q8h5La3KezC9HyM8+1/XFjFcUKQ 40evO/uLlBBEE8fzGqSxAM8MIqQjF1wBJcqHMh0rABSG2ETVxcieqCDuiHwqYFF7zkQQP 87K3njsaNhC2ArkOOOiQY0U9GLgHtiChIsfSw1xe8n7mG3kSAgOOKKWeT1ONq4mtNag== 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=IW3RXe0W; 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 2DB32606A326; Sat, 7 Nov 2026 14:31:02 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; dkim=pass (1024-bit key) header.d=linuxfoundation.org header.i=@linuxfoundation.org header.a=rsa-sha256 header.s=korg header.b=IW3RXe0W Received: by smtp.kernel.org (Postfix) with ESMTPSA id E0A4D1F0089F; Sat, 7 Nov 2026 14:31:01 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=linuxfoundation.org; s=korg; t=1794061862; bh=5YoB3phqmle/tZ9zySMgRCp+ll5ecjAMaGfXf1kM6l4=; h=From:To:Cc:Subject:Date:In-Reply-To:References; b=IW3RXe0WhjN9hKvNEf0G/gfNmAxnYUqI3qn5oMlYj1trpOEobjB8LmOm7KEVmxikm +pvhtNyNrhXWHSZ4sCsVL09iWbw9w/9znHABcRIxhaRrw0U9+cOaqlJbeM1JDbloSI FuVZMWdBhycF7TIMfMNIP4gnqO6mduH4Olqp/64A= From: Greg Kroah-Hartman To: stable@vger.kernel.org Cc: Greg Kroah-Hartman , patches@lists.linux.dev, Christoph Hellwig , =?UTF-8?q?J=C3=A9r=C3=A9my=20Jean?= , Keith Busch , Sasha Levin Subject: [PATCH 6.12 0879/1536] nvmet-auth: serialize access to DH-HMAC-CHAP state to fix UAF Date: Sat, 7 Nov 2026 08:48:15 -0500 Message-ID: <20261107135022.311991521@linuxfoundation.org> X-Mailer: git-send-email 2.56.0 In-Reply-To: <20261107134957.653319350@linuxfoundation.org> References: <20261107134957.653319350@linuxfoundation.org> User-Agent: quilt/0.69 X-stable: review X-Patchwork-Hint: ignore X-Mailing-List: stable@vger.kernel.org List-Id: List-Post: List-Help: List-Subscribe: List-Unsubscribe: List-Owner: Precedence: list MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 6.12-stable review patch. If anyone has any objections, please let me know. ------------------ From: Jérémy Jean [ Upstream commit 53f6efe00cea0c0cf7bd99debe3cdac8567170b5 ] A REPLY AUTH_SEND command can derive a dhchap_skey key while a concurrent AUTH_RECEIVE command observing this REPLY state executes the failure path and frees that key. The REPLY command then continues and writes into freed memory, causing a use-after-free. KASAN reports it as: BUG: KASAN: slab-use-after-free in __sha256_final Write of size 4 at addr ffff88800359c540 ... nvmet_auth_sq_free (drivers/nvme/target/auth.c:237) nvmet_execute_auth_receive (drivers/nvme/target/fabrics-cmd-auth.c:621) Add a mutex to each submission queue to serialize authentication commands and timeout handling. Unlock the mutex before completing requests and ignore outdated timeout callbacks. Limit timeout delays to prevent immediate expiry on 32-bit kernels. Fixes: 7a277c37d352 ("nvmet-auth: Diffie-Hellman key exchange support") Cc: stable@vger.kernel.org Assisted-by: LLM Reviewed-by: Christoph Hellwig Signed-off-by: Jérémy Jean Signed-off-by: Keith Busch Signed-off-by: Sasha Levin --- drivers/nvme/target/auth.c | 1 + drivers/nvme/target/fabrics-cmd-auth.c | 32 ++++++++++++++++++++++---- drivers/nvme/target/nvmet.h | 2 ++ 3 files changed, 31 insertions(+), 4 deletions(-) diff --git a/drivers/nvme/target/auth.c b/drivers/nvme/target/auth.c index 38d1ced21f532..ba12b59c67304 100644 --- a/drivers/nvme/target/auth.c +++ b/drivers/nvme/target/auth.c @@ -232,6 +232,7 @@ u8 nvmet_setup_auth(struct nvmet_ctrl *ctrl) void nvmet_auth_sq_free(struct nvmet_sq *sq) { + sq->auth_expiry_active = false; cancel_delayed_work(&sq->auth_expired_work); kfree(sq->dhchap_c1); sq->dhchap_c1 = NULL; diff --git a/drivers/nvme/target/fabrics-cmd-auth.c b/drivers/nvme/target/fabrics-cmd-auth.c index 92e75ebe7a173..de1a6d1a27a33 100644 --- a/drivers/nvme/target/fabrics-cmd-auth.c +++ b/drivers/nvme/target/fabrics-cmd-auth.c @@ -6,6 +6,7 @@ */ #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt #include +#include #include #include #include @@ -18,16 +19,27 @@ static void nvmet_auth_expired_work(struct work_struct *work) struct nvmet_sq *sq = container_of(to_delayed_work(work), struct nvmet_sq, auth_expired_work); + mutex_lock(&sq->auth_lock); + /* A command may have cancelled or rearmed this timeout. */ + if (!sq->auth_expiry_active || + delayed_work_pending(&sq->auth_expired_work)) + goto out_unlock; + sq->auth_expiry_active = false; + pr_debug("%s: ctrl %d qid %d transaction %u expired, resetting\n", __func__, sq->ctrl->cntlid, sq->qid, sq->dhchap_tid); sq->dhchap_step = NVME_AUTH_DHCHAP_MESSAGE_NEGOTIATE; sq->dhchap_tid = -1; +out_unlock: + mutex_unlock(&sq->auth_lock); } void nvmet_auth_sq_init(struct nvmet_sq *sq) { /* Initialize in-band authentication */ + mutex_init(&sq->auth_lock); INIT_DELAYED_WORK(&sq->auth_expired_work, nvmet_auth_expired_work); + sq->auth_expiry_active = false; sq->authenticated = false; sq->dhchap_step = NVME_AUTH_DHCHAP_MESSAGE_NEGOTIATE; } @@ -211,6 +223,8 @@ void nvmet_execute_auth_send(struct nvmet_req *req) u16 status = 0; u8 dhchap_status; + mutex_lock(&req->sq->auth_lock); + if (req->cmd->auth_send.secp != NVME_AUTH_DHCHAP_PROTOCOL_IDENTIFIER) { status = NVME_SC_INVALID_FIELD | NVME_STATUS_DNR; req->error_loc = @@ -236,8 +250,10 @@ void nvmet_execute_auth_send(struct nvmet_req *req) offsetof(struct nvmf_auth_send_command, tl); goto done; } - if (!nvmet_check_transfer_len(req, tl)) { + if (unlikely(tl != req->transfer_len)) { pr_debug("%s: transfer length mismatch (%u)\n", __func__, tl); + mutex_unlock(&req->sq->auth_lock); + nvmet_check_transfer_len(req, tl); return; } @@ -358,10 +374,12 @@ void nvmet_execute_auth_send(struct nvmet_req *req) status, req->error_loc); if (req->sq->dhchap_step != NVME_AUTH_DHCHAP_MESSAGE_SUCCESS2 && req->sq->dhchap_step != NVME_AUTH_DHCHAP_MESSAGE_FAILURE2) { - unsigned long auth_expire_secs = ctrl->kato ? ctrl->kato : 120; + unsigned long auth_expire = min_t(u64, MAX_JIFFY_OFFSET, + (u64)(ctrl->kato ? ctrl->kato : 120) * HZ); + req->sq->auth_expiry_active = true; mod_delayed_work(system_percpu_wq, &req->sq->auth_expired_work, - auth_expire_secs * HZ); + auth_expire); goto complete; } /* Final states, clear up variables */ @@ -370,6 +388,7 @@ void nvmet_execute_auth_send(struct nvmet_req *req) nvmet_ctrl_fatal_error(ctrl); complete: + mutex_unlock(&req->sq->auth_lock); nvmet_req_complete(req, status); } @@ -488,6 +507,8 @@ void nvmet_execute_auth_receive(struct nvmet_req *req) u32 al; u16 status = 0; + mutex_lock(&req->sq->auth_lock); + if (req->cmd->auth_receive.secp != NVME_AUTH_DHCHAP_PROTOCOL_IDENTIFIER) { status = NVME_SC_INVALID_FIELD | NVME_STATUS_DNR; req->error_loc = @@ -513,8 +534,10 @@ void nvmet_execute_auth_receive(struct nvmet_req *req) offsetof(struct nvmf_auth_receive_command, al); goto done; } - if (!nvmet_check_transfer_len(req, al)) { + if (unlikely(al != req->transfer_len)) { pr_debug("%s: transfer length mismatch (%u)\n", __func__, al); + mutex_unlock(&req->sq->auth_lock); + nvmet_check_transfer_len(req, al); return; } @@ -573,5 +596,6 @@ void nvmet_execute_auth_receive(struct nvmet_req *req) nvmet_auth_sq_free(req->sq); nvmet_ctrl_fatal_error(ctrl); } + mutex_unlock(&req->sq->auth_lock); nvmet_req_complete(req, status); } diff --git a/drivers/nvme/target/nvmet.h b/drivers/nvme/target/nvmet.h index 408c3cda47436..2a1a848189bea 100644 --- a/drivers/nvme/target/nvmet.h +++ b/drivers/nvme/target/nvmet.h @@ -110,8 +110,10 @@ struct nvmet_sq { u32 sqhd; bool sqhd_disabled; #ifdef CONFIG_NVME_TARGET_AUTH + struct mutex auth_lock; bool authenticated; struct delayed_work auth_expired_work; + bool auth_expiry_active; u16 dhchap_tid; u8 dhchap_status; u8 dhchap_step; -- 2.53.0