nvmet: Free the old negotiation state when restarting authentication

Restarting a DH-HMAC-CHAP negotiation with SEND(NEGOTIATE) resets
sq->dhchap_step but does not release the previous c1/c2/skey, which
are only freed in nvmet_auth_sq_free() at teardown.  The next
CHALLENGE/REPLY exchange then overwrites those pointers with fresh
allocations.

An unauthenticated host can repeat the negotiate message endlessly,
leaking a few dozen bytes per round and slowly exhausting target
memory.

Clear the old negotiation state with nvmet_auth_sq_free() before
setting up the new one.  Also reset dhchap_skey_len to zero: if a DH
session key was already established, a following round with dhvlen=0
would otherwise pass a NULL skey with a stale non-zero length to the
HMAC code and dereference it.

Fixes: db1312dd95 ("nvmet: implement basic In-Band Authentication")
Cc: stable@vger.kernel.org
Reviewed-by: Christoph Hellwig <hch@lst.de>
Signed-off-by: Guixin Liu <kanie@linux.alibaba.com>
Signed-off-by: Keith Busch <kbusch@kernel.org>
This commit is contained in:
Guixin Liu 2026-10-28 16:55:51 +08:00 committed by Keith Busch
parent 18fc5f6f1c
commit 5c279fcabb
2 changed files with 2 additions and 0 deletions

View File

@ -237,6 +237,7 @@ void nvmet_auth_sq_free(struct nvmet_sq *sq)
sq->dhchap_c2 = NULL;
kfree(sq->dhchap_skey);
sq->dhchap_skey = NULL;
sq->dhchap_skey_len = 0;
}
void nvmet_auth_sq_destroy(struct nvmet_sq *sq)

View File

@ -330,6 +330,7 @@ void nvmet_execute_auth_send(struct nvmet_req *req)
/* Restart negotiation */
pr_debug("%s: ctrl %d qid %d reset negotiation\n",
__func__, ctrl->cntlid, req->sq->qid);
nvmet_auth_sq_free(req->sq);
if (!req->sq->qid) {
dhchap_status = nvmet_setup_auth(ctrl, req->sq,
true);