[PATCH] nvmet-auth: fix uninitialized-memory leak in AUTH_RECEIVE

Ibrahim Hashimov <[email protected]>
Newsgroups org.infradead.lists.linux-nvme,org.kernel.vger.linux-kernel,org.kernel.vger.stable
Message-ID <[email protected]>
nvmet_execute_auth_receive() allocates its response buffer with the
attacker-controlled AUTH_RECEIVE `al` (data length) field:

    al = nvmet_auth_receive_data_len(req);
    ...
    d = kmalloc(al, GFP_KERNEL);
    ...
    switch (req->sq->dhchap_step) {
    case NVME_AUTH_DHCHAP_MESSAGE_CHALLENGE:
            nvmet_auth_challenge(req, d, al);
    ...
    status = nvmet_copy_to_sgl(req, 0, d, al);

nvmet_check_transfer_len() only enforces al == req->transfer_len, a
pure equality check with no upper bound tied to the message actually
being produced. nvmet_auth_challenge() computes its own data_size
(sizeof(challenge header) + hash_len, 48 bytes for SHA-256 with no DH
group) and only checks "al < data_size" as a floor -- it memset()s and
fills exactly data_size bytes of the buffer. The same pattern applies
to nvmet_auth_success1()/nvmet_auth_failure1(), which only memset()
sizeof(*data) bytes of `d`.

Whichever handler runs, nvmet_execute_auth_receive() then streams the
entire al-byte buffer back to the remote host with
nvmet_copy_to_sgl(req, 0, d, al). Since a remote NVMe-oF/TCP host
chooses `al` and can set it far larger than data_size (e.g. 4096 vs.
48), the tail [data_size, al) of the kmalloc()'d buffer is returned to
the peer without ever having been written by the kernel -- a remote,
pre-authentication disclosure of uninitialized kernel heap contents,
confirmed with KASAN by planting a marker byte in
a freed kmalloc-4096 object and observing it echoed back in the tail
of the CHALLENGE response. This is reachable as soon as
CONFIG_NVME_TARGET_AUTH is enabled (the default on Fedora/RHEL/Ubuntu
distro kernels) and a subsystem uses/allows the DH-CHAP auth path; no
secret needs to be proven first, since the leak occurs on the
CHALLENGE step of the handshake.

Fix it the same way nvmet_execute_disc_get_log_page() already handles
an analogous host-controlled-length response buffer
(drivers/nvme/target/discovery.c: "buffer = kzalloc(alloc_len,
GFP_KERNEL);"): zero the allocation up front instead of only zeroing
the sub-range each per-step helper happens to fill. This keeps the fix
to a single line, covers all three dhchap_step handlers that share
this buffer and the common copy-back site (CHALLENGE, SUCCESS1,
FAILURE1), and does not change the wire format, the transfer-length
checks, or any success/error paths.

Testing: the uninitialized-tail disclosure itself was confirmed live
under KASAN before this fix, by planting a marker byte in a freed
kmalloc-4096 object and observing it (or other live heap bytes)
echoed back in the CHALLENGE response tail; a CONTROL request with
al == data_size returned no such tail on every run, and an EXPLOIT
request with al = 4096 leaked up to 4048 bytes of stale heap on two
separate runs. The fix itself is verified by code inspection rather
than a fresh KASAN run: kzalloc() zeroes the whole al-byte allocation
before any dhchap_step handler executes, so the tail past whatever
bytes a handler's own memset()/fill writes is guaranteed zero instead
of leftover slab content, for all three handlers and the single
shared nvmet_copy_to_sgl() copy-back site. Re-running the KASAN
harness against a kernel built with this patch to reconfirm a
zero tail is currently blocked by an nvme-tcp loopback livelock in
the test stand; a controlled A/B check showed the same livelock on
the unpatched baseline as well, so it is a pre-existing harness
limitation, not something introduced by this one-line kzalloc()
change.

Fixes: db1312dd9548 ("nvmet: implement basic In-Band Authentication")
Cc: [email protected]
Signed-off-by: Ibrahim Hashimov <[email protected]>
Assisted-by: AuditCode-AI:2026.07
---
 drivers/nvme/target/fabrics-cmd-auth.c | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/drivers/nvme/target/fabrics-cmd-auth.c b/drivers/nvme/target/fabrics-cmd-auth.c
index 45820a12750d..2b617d3b8bba 100644
--- a/drivers/nvme/target/fabrics-cmd-auth.c
+++ b/drivers/nvme/target/fabrics-cmd-auth.c
@@ -557,7 +557,7 @@ void nvmet_execute_auth_receive(struct nvmet_req *req)
 		return;
 	}
 
-	d = kmalloc(al, GFP_KERNEL);
+	d = kzalloc(al, GFP_KERNEL);
 	if (!d) {
 		status = NVME_SC_INTERNAL;
 		goto done;
-- 
2.50.1 (Apple Git-155)
lmpx.com only provides a reader for public news (NNTP) servers. It is not affiliated with the servers or forums shown here and is not responsible for the content of articles, which is written by their respective authors.