[PATCH 1/2] nvme-auth: use the parsed offset for the DH-CHAP key

shao.mingyin at zte.com.cn shao.mingyin at zte.com.cn
Mon Nov 9 06:59:19 PST 2026


From: Shao Mingyin <shao.mingyin at zte.com.cn>

nvme_auth_parse_key() accepts the DH-HMAC-CHAP key with

	if (sscanf(secret, "DHHC-1:%hhd:%*s:", &key_hash) != 1)
		return -EINVAL;

and then skips a hardcoded ten characters:

	key = nvme_auth_extract_key(secret + 10, key_hash);

The ten characters assume that the hash identifier is exactly two
characters long.  A string such as "DHHC-1:1:" still makes sscanf()
return 1, and secret + 10 then points one byte past the NUL terminator
of the buffer.  nvme_auth_extract_key() starts with
strlen(secret), so it reads past the end of the allocation.  The sysfs
dhchap_secret and dhchap_ctrl_secret stores only require seven
characters and a "DHHC-1:" prefix, so a short value reaches this code
(even though the nvme-fabrics option parser rejects secrets shorter than
eleven characters).

Use the %n conversion to obtain the real offset after the
"DHHC-1:<hash>:" prefix and reject a string where the prefix is not
complete.  This accepts both one and two character hash identifiers and
never starts the key past the terminator.

Fixes: f50fff73d620 ("nvme: implement In-Band authentication")
Cc: stable at vger.kernel.org
Signed-off-by: Shao Mingyin <shao.mingyin at zte.com.cn>
---
Tested on 7.3.0-next-20261030 (QEMU/TCG guest, KASAN) with a live loop
controller and host DH-CHAP.

Writing the 9-byte secret "DHHC-1:1:" to the dhchap_secret sysfs
attribute makes the driver start the key ten bytes in, one byte past
the NUL terminator:

  unpatched                            patched
  KASAN slab-out-of-bounds in          write rejected (-EINVAL)
  strlen/strrchr, nvme_auth_extract_key

diff --git a/drivers/nvme/common/auth.c b/drivers/nvme/common/auth.c
index e2e0c736540a..1fdc4e4ed0b9 100644
--- a/drivers/nvme/common/auth.c
+++ b/drivers/nvme/common/auth.c
@@ -489,17 +489,19 @@ int nvme_auth_parse_key(const char *secret, struct nvme_dhchap_key **ret_key)
 {
 	struct nvme_dhchap_key *key;
 	u8 key_hash;
+	int offset = 0;
 
 	if (!secret) {
 		*ret_key = NULL;
 		return 0;
 	}
 
-	if (sscanf(secret, "DHHC-1:%hhd:%*s:", &key_hash) != 1)
+	if (sscanf(secret, "DHHC-1:%hhd:%n", &key_hash, &offset) != 1 ||
+	    !offset)
 		return -EINVAL;
 
 	/* Pass in the secret without the 'DHHC-1:XX:' prefix */
-	key = nvme_auth_extract_key(secret + 10, key_hash);
+	key = nvme_auth_extract_key(secret + offset, key_hash);
 	if (IS_ERR(key)) {
 		*ret_key = NULL;
 		return PTR_ERR(key);



More information about the Linux-nvme mailing list