]> git.ipfire.org Git - thirdparty/linux.git/commitdiff
smb: client: use data_len for SMB2 READ encrypted folioq copy
authorJeremy Erazo <mendozayt13@gmail.com>
Fri, 15 May 2026 19:31:41 +0000 (19:31 +0000)
committerSteve French <stfrench@microsoft.com>
Tue, 19 May 2026 16:17:07 +0000 (11:17 -0500)
In handle_read_data() the encrypted/folioq branch
(buf_len <= data_offset, reached via receive_encrypted_read for
transform PDUs > CIFSMaxBufSize + MAX_HEADER_SIZE) copies the READ
payload using buffer_len rather than data_len:

rdata->result = cifs_copy_folioq_to_iter(buffer, buffer_len,
 cur_off,
 &rdata->subreq.io_iter);
...
rdata->got_bytes = buffer_len;

buffer_len comes from the SMB3 transform header OriginalMessageSize
field (OriginalMessageSize - read_rsp_size); it represents the size
of the decrypted message after the SMB2 header.  data_len comes from
the SMB2 READ response DataLength field; it represents the actual
READ payload size and may be smaller than buffer_len when the
decrypted message contains padding or other trailing bytes after the
READ payload.  The existing check `data_len > buffer_len - pad_len`
only enforces an upper bound, so a server that emits
OriginalMessageSize larger than read_rsp_size + pad_len + data_len
passes the check and the kernel copies buffer_len bytes per response,
ignoring the server-asserted DataLength.

Two observable failures with a crafted server (DataLength=4,
buffer_len=20000):

  - the kernel returns 20000 bytes per sub-request to userspace and
    sets got_bytes = buffer_len, even though the response claimed
    only 4 bytes of payload;

  - on a partial netfs sub-request whose iterator is sized to
    data_len, the over-large copy_folio_to_iter() short-reads,
    cifs_copy_folioq_to_iter() returns -EIO via the n != len path,
    and the entire netfs read collapses to -EIO even though the
    leading sub-requests succeeded.

Use data_len for the copy length and for got_bytes so the kernel
honours the server-asserted READ payload size.  For well-formed
servers (where buffer_len == pad_len + data_len) the change is
behaviour-equivalent.

Cc: stable@vger.kernel.org
Signed-off-by: Jeremy Erazo <mendozayt13@gmail.com>
Acked-by: David Howells <dhowells@redhat.com>
Signed-off-by: Steve French <stfrench@microsoft.com>
fs/smb/client/smb2ops.c

index 3738204984f5a05f2798f22962c40aa0c2450259..ee83700269cfd62aadf29df8a5a161f0a7856c6f 100644 (file)
@@ -4826,7 +4826,7 @@ handle_read_data(struct TCP_Server_Info *server, struct mid_q_entry *mid,
                }
 
                /* Copy the data to the output I/O iterator. */
-               rdata->result = cifs_copy_folioq_to_iter(buffer, buffer_len,
+               rdata->result = cifs_copy_folioq_to_iter(buffer, data_len,
                                                         cur_off, &rdata->subreq.io_iter);
                if (rdata->result != 0) {
                        if (is_offloaded)
@@ -4835,7 +4835,7 @@ handle_read_data(struct TCP_Server_Info *server, struct mid_q_entry *mid,
                                dequeue_mid(server, mid, rdata->result);
                        return 0;
                }
-               rdata->got_bytes = buffer_len;
+               rdata->got_bytes = data_len;
 
        } else if (!check_add_overflow(data_offset, data_len, &end_off) &&
                   buf_len >= end_off) {