[PATCH] ksmbd: initialise the reserved fields of six SMB2 responses
From: Aamir Ahmed
Date: Sun Sep 06 2026 - 19:43:54 EST
smb2_allocate_rsp_buf() zeroes the response buffer, so a standalone
request never leaks. A compound request can: the offset of the next
response is advanced by the length pinned for the previous one, so a
reply that was written into the buffer and then dropped in favour of the
short error response of smb2_set_err_rsp() stays there, and the next
reply is laid over it clearing only
memset(rsp_hdr, 0, sizeof(struct smb2_hdr) + 2);
which covers the header and StructureSize and stops before Reserved.
Six responses declare their full size but leave a field unwritten, so
those bytes reach the client as they are found in the buffer:
- get_file_standard_info() and get_standard_info_pipe() never set
smb2_file_standard_info.Reserved, 2 bytes at offset 22 of the 24
they report.
- find_file_posix_info() never sets smb311_posix_qinfo.Zero or
.ReparseTag, 4 bytes each at offsets 64 and 72 of the 112 it
reports.
- smb2_session_logoff() and smb2_tree_disconnect() never set Reserved
in their 4-byte bodies, which are pinned with a bare sizeof().
- the FSCTL_GET_REPARSE_POINT case of smb2_ioctl() sets ReparseTag and
ReparseDataLength but not reparse_data_buffer.Reserved, 2 bytes at
offset 6 of the 8 it reports.
The logoff and tree disconnect cases are oversights rather than
convention: five responses share the layout { hdr; __le16 StructureSize;
__le16 Reserved; }, and smb2_echo(), smb2_flush() and smb2_lock() all
zero Reserved explicitly while those two do not.
This is the same class as the FS_OBJECT_ID_INFORMATION and
FS_CONTROL_INFORMATION leaks fixed in the series merged on 2026-08-31.
Fixes: e2f34481b24d ("cifsd: add server-side procedures for SMB3")
Cc: stable@xxxxxxxxxxxxxxx
Signed-off-by: Aamir Ahmed <elb12345@xxxxxxxxxxxxx>
---
Found with AI assistance (Claude Code) by extracting the class from the
four fixes merged 2026-08-31 and sweeping every response builder and
every ksmbd_iov_pin_rsp() site in fs/smb/server/ against the struct
definitions.
Observed on the wire. An SMB2 QUERY_INFO for FileStandardInformation,
against a ksmbd built with the 24-byte reply poisoned with 0xAA before
the handler fills it, returns to the client:
00100000 00000000 AllocationSize = 4096
0c000000 00000000 EndOfFile = 12
01000000 NumberOfLinks = 1
00 DeletePending = 0
00 Directory = 0
aaaa Reserved = untouched poison
The same probe confirms find_file_posix_info() leaves both Zero and
ReparseTag at 0xaaaaaaaa (driven by a cifs mount with vers=3.1.1,posix,
which sends SMB_FIND_FILE_POSIX_INFO inside a compound request), and
smb2_tree_disconnect() leaves Reserved at 0xaaaa.
Struct offsets verified by compiling the layouts:
smb2_file_standard_info size 24, Reserved at 22
smb311_posix_qinfo size 80, Zero at 64, ReparseTag at 72
reparse_data_buffer size 8, Reserved at 6
Two candidates the sweep raised and I discarded: get_file_stream_info()
computes its length as sizeof() + streamlen * 2 rather than a bare
sizeof, and smb2_file_all_info.__pad is a union member sharing storage
with the flexible FileName array, which is written. The
FSCTL_CREATE_OR_GET_OBJECT_ID reply looks similar but explicitly memsets
all four of its 16-byte IDs.
fs/smb/server/smb2pdu.c | 7 +++++++
1 file changed, 7 insertions(+)
diff --git a/fs/smb/server/smb2pdu.c b/fs/smb/server/smb2pdu.c
index d656832d82efe..c9786732f00c4 100644
--- a/fs/smb/server/smb2pdu.c
+++ b/fs/smb/server/smb2pdu.c
@@ -3052,6 +3052,7 @@ int smb2_tree_disconnect(struct ksmbd_work *work)
}
rsp->StructureSize = cpu_to_le16(4);
+ rsp->Reserved = 0;
err = ksmbd_iov_pin_rsp(work, rsp,
sizeof(struct smb2_tree_disconnect_rsp));
if (err) {
@@ -3140,6 +3141,7 @@ int smb2_session_logoff(struct ksmbd_work *work)
return err;
rsp->StructureSize = cpu_to_le16(4);
+ rsp->Reserved = 0;
err = ksmbd_iov_pin_rsp(work, rsp, sizeof(struct smb2_logoff_rsp));
if (err) {
rsp->hdr.Status = STATUS_INSUFFICIENT_RESOURCES;
@@ -6301,6 +6303,7 @@ static void get_standard_info_pipe(struct smb2_query_info_rsp *rsp,
sinfo->NumberOfLinks = cpu_to_le32(1);
sinfo->DeletePending = 1;
sinfo->Directory = 0;
+ sinfo->Reserved = 0;
rsp->OutputBufferLength =
cpu_to_le32(sizeof(struct smb2_file_standard_info));
}
@@ -6628,6 +6631,7 @@ static int get_file_standard_info(struct smb2_query_info_rsp *rsp,
sinfo->NumberOfLinks = cpu_to_le32(get_nlink(&stat) - delete_pending);
sinfo->DeletePending = delete_pending;
sinfo->Directory = S_ISDIR(stat.mode) ? 1 : 0;
+ sinfo->Reserved = 0;
rsp->OutputBufferLength =
cpu_to_le32(sizeof(struct smb2_file_standard_info));
@@ -7162,6 +7166,8 @@ static int find_file_posix_info(struct smb2_query_info_rsp *rsp,
}
file_info->DeviceId = cpu_to_le32(stat.rdev);
+ file_info->Zero = 0;
+ file_info->ReparseTag = 0;
/*
* Sids(32) contain two sids(Domain sid(16), UNIX group sid(16)).
@@ -11288,6 +11294,7 @@ int smb2_ioctl(struct ksmbd_work *work)
reparse_ptr->ReparseTag =
smb2_get_reparse_tag_special_file(file_inode(fp->filp)->i_mode);
reparse_ptr->ReparseDataLength = 0;
+ reparse_ptr->Reserved = 0;
ksmbd_fd_put(work, fp);
nbytes = sizeof(struct reparse_data_buffer);
break;