[PATCH v2] nvmet: auth: validate dhchap id list lengths
YunJe Shin
yjshin0438 at gmail.com
Thu Mar 12 22:24:09 PDT 2026
From: Yunje Shin <ioerts at kookmin.ac.kr>
The function nvmet_auth_negotiate() parses the idlist array in the
struct nvmf_auth_dhchap_protocol_descriptor payload. This array is 60
bytes and is logically divided into two 30-byte halves: the first half
for HMAC IDs and the second half for DH group IDs. The current code
uses a hardcoded +30 offset for the DH list, but does not validate
halen and dhlen against the per-half bounds. As a result, if a
malicious host sends halen or dhlen larger than 30, the loop can
read past the 60-byte array into adjacent slab memory, triggering a
KASAN slab-out-of-bounds read.
KASAN splat:
[ 4.241646] BUG: KASAN: slab-out-of-bounds in nvmet_execute_auth_send+0x19b8/0x2090
[ 4.242874] Read of size 1 at addr ffff8881045754e8 by task kworker/1:1H/41
[ 4.265342] The buggy address belongs to the cache kmalloc-96 of size 96
[ 4.266291] allocated 72-byte region [ffff8881045754a0, ffff8881045754e8)
[ 4.270337] page dumped because: kasan: bad access detected
This patch fixes the issue by introducing NVME_AUTH_DHCHAP_MAX_HASH_IDS
and NVME_AUTH_DHCHAP_MAX_DH_IDS defined as 30, which explicitly indicates
the maximum boundaries allowed per NVMe specification. The lengths halen
and dhlen are validated against these boundaries before processing,
preventing the out-of-bounds reads.
Fixes: db1312dd95488 ("nvmet: implement basic In-Band Authentication")
Cc: stable at kernel.org
Signed-off-by: Yunje Shin <ioerts at kookmin.ac.kr>
Reviewed-by: Hannes Reinecke <hare at suse.de>
---
v2:
- Replaced the runtime 'sizeof' calculation (idlist_half) with explicit
NVME_AUTH_DHCHAP_MAX_HASH_IDS and NVME_AUTH_DHCHAP_MAX_DH_IDS macros
to clearly reflect the 30:30 split limit per Chris Leech's feedback.
drivers/nvme/target/fabrics-cmd-auth.c | 11 ++++++++++-
include/linux/nvme.h | 2 ++
2 files changed, 12 insertions(+), 1 deletion(-)
diff --git a/drivers/nvme/target/fabrics-cmd-auth.c b/drivers/nvme/target/fabrics-cmd-auth.c
index 5946681cb0e3..acba4878a873 100644
--- a/drivers/nvme/target/fabrics-cmd-auth.c
+++ b/drivers/nvme/target/fabrics-cmd-auth.c
@@ -72,6 +72,14 @@ static u8 nvmet_auth_negotiate(struct nvmet_req *req, void *d)
NVME_AUTH_DHCHAP_AUTH_ID)
return NVME_AUTH_DHCHAP_FAILURE_INCORRECT_PAYLOAD;
+ /*
+ * idlist[0..29]: hash IDs
+ * idlist[30..59]: DH group IDs
+ */
+ if (data->auth_protocol[0].dhchap.halen > NVME_AUTH_DHCHAP_MAX_HASH_IDS ||
+ data->auth_protocol[0].dhchap.dhlen > NVME_AUTH_DHCHAP_MAX_DH_IDS)
+ return NVME_AUTH_DHCHAP_FAILURE_INCORRECT_PAYLOAD;
+
for (i = 0; i < data->auth_protocol[0].dhchap.halen; i++) {
u8 host_hmac_id = data->auth_protocol[0].dhchap.idlist[i];
@@ -97,7 +105,8 @@ static u8 nvmet_auth_negotiate(struct nvmet_req *req, void *d)
dhgid = -1;
fallback_dhgid = -1;
for (i = 0; i < data->auth_protocol[0].dhchap.dhlen; i++) {
- int tmp_dhgid = data->auth_protocol[0].dhchap.idlist[i + 30];
+ int tmp_dhgid =
+ data->auth_protocol[0].dhchap.idlist[i + NVME_AUTH_DHCHAP_MAX_HASH_IDS];
if (tmp_dhgid != ctrl->dh_gid) {
dhgid = tmp_dhgid;
diff --git a/include/linux/nvme.h b/include/linux/nvme.h
index b09dcaf5bcbc..ea0393ab16fc 100644
--- a/include/linux/nvme.h
+++ b/include/linux/nvme.h
@@ -1824,6 +1824,8 @@ struct nvmf_auth_dhchap_protocol_descriptor {
__u8 dhlen;
__u8 idlist[60];
};
+#define NVME_AUTH_DHCHAP_MAX_HASH_IDS 30
+#define NVME_AUTH_DHCHAP_MAX_DH_IDS 30
enum {
NVME_AUTH_DHCHAP_AUTH_ID = 0x01,
--
2.43.0
More information about the Linux-nvme
mailing list