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=1793840708; b=3rtXrhngzYBTIwjZWIugu/ZtZbvZvJ1PtAVeRQQYEz8ygHyBLzot9LVvCpbDvnqootXE JGCUS4g2AeqICn1404o9uZej5Uu3DJ/gj5LeAm3Ifs5Svrdmk4T2Usxz8s3MQZPZC81LK gbkYLcSAOIkIl2TeUphjKi1muzu/5+ja6w0rXMp0gxe24DXJZicC5Pgk/q2H92OWNQv4h Qikx4+UHWie8e19SEb3BkYXuWLrNUn3cVztAW7NWYgwStx46QNOJ31FFMxU+WfCgHGFky SZTNA6D0EwV1lDL37Eg6/7WrWO1yTLd4/vPrwQpBUC2r1BkhzgxqLw+DcVsIhSpoUgQ== ARC-Message-Signature: i=1; d=subspace.kernel.org; s=arc20261015; a=rsa-sha256; c=relaxed/relaxed; t=1793840708; h=Received:DKIM-Signature:From:To:Cc:Subject:Date:Message-ID:X-Mailer: In-Reply-To:References:MIME-Version:Content-Type: Content-Transfer-Encoding; bh=ktuUAM4SQfq2QBzjWDcYtj0yL5dPDAq+Ka/AVJlY30Y=; b=2LNrl9BfHYVDY6TIL2itIXm/NgQ75VWETKT5qjufzLuyNAtZxYM717txGlgDZ/liR8uX 8eyaqqvjfoRzjHvDatBlqhaDUaHhZpjeagTW4jCTXhVVuvnDd/h9FUMq8Q4xN5Nc7oovU mFrqM1C17dnCejjomOcjXOrVWZw5c3utx51Pv+JKaexNM9CDVG37e79cxLffOpQEe07Ge rApU4KdIfKRPq2DYEz9JViLNZGPSlMXDPUPwzgqVrKyhQ/h71hHxYNxsFnJVDbWIZpj2x vQJTLO7kVgaV0HYFEIMDdjqYYzO67lPGboluGUeb4p/b6z95J8yAfUgRYtBy/KLIPwg== ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dkim=pass header.d=kernel.org header.i=@kernel.org header.a=rsa-sha256 header.s=k20260515 header.b=EOVnAxG8; 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 F0139606D803 for ; Thu, 5 Nov 2026 01:05:07 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key, unprotected) header.d=kernel.org header.i=@kernel.org header.a=rsa-sha256 header.s=k20260515 header.b=EOVnAxG8 Received: by smtp.kernel.org (Postfix) with ESMTPSA id 441281F00A15; Thu, 5 Nov 2026 01:05:07 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=kernel.org; s=k20260515; t=1793840707; bh=ktuUAM4SQfq2QBzjWDcYtj0yL5dPDAq+Ka/AVJlY30Y=; h=From:To:Cc:Subject:Date:In-Reply-To:References; b=EOVnAxG8Bxu/afDAm4XYLaNIW3lw1COxV7mqge2J8U8KhJTLEOY3v9aVkoHgYjM0e VwiIhLCDECd9R7Mzl61SbgNU6jFu7/gs/NBgIhq9tBeMd5UMpeLK4kRZMRXAHmeD52 6fY4duQOfTvqfdMArTeByOYjCBtnP5uPwaDmvx4vtRZQdE8wj1OzcP/kgjB571196X 2DtVfe0mJy2yZgH+Usqy5OABOLTX4mIm2JWqMXgOuHyAoCK72azmoapVecSJatlWpj KTj/nVS3+xg4bG1ymAVVooY9oQnMAW/TePaqWAFD5nmYk4DjrGmKJ1OwpDriLEEiLB PbJ26sqizalZg== From: Sasha Levin To: stable@vger.kernel.org Cc: =?UTF-8?q?J=C3=A9r=C3=A9my=20Jean?= , Christoph Hellwig , Keith Busch , Sasha Levin Subject: [PATCH 6.6.y 2/2] nvmet-auth: serialize access to DH-HMAC-CHAP state to fix UAF Date: Wed, 4 Nov 2026 20:05:05 -0500 Message-ID: <20261105010458.1097143-2-sashal@kernel.org> X-Mailer: git-send-email 2.53.0 In-Reply-To: <20261105010458.1097143-1-sashal@kernel.org> References: <2026110458-chariot-kinfolk-545d@gregkh> <20261105010458.1097143-1-sashal@kernel.org> 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 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 b3a0e19464539..59762bbb98da3 100644 --- a/drivers/nvme/target/auth.c +++ b/drivers/nvme/target/auth.c @@ -218,6 +218,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 8d97ced8b1d19..c1d4e068ff721 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 2189e5a5985bf..56b9bbe8c6026 100644 --- a/drivers/nvme/target/nvmet.h +++ b/drivers/nvme/target/nvmet.h @@ -109,8 +109,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