* [PATCH] nvmet-auth: serialize access to DH-HMAC-CHAP state to fix UAF
@ 2026-10-03 21:01 Jérémy Jean
2026-10-13 14:33 ` Christoph Hellwig
2026-10-13 15:38 ` Keith Busch
0 siblings, 2 replies; 3+ messages in thread
From: Jérémy Jean @ 2026-10-03 21:01 UTC (permalink / raw)
To: Hannes Reinecke, Christoph Hellwig, Sagi Grimberg, Chaitanya Kulkarni
Cc: linux-nvme, linux-kernel, Jérémy Jean, stable
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
Signed-off-by: Jérémy Jean <Jeremy.Jean@oss.cyber.gouv.fr>
---
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 a55319b..1680e5e 100644
--- 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;
diff --git a/drivers/nvme/target/fabrics-cmd-auth.c b/drivers/nvme/target/fabrics-cmd-auth.c
index dccdb55..f30c253 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 <linux/blkdev.h>
+#include <linux/jiffies.h>
#include <linux/random.h>
#include <linux/nvme-auth.h>
#include <crypto/kpp.h>
@@ -17,16 +18,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;
}
@@ -256,6 +268,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 =
@@ -281,8 +295,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;
}
@@ -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 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 =
@@ -561,8 +582,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;
}
@@ -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);
}
diff --git a/drivers/nvme/target/nvmet.h b/drivers/nvme/target/nvmet.h
index 162e2fd..2032db3 100644
--- 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;
--
2.47.3
^ permalink raw reply [flat|nested] 3+ messages in thread
* Re: [PATCH] nvmet-auth: serialize access to DH-HMAC-CHAP state to fix UAF
2026-10-03 21:01 [PATCH] nvmet-auth: serialize access to DH-HMAC-CHAP state to fix UAF Jérémy Jean
@ 2026-10-13 14:33 ` Christoph Hellwig
2026-10-13 15:38 ` Keith Busch
1 sibling, 0 replies; 3+ messages in thread
From: Christoph Hellwig @ 2026-10-13 14:33 UTC (permalink / raw)
To: Jérémy Jean
Cc: Hannes Reinecke, Christoph Hellwig, Sagi Grimberg,
Chaitanya Kulkarni, linux-nvme, linux-kernel, stable
Looks good:
Reviewed-by: Christoph Hellwig <hch@lst.de>
^ permalink raw reply [flat|nested] 3+ messages in thread
* Re: [PATCH] nvmet-auth: serialize access to DH-HMAC-CHAP state to fix UAF
2026-10-03 21:01 [PATCH] nvmet-auth: serialize access to DH-HMAC-CHAP state to fix UAF Jérémy Jean
2026-10-13 14:33 ` Christoph Hellwig
@ 2026-10-13 15:38 ` Keith Busch
1 sibling, 0 replies; 3+ messages in thread
From: Keith Busch @ 2026-10-13 15:38 UTC (permalink / raw)
To: Jérémy Jean
Cc: Hannes Reinecke, Christoph Hellwig, Sagi Grimberg,
Chaitanya Kulkarni, linux-nvme, linux-kernel, stable
On Sat, Oct 03, 2026 at 09:01:55PM +0000, Jérémy Jean wrote:
> 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.
Thanks, applied to nvme-7.4.
^ permalink raw reply [flat|nested] 3+ messages in thread
end of thread, other threads:[~2026-10-13 15:38 UTC | newest]
Thread overview: 3+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2026-10-03 21:01 [PATCH] nvmet-auth: serialize access to DH-HMAC-CHAP state to fix UAF Jérémy Jean
2026-10-13 14:33 ` Christoph Hellwig
2026-10-13 15:38 ` Keith Busch
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox
all inboxes | Powered by JetHome®