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=1793828896; b=y8Bq3ytJhmMOuzqTNQOUYb/QNL/doD2bhT75qySVlrkRwpW0KQIYyXkq0pqsS8iXg0tC sGZ/GHKVqXuLh0gVNmMbaJeTPbRpFxoigL0eoCa3I8+lzANlQ4XzUlTJ/5BT9UYxotNCt tQ/FLzkEuN8GdHb4v2YdAi39UfVWJ+LRVgVECaPgsvwNzJv681SFI/EjtGqPSNQw8ovlu y2t4ZWkGSm9rOW6qsG3+Da52tRMxkc5l9x08QaThUc3hpo+G/TCOrm2JGc7eAqpzi74sS HRwtUOtRT/tImThk2UNuM7Qv0B5WD388Ey2QGUZOBFLqu1hTrMna6iMh62BLGtn3Rzw== ARC-Message-Signature: i=1; d=subspace.kernel.org; s=arc20261015; a=rsa-sha256; c=relaxed/relaxed; t=1793828896; 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=bSDtTSbit39dk9Tw4Q3rAjOBPVB/6KXwComGaNi0wiI=; b=qn8hN5GT4Bi72Udh+8maKcbfpA+cXJUbcodBOmVvChUN+mJbwf5o2cAOmuA8GWdKQ6sh 76CMvrIGj1h7sKc9c6xHKQz7RIThBwSVqlw2uKfCMtJrOJ6XBIaVawe9owXlC09jJX6Ve wx+h0GN415eXEl+MmyjjfbZUA7s81e6hJFYZd5gy28zv9MR6ihWCoqFlXfP6MN50UvOvy yRpEx6v0N7kucR0w+wkT2yoAUJWj/x9vlPl6Wo49Iwf5CDNbB3yZMtJ9+AcOev11C3TgL P6DcnBKmkR3zMJ3GWE1Mvoh2e0o76Nj48VUmJtO8JfWuZWC1DV1nQAsPAxT2ig3vAnQ== 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="YElv/Fjx"; 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 22E396071AE2; Wed, 4 Nov 2026 21:48:16 +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=YElv/Fjx Received: by smtp.kernel.org (Postfix) with ESMTPSA id B67BC1F000C1; Wed, 4 Nov 2026 21:48:15 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=linuxfoundation.org; s=korg; t=1793828896; bh=bSDtTSbit39dk9Tw4Q3rAjOBPVB/6KXwComGaNi0wiI=; h=From:To:Cc:Subject:Date:In-Reply-To:References; b=YElv/FjxxiifAh+62Zlpj/ksDokans7BCCZVQ5tGNRx3j6XUFCDPoAv6lxrp22HMI 4SOJCaRp2hS1EaW1oTcH1C0YhMWXsYjZAVPjHImJXYVGRci6JDe0CdY7ZqvtF5aNoV T1YOb7dr8Q8BvsCz9zswAmVRg8Pi0DyGiV2C845I= 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 Subject: [PATCH 7.3 261/547] nvmet-auth: serialize access to DH-HMAC-CHAP state to fix UAF Date: Wed, 4 Nov 2026 16:07:25 -0500 Message-ID: <20261104211045.603693153@linuxfoundation.org> X-Mailer: git-send-email 2.56.0 In-Reply-To: <20261104211038.406938024@linuxfoundation.org> References: <20261104211038.406938024@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-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 7.3-stable review patch. If anyone has any objections, please let me know. ------------------ From: Jérémy Jean commit 53f6efe00cea0c0cf7bd99debe3cdac8567170b5 upstream. 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: Greg Kroah-Hartman --- 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(-) --- a/drivers/nvme/target/auth.c +++ b/drivers/nvme/target/auth.c @@ -229,6 +229,7 @@ out_unlock: 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; --- 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 @@ -17,16 +18,27 @@ static void nvmet_auth_expired_work(stru 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; } @@ -256,6 +268,8 @@ void nvmet_execute_auth_send(struct nvme 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 = @@ -281,8 +295,10 @@ void nvmet_execute_auth_send(struct nvme 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; } @@ -406,10 +422,12 @@ done: 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 */ @@ -418,6 +436,7 @@ done: nvmet_ctrl_fatal_error(ctrl); complete: + mutex_unlock(&req->sq->auth_lock); nvmet_req_complete(req, status); } @@ -536,6 +555,8 @@ void nvmet_execute_auth_receive(struct n 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 = @@ -561,8 +582,10 @@ void nvmet_execute_auth_receive(struct n 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; } @@ -621,5 +644,6 @@ done: nvmet_auth_sq_free(req->sq); nvmet_ctrl_fatal_error(ctrl); } + mutex_unlock(&req->sq->auth_lock); nvmet_req_complete(req, status); } --- a/drivers/nvme/target/nvmet.h +++ b/drivers/nvme/target/nvmet.h @@ -161,8 +161,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 sc_c; u8 dhchap_status;