[PATCH v2] nvmet-auth: validate negotiate payload length(BUG: KASAN: slab-out-of-bounds)
From: YunJe Shin
Date: Wed Feb 11 2026 - 20:35:05 EST
From: Yunje Shin <ioerts@xxxxxxxxxxxxx>
AUTH_SEND negotiation requires at least one DH-HMAC-CHAP protocol descriptor.
Validate the payload length before parsing the negotiate payload to avoid
out-of-bounds reads.
KASAN splat:
[ 1224.388857] BUG: KASAN: slab-out-of-bounds in nvmet_execute_auth_send+0x1d24/0x2090
[ 1224.407035] The buggy address belongs to the cache kmalloc-8 of size 8
[ 1224.407998] allocated 8-byte region [ffff88800a6537c0, ffff88800a6537c8)
[ 1224.412412] page dumped because: kasan: bad access detected
Use struct_size() for minimum length computation and move the negotiate
restart flow into a helper so the call site stays compact.
Fixes: db1312dd95488 ("nvmet: implement basic In-Band Authentication")
Signed-off-by: Yunje Shin <ioerts@xxxxxxxxxxxxx>
---
v2:
- use struct_size() for negotiate payload minimum length
- split negotiate handling into nvmet_restart_dhchap_auth() helper
- use NVME_AUTH_DHCHAP_FAILURE_INCORRECT_PAYLOAD instead of NVMe status
drivers/nvme/target/fabrics-cmd-auth.c | 48 +++++++++++++++++---------
1 file changed, 32 insertions(+), 16 deletions(-)
diff --git a/drivers/nvme/target/fabrics-cmd-auth.c b/drivers/nvme/target/fabrics-cmd-auth.c
index 5946681cb0e3..3773980bcb1c 100644
--- a/drivers/nvme/target/fabrics-cmd-auth.c
+++ b/drivers/nvme/target/fabrics-cmd-auth.c
@@ -231,6 +231,36 @@ u32 nvmet_auth_send_data_len(struct nvmet_req *req)
return le32_to_cpu(req->cmd->auth_send.tl);
}
+static bool nvmet_restart_dhchap_auth(struct nvmet_req *req, void *d, u32 tl)
+{
+ struct nvmet_ctrl *ctrl = req->sq->ctrl;
+ struct nvmf_auth_dhchap_negotiate_data *neg = d;
+ u8 dhchap_status;
+ size_t min_len = struct_size(neg, auth_protocol, 1);
+
+ if (tl < min_len) {
+ req->sq->dhchap_status = NVME_AUTH_DHCHAP_FAILURE_INCORRECT_PAYLOAD;
+ req->sq->dhchap_step = NVME_AUTH_DHCHAP_MESSAGE_FAILURE1;
+ return false;
+ }
+
+ /* Restart negotiation */
+ pr_debug("%s: ctrl %d qid %d reset negotiation\n",
+ __func__, ctrl->cntlid, req->sq->qid);
+ if (!req->sq->qid) {
+ dhchap_status = nvmet_setup_auth(ctrl, req->sq);
+ if (dhchap_status) {
+ pr_err("ctrl %d qid 0 failed to setup re-authentication\n",
+ ctrl->cntlid);
+ req->sq->dhchap_status = dhchap_status;
+ req->sq->dhchap_step = NVME_AUTH_DHCHAP_MESSAGE_FAILURE1;
+ return false;
+ }
+ }
+ req->sq->dhchap_step = NVME_AUTH_DHCHAP_MESSAGE_NEGOTIATE;
+ return true;
+}
+
void nvmet_execute_auth_send(struct nvmet_req *req)
{
struct nvmet_ctrl *ctrl = req->sq->ctrl;
@@ -289,22 +319,8 @@ void nvmet_execute_auth_send(struct nvmet_req *req)
goto done_failure1;
if (data->auth_type == NVME_AUTH_COMMON_MESSAGES) {
if (data->auth_id == NVME_AUTH_DHCHAP_MESSAGE_NEGOTIATE) {
- /* Restart negotiation */
- pr_debug("%s: ctrl %d qid %d reset negotiation\n",
- __func__, ctrl->cntlid, req->sq->qid);
- if (!req->sq->qid) {
- dhchap_status = nvmet_setup_auth(ctrl, req->sq);
- if (dhchap_status) {
- pr_err("ctrl %d qid 0 failed to setup re-authentication\n",
- ctrl->cntlid);
- req->sq->dhchap_status = dhchap_status;
- req->sq->dhchap_step =
- NVME_AUTH_DHCHAP_MESSAGE_FAILURE1;
- goto done_kfree;
- }
- }
- req->sq->dhchap_step =
- NVME_AUTH_DHCHAP_MESSAGE_NEGOTIATE;
+ if (!nvmet_restart_dhchap_auth(req, d, tl))
+ goto done_kfree;
} else if (data->auth_id != req->sq->dhchap_step)
goto done_failure1;
/* Validate negotiation parameters */
--
2.43.0