From: Shoichiro Miyamoto <shoichiro.miyamoto@gmail.com> stable inclusion from stable-v6.6.145 commit 36bfa52459e45c0d5b668de2f1c91f6dc5c67775 category: bugfix bugzilla: https://atomgit.com/src-openeuler/kernel/issues/16739 CVE: CVE-2026-64448 Reference: https://web.git.kernel.org/pub/scm/linux/kernel/git/stable/linux.git/commit/... -------------------------------- commit 8986c932905ea508d66da421eb2eb6e676ace1fe upstream. Commit 53b7c271f06b ("smb: client: restrict implied bcc[0] exemption to responses without data area") restricted the implied bcc[0] length exception to responses without a data area. However, the overlap handling in __smb2_calc_size() clears data_length, which can make an invalid response appear to have no data area and so qualify for the exception. Track data area overlap separately and reject such responses before applying the length compatibility exceptions. Fixes: 53b7c271f06b ("smb: client: restrict implied bcc[0] exemption to responses without data area") Cc: stable@vger.kernel.org Signed-off-by: Shoichiro Miyamoto <shoichiro.miyamoto@gmail.com> Signed-off-by: Steve French <stfrench@microsoft.com> Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org> Signed-off-by: Zizhi Wo <wozizhi@huawei.com> --- fs/smb/client/smb2misc.c | 34 +++++++++++++++++++++++++--------- 1 file changed, 25 insertions(+), 9 deletions(-) diff --git a/fs/smb/client/smb2misc.c b/fs/smb/client/smb2misc.c index b34ea4390ef3..6fa23cf7a529 100644 --- a/fs/smb/client/smb2misc.c +++ b/fs/smb/client/smb2misc.c @@ -16,11 +16,12 @@ #include "smb2status.h" #include "smb2glob.h" #include "nterr.h" #include "cached_dir.h" -static unsigned int __smb2_calc_size(void *buf, bool *have_data); +static unsigned int __smb2_calc_size(void *buf, bool *have_data, + bool *data_area_overlap); static int check_smb2_hdr(struct smb2_hdr *shdr, __u64 mid) { __u64 wire_mid = le64_to_cpu(shdr->MessageId); @@ -144,10 +145,11 @@ smb2_check_message(char *buf, unsigned int len, struct TCP_Server_Info *server) int pdu_size = sizeof(struct smb2_pdu); int command; __u32 calc_len; /* calculated length */ __u64 mid; bool have_data; + bool data_area_overlap; /* If server is a channel, select the primary channel */ pserver = SERVER_IS_CHAN(server) ? server->primary_server : server; /* @@ -228,11 +230,16 @@ smb2_check_message(char *buf, unsigned int len, struct TCP_Server_Info *server) return 1; } } have_data = false; - calc_len = __smb2_calc_size(buf, &have_data); + data_area_overlap = false; + calc_len = __smb2_calc_size(buf, &have_data, &data_area_overlap); + + /* Reject responses whose data area overlaps the fixed area. */ + if (data_area_overlap) + return 1; /* For SMB2_IOCTL, OutputOffset and OutputLength are optional, so might * be 0, and not a real miscalculation */ if (command == SMB2_IOCTL_HE && calc_len == 0) return 0; @@ -412,26 +419,32 @@ smb2_get_data_area_len(int *off, int *len, struct smb2_hdr *shdr) return (char *)shdr + *off; return NULL; } /* - * Calculate the size of the SMB message based on the fixed header - * portion, the number of word parameters and the data portion of the message. - * If have_data is non-NULL, it is set to true when a non-empty data area was - * found (data_length > 0), allowing callers to distinguish the implied bcc[0] - * case (no data area) from an overreported data length. + * Calculate the size of the SMB message based on the fixed header, fixed + * parameter area, and variable data area. + * + * If have_data is not NULL, it is set when a non-empty data area is found. + * If data_area_overlap is not NULL, it is set when the data area overlaps + * the fixed area. */ static unsigned int -__smb2_calc_size(void *buf, bool *have_data) +__smb2_calc_size(void *buf, bool *have_data, bool *data_area_overlap) { struct smb2_pdu *pdu = buf; struct smb2_hdr *shdr = &pdu->hdr; int offset; /* the offset from the beginning of SMB to data area */ int data_length = 0; /* the length of the variable length data area */ /* Structure Size has already been checked to make sure it is 64 */ int len = le16_to_cpu(shdr->StructureSize); + if (have_data) + *have_data = false; + if (data_area_overlap) + *data_area_overlap = false; + /* * StructureSize2, ie length of fixed parameter area has already * been checked to make sure it is the correct length. */ len += le16_to_cpu(pdu->StructureSize2); @@ -450,11 +463,14 @@ __smb2_calc_size(void *buf, bool *have_data) * so we must add one to the calculation. */ if (offset + 1 < len) { cifs_dbg(VFS, "data area offset %d overlaps SMB2 header %d\n", offset + 1, len); + if (data_area_overlap) + *data_area_overlap = true; data_length = 0; + goto calc_size_exit; } else { len = offset + data_length; } } calc_size_exit: @@ -465,11 +481,11 @@ __smb2_calc_size(void *buf, bool *have_data) } unsigned int smb2_calc_size(void *buf) { - return __smb2_calc_size(buf, NULL); + return __smb2_calc_size(buf, NULL, NULL); } /* Note: caller must free return buffer */ __le16 * cifs_convert_path_to_utf16(const char *from, struct cifs_sb_info *cifs_sb) -- 2.52.0