[RFC PATCH 19/36] cifs: Try to better handle the "Dynamic" flag in StructureSize2 in SMB2/3

From: David Howells

Date: Tue May 19 2026 - 06:32:41 EST


The bottom bit of the size field of the op-specific SMB2/3 struct that
immediately follows the smb[23]_hdr struct is a bit weird. Wireshark
considers it a "dynamic flag" and it would seem that it needs to be masked
off to get the actualy size of the struct - and if there is data following
the struct, it is kind of meaningless.

The adjusted value, however, must still be set that way, and if there is
nothing following the struct, it seems that the message must be padded out
with an extra zero byte so that the message size is no less than the header
including that flag in the StructureSize.

Possibly this is a remnant of someone putting an empty data field at the
end of the struct as "unsigned char Buffer[1];". But whatever the origin,
there are a bunch of places in which the extra 1 needs to be subtracted
off.

Since subtracting 1 is the rule rather than the exception, return the size
with the bit masked off and add the pad byte if we don't have anything else
to add (Read being the main example of this).

This can then be handled more transparently in future.

Signed-off-by: David Howells <dhowells@xxxxxxxxxx>
cc: Steve French <sfrench@xxxxxxxxx>
cc: Paulo Alcantara <pc@xxxxxxxxxxxxx>
cc: Shyam Prasad N <sprasad@xxxxxxxxxxxxx>
cc: Tom Talpey <tom@xxxxxxxxxx>
cc: linux-cifs@xxxxxxxxxxxxxxx
cc: linux-fsdevel@xxxxxxxxxxxxxxx
---
fs/smb/client/smb2pdu.c | 28 ++++++++++++++++------------
1 file changed, 16 insertions(+), 12 deletions(-)

diff --git a/fs/smb/client/smb2pdu.c b/fs/smb/client/smb2pdu.c
index ddf127dc27bb..48f56ce73cd3 100644
--- a/fs/smb/client/smb2pdu.c
+++ b/fs/smb/client/smb2pdu.c
@@ -543,7 +543,11 @@ fill_small_buf(__le16 smb2_command, struct cifs_tcon *tcon,
smb2_hdr_assemble(&spdu->hdr, smb2_command, tcon, server);
spdu->StructureSize2 = cpu_to_le16(parmsize);

- *total_len = parmsize + sizeof(struct smb2_hdr);
+ if (smb2_command == SMB2_READ)
+ *total_len = sizeof(struct smb2_hdr) + parmsize;
+ else
+ *total_len = sizeof(struct smb2_hdr) +
+ (parmsize & ~SMB2_STRUCT_HAS_DYNAMIC_PART);
}

/*
@@ -1589,7 +1593,7 @@ SMB2_sess_alloc_buffer(struct SMB2_sess_data *sess_data)

sess_data->iov[0].iov_base = (char *)req;
/* 1 for pad */
- sess_data->iov[0].iov_len = total_len - 1;
+ sess_data->iov[0].iov_len = total_len;
/*
* This variable will be used to clear the buffer
* allocated above in case of any error in the calling function.
@@ -2154,7 +2158,7 @@ SMB2_tcon(const unsigned int xid, struct cifs_ses *ses, const char *tree,

iov[0].iov_base = (char *)req;
/* 1 for pad */
- iov[0].iov_len = total_len - 1;
+ iov[0].iov_len = total_len;

/* Testing shows that buffer offset must be at location of Buffer[0] */
req->PathOffset = cpu_to_le16(sizeof(struct smb2_tree_connect_req));
@@ -2958,7 +2962,7 @@ int smb311_posix_mkdir(const unsigned int xid, struct inode *inode,

iov[0].iov_base = (char *)req;
/* -1 since last byte is buf[0] which is sent below (path) */
- iov[0].iov_len = total_len - 1;
+ iov[0].iov_len = total_len;

req->NameOffset = cpu_to_le16(sizeof(struct smb2_create_req));

@@ -3105,7 +3109,7 @@ SMB2_open_init(struct cifs_tcon *tcon, struct TCP_Server_Info *server,

iov[0].iov_base = (char *)req;
/* -1 since last byte is buf[0] which is sent below (path) */
- iov[0].iov_len = total_len - 1;
+ iov[0].iov_len = total_len;

if (oparms->create_options & CREATE_OPTION_READONLY)
file_attributes |= ATTR_READONLY;
@@ -3427,12 +3431,12 @@ SMB2_ioctl_init(struct cifs_tcon *tcon, struct TCP_Server_Info *server,
req->InputOffset =
cpu_to_le32(offsetof(struct smb2_ioctl_req, Buffer));
rqst->rq_nvec = 2;
- iov[0].iov_len = total_len - 1;
+ iov[0].iov_len = total_len;
iov[1].iov_base = in_data_buf;
iov[1].iov_len = indatalen;
} else {
rqst->rq_nvec = 1;
- iov[0].iov_len = total_len;
+ iov[0].iov_len = total_len + 1;
}

req->OutputOffset = 0;
@@ -3874,7 +3878,7 @@ SMB2_query_info_init(struct cifs_tcon *tcon, struct TCP_Server_Info *server,
if (input_len) {
req->InputBufferLength = cpu_to_le32(input_len);
/* total_len for smb query request never close to le16 max */
- req->InputBufferOffset = cpu_to_le16(total_len - 1);
+ req->InputBufferOffset = cpu_to_le16(total_len);
memcpy(req->Buffer, input, input_len);
}

@@ -4579,7 +4583,7 @@ smb2_new_read_req(void **buf, unsigned int *total_len,
v1 = (struct smbdirect_buffer_descriptor_v1 *) &req->Buffer[0];
smbd_mr_fill_buffer_descriptor(rdata->mr, v1);

- *total_len += sizeof(*v1) - 1;
+ *total_len += sizeof(*v1);
}
#endif
if (request_type & CHAINED_REQUEST) {
@@ -5339,7 +5343,7 @@ SMB2_write(const unsigned int xid, struct cifs_io_parms *io_parms,

iov[0].iov_base = (char *)req;
/* 1 for Buffer */
- iov[0].iov_len = total_len - 1;
+ iov[0].iov_len = total_len;

memset(&rqst, 0, sizeof(struct smb_rqst));
rqst.rq_iov = iov;
@@ -5597,7 +5601,7 @@ int SMB2_query_directory_init(const unsigned int xid,

iov[0].iov_base = (char *)req;
/* 1 for Buffer */
- iov[0].iov_len = total_len - 1;
+ iov[0].iov_len = total_len;

iov[1].iov_base = (char *)(req->Buffer);
iov[1].iov_len = len;
@@ -5810,7 +5814,7 @@ SMB2_set_info_init(struct cifs_tcon *tcon, struct TCP_Server_Info *server,

iov[0].iov_base = (char *)req;
/* 1 for Buffer */
- iov[0].iov_len = total_len - 1;
+ iov[0].iov_len = total_len;

for (i = 1; i < rqst->rq_nvec; i++) {
le32_add_cpu(&req->BufferLength, size[i]);