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=1793840341; b=TWgASYnchhKczTLP9BZF8R9+9uvycrJsG0WHj6zS/KVENymt1a8qV42RSA804TASOS7+ dxYy1NQKZZEEVY4Os8t5DMtdN51yFLrjsWWbdZBV6RCQ6VVMGuQgypelDZebvxbVZHhHZ 6EWu3+OYhTvSHtNJNieHv8q9RoDezi5EwYrzMlUxBZHVRm7N3RcWr26ryya3xfcGqcTXa vzUl+59YI12/Zapkki88exSIasHrmLu28rZHrJ1e9Nn4xr+Eem4UJQJvCxRIrOC2H+b1f O5PtV23SFFn/wO3G5NL6UOhqdmY1ymJRoJpu2iL1goB1amX5X9R2yC6txV0wmRWmfUA== ARC-Message-Signature: i=1; d=subspace.kernel.org; s=arc20261015; a=rsa-sha256; c=relaxed/relaxed; t=1793840341; 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=uvtp9FZ8IpptF4OjodKROBPYZ3OtcDb4tOOa9w4fZ+8=; b=biWhTAxEQNMb1TURhFnXKampXsVhp6KBks+y/wCdUGSrtWGjjN19kqJndm3icMGfohnc YPNlrU3h3qusLADnMdzB3ZsJ1V8H47LUMWxmzbKl3Qz/7g63glOZ3x4VWIp/eiGxabYHr xKxHPq4xq3/rU5CSJ9oKjrAW28TYUqVO0ZGRRrbzQWVq5M9pIv+JNqDdWw6bm9BKswYec 4uCIWXIVR55asLTdXK7gAZ97dU3w7RAOjX/Yjbc/FQ9MS+ODCENbZfEsuypocnctoTM3G MBwApDDFQ5v3qh2pt2Sh220fFutMaqQHETYR6YCHlDY8Ho0BSr5WygqQw+pzIDIOmIg== 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="gVX55Ev/"; 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 8DD9B606A31B for ; Thu, 5 Nov 2026 00:59:01 +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=gVX55Ev/ Received: by smtp.kernel.org (Postfix) with ESMTPSA id C39551F0093A; Thu, 5 Nov 2026 00:59:00 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=kernel.org; s=k20260515; t=1793840341; bh=uvtp9FZ8IpptF4OjodKROBPYZ3OtcDb4tOOa9w4fZ+8=; h=From:To:Cc:Subject:Date:In-Reply-To:References; b=gVX55Ev/46H3nDIuFdLmMfaoGwDz19cbyka3ZQAASktrH5tGgu/ZNcT0jv1csED2S 1hCeiaY9PsGbP8mY9UjvzRvntEQftMvg9LtiVOWU2ACFfgsyiXkwOUeycZFngn+jIm gNjiaXHIZnilLlwTmuQEvRghRJ+uJQtftAJtAN8tlf6nBrG4LVPLZJWegnQOlfFxeh KyJsw5W0CX0ZXXjHTn+LiZK31257s44Vs+XICeL5Iedsq8ZN85cBptPW6VutZYOuAJ 53SlqPXruMRHgHRx5zi/v6Oi2uCWu8wHCZ2XHeDv5GZdlyqSxQGuGZ6Qyja23aLvaz ZAcRzR9jGT6Iw== 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.12.y 2/2] nvmet-auth: serialize access to DH-HMAC-CHAP state to fix UAF Date: Wed, 4 Nov 2026 19:58:58 -0500 Message-ID: <20261105005847.1079856-2-sashal@kernel.org> X-Mailer: git-send-email 2.53.0 In-Reply-To: <20261105005847.1079856-1-sashal@kernel.org> References: <2026110455-poet-brute-7114@gregkh> <20261105005847.1079856-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 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