--- /dev/null
+From 8986c932905ea508d66da421eb2eb6e676ace1fe Mon Sep 17 00:00:00 2001
+From: Shoichiro Miyamoto <shoichiro.miyamoto@gmail.com>
+Date: Sat, 11 Jul 2026 22:33:26 +0900
+Subject: smb: client: reject overlapping data areas in SMB2 responses
+
+From: Shoichiro Miyamoto <shoichiro.miyamoto@gmail.com>
+
+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>
+---
+ fs/smb/client/smb2misc.c | 34 +++++++++++++++++++++++++---------
+ 1 file changed, 25 insertions(+), 9 deletions(-)
+
+--- a/fs/smb/client/smb2misc.c
++++ b/fs/smb/client/smb2misc.c
+@@ -18,7 +18,8 @@
+ #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)
+@@ -146,6 +147,7 @@ smb2_check_message(char *buf, unsigned i
+ __u32 calc_len; /* calculated length */
+ __u64 mid;
+ bool have_data;
++ bool data_area_overlap;
+
+ /* If server is a channel, select the primary channel */
+ pserver = CIFS_SERVER_IS_CHAN(server) ? server->primary_server : server;
+@@ -230,7 +232,12 @@ smb2_check_message(char *buf, unsigned i
+ }
+
+ 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 */
+@@ -414,14 +421,15 @@ smb2_get_data_area_len(int *off, int *le
+ }
+
+ /*
+- * 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;
+@@ -430,6 +438,11 @@ __smb2_calc_size(void *buf, bool *have_d
+ /* 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.
+@@ -452,7 +465,10 @@ __smb2_calc_size(void *buf, bool *have_d
+ 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;
+ }
+@@ -467,7 +483,7 @@ calc_size_exit:
+ 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 */