Linux CIFS filesystem development
 help / color / mirror / Atom feed
* [PATCH 00/11] smb: client: fix OOB reads and UAFs in SMB2/3 receive paths
@ 2026-08-23 18:57 Frank Sorenson
  2026-08-23 18:57 ` [PATCH 01/11] smb: client: fix NextCommand bounds and aliasing UAF in receive_encrypted_standard() Frank Sorenson
                   ` (11 more replies)
  0 siblings, 12 replies; 16+ messages in thread
From: Frank Sorenson @ 2026-08-23 18:57 UTC (permalink / raw)
  To: linux-cifs; +Cc: pc, linkinjeon

This series fixes eleven bounds-checking defects in the SMB2/3 client,
all reachable from a malicious or compromised server.

Patches 1-3 address the compound encrypted frame processing path:

Patch 1 fixes four interacting bugs in receive_encrypted_standard():
a missing lower bound on NextCommand, an off-by-one upper bound that
admitted trailing slices too small for an SMB2 header (producing a
write-after-free via next_buffer aliasing server->bigbuf), a stale
next_buffer pointer not cleared before goto one_more, and use of the
pre-decryption pdu_length instead of the plaintext extent for bounds
checking.

Patch 2 adds smb2_min_pdu_len[], a per-command table of minimum response
struct sizes, and uses it in smb2_check_message() to reject responses
too short for smb2_get_data_area_len() to safely read command-specific
struct fields.

Patch 3 fixes server->total_read tracking in receive_encrypted_standard()
so that smb2_check_message() sees the actual per-sub-PDU size rather than
the full remaining compound tail.  Without this, a rogue server can craft
a compound frame where any non-last sub-PDU is shorter than its declared
fixed struct, bypassing the guards added in patch 2.

The remaining patches fix lower-bound gaps and OOB reads in DFS referral
parsing, EA list traversal, posix SID bounds, change-notify offset,
snapshot enumeration, and SMB1 reparse point validation.

Note on overlap with a concurrent series: Zihan Xi's
[PATCH v2 0/2] "smb: client: fix create context out-of-bounds reads"
(Message-ID: <cover.1787486936.git.zihanx@nebusec.ai>) touches
smb2_parse_contexts() and parse_posix_ctxt() independently.  Patch 11
here addresses the same function (smb2_parse_contexts()) but focuses on
complementary issues that their series does not cover: NameOffset
validation (lower and upper bounds) and gating all three handler
dispatches on a non-zero DataLength to prevent zero-DataLength contexts
from exercising parse_lease_buf, parse_query_id_ctxt, or parse_posix_ctxt.
Their per-context cc_len bounding and lease/QFid minimum-length checks
are not duplicated here.  parse_posix_ctxt() DataLength validation is
omitted from this series entirely since their patch 2/2 addresses it.

Frank Sorenson (11):
  smb: client: fix NextCommand bounds and aliasing UAF in
    receive_encrypted_standard()
  smb: client: validate PDU length before smb2_get_data_area_len()
    struct access
  smb: client: fix server->total_read not tracking sub-PDU size in
    receive_encrypted_standard()
  smb: client: fix missing lower-bound check on DFS referral string
    offsets
  smb: client: fix missing lower-bound on Next field in
    parse_server_interfaces()
  smb: client: fix OOB struct field reads in move_smb2_ea_to_cifs()
  smb: client: fix missing iov bounds check in parse_posix_sids()
  smb: client: fix underflow in is_valid_oplock_break() notify offset
    check
  smb: client: fix potential OOB read in smb3_enum_snapshots()
  smb: client: fix incomplete bounds check on reparse buffer in
    cifs_query_reparse_point()
  smb: client: fix NameOffset and Next field validation in
    smb2_parse_contexts()

 fs/smb/client/cifssmb.c   |  2 +-
 fs/smb/client/misc.c      | 12 +++++++--
 fs/smb/client/smb1misc.c  |  3 ++-
 fs/smb/client/smb2inode.c | 11 ++++++++
 fs/smb/client/smb2misc.c  | 53 +++++++++++++++++++++++++++++++++++++++
 fs/smb/client/smb2ops.c   | 45 +++++++++++++++++++++++----------
 fs/smb/client/smb2pdu.c   | 11 ++++----
 fs/smb/client/trace.h     |  1 +
 8 files changed, 116 insertions(+), 22 deletions(-)

-- 
2.55.0


^ permalink raw reply	[flat|nested] 16+ messages in thread

* [PATCH 01/11] smb: client: fix NextCommand bounds and aliasing UAF in receive_encrypted_standard()
  2026-08-23 18:57 [PATCH 00/11] smb: client: fix OOB reads and UAFs in SMB2/3 receive paths Frank Sorenson
@ 2026-08-23 18:57 ` Frank Sorenson
  2026-08-23 18:57 ` [PATCH 02/11] smb: client: validate PDU length before smb2_get_data_area_len() struct access Frank Sorenson
                   ` (10 subsequent siblings)
  11 siblings, 0 replies; 16+ messages in thread
From: Frank Sorenson @ 2026-08-23 18:57 UTC (permalink / raw)
  To: linux-cifs; +Cc: pc, linkinjeon, stable

Four related bugs in receive_encrypted_standard():

Lower bound: next_cmd is not checked against MID_HEADER_SIZE(server).  A
non-zero NextCommand smaller than the SMB2 header size passes the
upper-bound check; memcpy pulls bytes from within the current PDU header
into next_buffer, and the next iteration casts that region as a fresh
smb2_hdr.

Upper bound / trailing slice: the original check uses strict greater-than
(`next_cmd > pdu_length`), so next_cmd == pdu_length passes; memcpy copies
zero bytes, smb2_check_message() rejects the zeroed buffer, ret != 0, and
free_rsp_buf() releases next_buffer (aliasing server->bigbuf) while
allocate_buffers() still holds it: write-after-free.  Even with strict
less-than enforced, a next_cmd satisfying next_cmd < pdu_length but
pdu_length - next_cmd < MID_HEADER_SIZE(server) leaves a trailing slice too
small to hold an SMB2 header.  The next iteration reads shdr->NextCommand
from uninitialized slab content; if it reads as 0, the else-if (ret != 0)
branch frees next_buffer, again aliasing server->bigbuf: another
write-after-free.  Replace with three unsigned comparisons that avoid
addition: `next_cmd > pdu_length` to guard against underflow, then
`pdu_length - next_cmd < MID_HEADER_SIZE(server)` for the trailing slice
minimum.  The addition form (`(size_t)next_cmd +
MID_HEADER_SIZE(server) > pdu_length`) wraps to zero on 32-bit
kernels for next_cmd near UINT_MAX, silently admitting out-of-bounds.

Aliasing after goto: once server->bigbuf = buf = next_buffer and the goto
fires, next_buffer still holds the live server->bigbuf pointer.  If
smb2_check_message() fails on that iteration, ret != 0 and the else-if path
frees next_buffer, freeing server->bigbuf while it remains referenced.
Set next_buffer = NULL before goto one_more to prevent the aliased pointer
from being freed as a standalone buffer.

Decrypted extent: decrypt_raw_data() moves plaintext to buf[0..buf_size-1]
via memmove, where buf_size = pdu_length -
sizeof(struct smb2_transform_hdr).  Using pdu_length as the upper bound
allows next_cmd values in (buf_size, pdu_length], letting buf + next_cmd
land in the stale 52-byte transform-header residue and memcpy to copy
stale ciphertext into next_buffer as if it were the next SMB2 frame.
Assign pdu_length = buf_size after successful decryption so bounds checks
and the per-iteration decrement operate on the correct plaintext extent.

smb2_next_header() already enforces the equivalent lower-bound:

    if (unlikely(*noff && *noff < MID_HEADER_SIZE(server)))
        return -EINVAL;

Apply the same constraint here.

Fixes: b24df3e30cbf ("cifs: update receive_encrypted_standard to handle compounded responses")
Cc: stable@vger.kernel.org
Signed-off-by: Frank Sorenson <sorenson@redhat.com>
---
 fs/smb/client/smb2ops.c | 11 ++++++++++-
 1 file changed, 10 insertions(+), 1 deletion(-)

diff --git a/fs/smb/client/smb2ops.c b/fs/smb/client/smb2ops.c
index 7d6738ffcb80..e75420dbe950 100644
--- a/fs/smb/client/smb2ops.c
+++ b/fs/smb/client/smb2ops.c
@@ -5242,6 +5242,7 @@ receive_encrypted_standard(struct TCP_Server_Info *server,
 	length = decrypt_raw_data(server, buf, buf_size, NULL, false);
 	if (length)
 		return length;
+	pdu_length = buf_size;
 
 	next_is_large = server->large_buf;
 one_more:
@@ -5254,8 +5255,15 @@ receive_encrypted_standard(struct TCP_Server_Info *server,
 	}
 
 	if (next_cmd) {
-		if (WARN_ON_ONCE(next_cmd > pdu_length))
+		if (next_cmd < MID_HEADER_SIZE(server) ||
+		    next_cmd > pdu_length ||
+		    pdu_length - next_cmd < MID_HEADER_SIZE(server)) {
+			unsigned int max_next = pdu_length > (unsigned int)MID_HEADER_SIZE(server) ?
+					pdu_length - (unsigned int)MID_HEADER_SIZE(server) : 0;
+			cifs_server_dbg(VFS, "invalid NextCommand offset %u out of range [%zu, %u]\n",
+					next_cmd, MID_HEADER_SIZE(server), max_next);
 			return -1;
+		}
 		if (next_is_large)
 			next_buffer = (char *)cifs_buf_get();
 		else
@@ -5291,6 +5299,7 @@ receive_encrypted_standard(struct TCP_Server_Info *server,
 			server->bigbuf = buf = next_buffer;
 		else
 			server->smallbuf = buf = next_buffer;
+		next_buffer = NULL;
 		goto one_more;
 	} else if (ret != 0) {
 		/*
-- 
2.55.0


^ permalink raw reply related	[flat|nested] 16+ messages in thread

* [PATCH 02/11] smb: client: validate PDU length before smb2_get_data_area_len() struct access
  2026-08-23 18:57 [PATCH 00/11] smb: client: fix OOB reads and UAFs in SMB2/3 receive paths Frank Sorenson
  2026-08-23 18:57 ` [PATCH 01/11] smb: client: fix NextCommand bounds and aliasing UAF in receive_encrypted_standard() Frank Sorenson
@ 2026-08-23 18:57 ` Frank Sorenson
  2026-08-23 18:57 ` [PATCH 03/11] smb: client: fix server->total_read not tracking sub-PDU size in receive_encrypted_standard() Frank Sorenson
                   ` (9 subsequent siblings)
  11 siblings, 0 replies; 16+ messages in thread
From: Frank Sorenson @ 2026-08-23 18:57 UTC (permalink / raw)
  To: linux-cifs; +Cc: pc, linkinjeon

smb2_check_message() validates StructureSize2 but not that the received
PDU is large enough to hold the fixed response struct before calling
__smb2_calc_size() -> smb2_get_data_area_len().

smb2_get_data_area_len() reads command-specific struct fields (e.g.,
CreateContextsOffset at offset 144 in smb2_create_rsp) to locate the
data area before the length is validated.  A rogue server can send a
truncated response that passes the StructureSize2 check but causes
smb2_get_data_area_len() to read stale kmalloc'd content.

Add smb2_min_pdu_len[], parallel to smb2_rsp_struct_sizes[], holding
the minimum PDU size (sizeof the fixed response struct) for each command
with a data area.  Reject responses shorter than this minimum before
calling __smb2_calc_size().

The guard condition is the complement of smb2_get_data_area_len()'s
early-return predicate: fires when Status == 0, Status ==
STATUS_MORE_PROCESSING_REQUIRED, or StructureSize2 !=
SMB2_ERROR_STRUCTURE_SIZE2_LE.

SESSION_SETUP with STATUS_MORE_PROCESSING_REQUIRED is covered by the
second term; READ and IOCTL with STATUS_BUFFER_OVERFLOW by the third —
their StructureSize2 (17 and 49 respectively) differs from the error
struct size 9, so smb2_get_data_area_len() reaches the switch without
the guard.

Signed-off-by: Frank Sorenson <sorenson@redhat.com>
---
 fs/smb/client/smb2misc.c | 53 ++++++++++++++++++++++++++++++++++++++++
 1 file changed, 53 insertions(+)

diff --git a/fs/smb/client/smb2misc.c b/fs/smb/client/smb2misc.c
index 9068175e57cd..7bf7c602cda0 100644
--- a/fs/smb/client/smb2misc.c
+++ b/fs/smb/client/smb2misc.c
@@ -85,6 +85,49 @@ static const __le16 smb2_rsp_struct_sizes[NUMBER_OF_SMB2_COMMANDS] = {
 	/* SMB2_OPLOCK_BREAK */ cpu_to_le16(24)
 };
 
+/*
+ * Minimum received PDU size for commands whose fixed response struct is read
+ * by smb2_get_data_area_len() before the packet length is validated.  Must
+ * be non-zero for every command where has_smb2_data_area[] is true; zero
+ * otherwise (guard in smb2_check_message() is skipped).  Keep in sync with
+ * has_smb2_data_area[] above: adding a data area for a currently-zero command
+ * requires a matching sizeof() entry here.
+ */
+static const size_t smb2_min_pdu_len[NUMBER_OF_SMB2_COMMANDS] = {
+	/* SMB2_NEGOTIATE */       sizeof(struct smb2_negotiate_rsp),
+	/* SMB2_SESSION_SETUP */   sizeof(struct smb2_sess_setup_rsp),
+	/* SMB2_LOGOFF */          0,
+	/* SMB2_TREE_CONNECT */    0,
+	/* SMB2_TREE_DISCONNECT */ 0,
+	/* SMB2_CREATE */          sizeof(struct smb2_create_rsp),
+	/* SMB2_CLOSE */           0,
+	/* SMB2_FLUSH */           0,
+	/* SMB2_READ */            sizeof(struct smb2_read_rsp),
+	/* SMB2_WRITE */           0,
+	/* SMB2_LOCK */            0,
+	/* SMB2_IOCTL */           sizeof(struct smb2_ioctl_rsp),
+	/* SMB2_CANCEL */          0,
+	/* SMB2_ECHO */            0,
+	/* SMB2_QUERY_DIRECTORY */ sizeof(struct smb2_query_directory_rsp),
+	/* SMB2_CHANGE_NOTIFY */   sizeof(struct smb2_change_notify_rsp),
+	/* SMB2_QUERY_INFO */      sizeof(struct smb2_query_info_rsp),
+	/* SMB2_SET_INFO */        0,
+	/* SMB2_OPLOCK_BREAK */    0,
+};
+
+/* Enforce: smb2_min_pdu_len[x] != 0 for every x where has_smb2_data_area[x]. */
+static void __maybe_unused smb2_check_min_pdu_len_table(void)
+{
+	BUILD_BUG_ON(!smb2_min_pdu_len[SMB2_NEGOTIATE_HE]);
+	BUILD_BUG_ON(!smb2_min_pdu_len[SMB2_SESSION_SETUP_HE]);
+	BUILD_BUG_ON(!smb2_min_pdu_len[SMB2_CREATE_HE]);
+	BUILD_BUG_ON(!smb2_min_pdu_len[SMB2_READ_HE]);
+	BUILD_BUG_ON(!smb2_min_pdu_len[SMB2_IOCTL_HE]);
+	BUILD_BUG_ON(!smb2_min_pdu_len[SMB2_QUERY_DIRECTORY_HE]);
+	BUILD_BUG_ON(!smb2_min_pdu_len[SMB2_CHANGE_NOTIFY_HE]);
+	BUILD_BUG_ON(!smb2_min_pdu_len[SMB2_QUERY_INFO_HE]);
+}
+
 #define SMB311_NEGPROT_BASE_SIZE (sizeof(struct smb2_hdr) + sizeof(struct smb2_negotiate_rsp))
 
 static __u32 get_neg_ctxt_len(struct smb2_hdr *hdr, __u32 len,
@@ -233,6 +276,16 @@ smb2_check_message(char *buf, unsigned int pdu_len, unsigned int len,
 		}
 	}
 
+	if ((shdr->Status == 0 ||
+	     shdr->Status == STATUS_MORE_PROCESSING_REQUIRED ||
+	     pdu->StructureSize2 != SMB2_ERROR_STRUCTURE_SIZE2_LE) &&
+	    smb2_min_pdu_len[command] &&
+	    len < smb2_min_pdu_len[command]) {
+		cifs_dbg(VFS, "SMB2 command %d response too short: %u < %zu\n",
+			 command, len, smb2_min_pdu_len[command]);
+		return 1;
+	}
+
 	have_data = false;
 	data_area_overlap = false;
 	calc_len = __smb2_calc_size(buf, &have_data, &data_area_overlap);
-- 
2.55.0


^ permalink raw reply related	[flat|nested] 16+ messages in thread

* [PATCH 03/11] smb: client: fix server->total_read not tracking sub-PDU size in receive_encrypted_standard()
  2026-08-23 18:57 [PATCH 00/11] smb: client: fix OOB reads and UAFs in SMB2/3 receive paths Frank Sorenson
  2026-08-23 18:57 ` [PATCH 01/11] smb: client: fix NextCommand bounds and aliasing UAF in receive_encrypted_standard() Frank Sorenson
  2026-08-23 18:57 ` [PATCH 02/11] smb: client: validate PDU length before smb2_get_data_area_len() struct access Frank Sorenson
@ 2026-08-23 18:57 ` Frank Sorenson
  2026-08-23 18:58 ` [PATCH 04/11] smb: client: fix missing lower-bound check on DFS referral string offsets Frank Sorenson
                   ` (8 subsequent siblings)
  11 siblings, 0 replies; 16+ messages in thread
From: Frank Sorenson @ 2026-08-23 18:57 UTC (permalink / raw)
  To: linux-cifs; +Cc: pc, linkinjeon, stable

receive_encrypted_standard() processes compound encrypted frames by
iterating sub-PDUs, decrementing pdu_length by NextCommand on each
hop but leaving server->total_read at the original full-frame size.
cifs_handle_standard() passes server->total_read as len to
smb2_check_message(), so every length guard in smb2_check_message()
(smb2_rsp_struct_sizes[], smb2_min_pdu_len[]) operates on the
full-frame size rather than the current sub-PDU.

A malicious server can craft a compound encrypted frame where any
non-last sub-PDU is shorter than the fixed response struct it
declares.  The oversized len bypasses both guards, smb2_get_data_area_len()
reads struct fields at offsets past the received data, and the
data-area length arithmetic produces a spurious pointer into
uninitialized slab content or the next sub-PDU, which a rogue server
controls.

At the top of each iteration, pdu_length is the full remaining
compound tail (current sub-PDU + all subsequent ones), not the current
sub-PDU alone.  For a non-last sub-PDU, server->total_read must be
derived from next_cmd, not pdu_length.  Read next_cmd before assigning
server->total_read, then use `next_cmd ? next_cmd : pdu_length` so
that smb2_check_message() sees the actual per-sub-PDU extent on every
pass through the loop.

Fixes: b24df3e30cbf ("cifs: update receive_encrypted_standard to handle compounded responses")
Cc: stable@vger.kernel.org
Signed-off-by: Frank Sorenson <sorenson@redhat.com>
---
 fs/smb/client/smb2ops.c | 1 +
 1 file changed, 1 insertion(+)

diff --git a/fs/smb/client/smb2ops.c b/fs/smb/client/smb2ops.c
index e75420dbe950..1bc3f8266eda 100644
--- a/fs/smb/client/smb2ops.c
+++ b/fs/smb/client/smb2ops.c
@@ -5248,6 +5248,7 @@ receive_encrypted_standard(struct TCP_Server_Info *server,
 one_more:
 	shdr = (struct smb2_hdr *)buf;
 	next_cmd = le32_to_cpu(shdr->NextCommand);
+	server->total_read = next_cmd ? next_cmd : pdu_length;
 
 	if (*num_mids >= MAX_COMPOUND) {
 		cifs_server_dbg(VFS, "too many PDUs in compound\n");
-- 
2.55.0


^ permalink raw reply related	[flat|nested] 16+ messages in thread

* [PATCH 04/11] smb: client: fix missing lower-bound check on DFS referral string offsets
  2026-08-23 18:57 [PATCH 00/11] smb: client: fix OOB reads and UAFs in SMB2/3 receive paths Frank Sorenson
                   ` (2 preceding siblings ...)
  2026-08-23 18:57 ` [PATCH 03/11] smb: client: fix server->total_read not tracking sub-PDU size in receive_encrypted_standard() Frank Sorenson
@ 2026-08-23 18:58 ` Frank Sorenson
  2026-08-23 18:58 ` [PATCH 05/11] smb: client: fix missing lower-bound on Next field in parse_server_interfaces() Frank Sorenson
                   ` (7 subsequent siblings)
  11 siblings, 0 replies; 16+ messages in thread
From: Frank Sorenson @ 2026-08-23 18:58 UTC (permalink / raw)
  To: linux-cifs; +Cc: pc, linkinjeon, stable

parse_dfs_referrals() checks DfsPathOffset and NetworkAddressOffset
against the buffer end but not against sizeof(*ref).  An offset smaller
than sizeof(struct dfs_referral_level_3) places the derived pointer
inside the referral header, causing cifs_strndup_from_utf16() to read
struct fields as string data.

Require each offset to be at least sizeof(*ref).

Fixes: 4ecce920e13a ("CIFS: move DFS response parsing out of SMB1 code")
Cc: stable@vger.kernel.org
Signed-off-by: Frank Sorenson <sorenson@redhat.com>
---
 fs/smb/client/misc.c | 12 ++++++++++--
 1 file changed, 10 insertions(+), 2 deletions(-)

diff --git a/fs/smb/client/misc.c b/fs/smb/client/misc.c
index 46e1382e8e04..bc7cccebf387 100644
--- a/fs/smb/client/misc.c
+++ b/fs/smb/client/misc.c
@@ -787,7 +787,11 @@ parse_dfs_referrals(struct get_dfs_referral_rsp *rsp, u32 rsp_size,
 		node->ref_flag = le16_to_cpu(ref->ReferralEntryFlags);
 
 		/* copy DfsPath */
-		if (le16_to_cpu(ref->DfsPathOffset) > data_end - (char *)ref) {
+		if (le16_to_cpu(ref->DfsPathOffset) < sizeof(*ref) ||
+		    le16_to_cpu(ref->DfsPathOffset) > data_end - (char *)ref) {
+			cifs_dbg(VFS, "%s: DfsPathOffset %u out of range [%zu, %td]\n",
+				 __func__, le16_to_cpu(ref->DfsPathOffset),
+				 sizeof(*ref), data_end - (char *)ref);
 			rc = -EINVAL;
 			goto parse_DFS_referrals_exit;
 		}
@@ -801,7 +805,11 @@ parse_dfs_referrals(struct get_dfs_referral_rsp *rsp, u32 rsp_size,
 		}
 
 		/* copy link target UNC */
-		if (le16_to_cpu(ref->NetworkAddressOffset) > data_end - (char *)ref) {
+		if (le16_to_cpu(ref->NetworkAddressOffset) < sizeof(*ref) ||
+		    le16_to_cpu(ref->NetworkAddressOffset) > data_end - (char *)ref) {
+			cifs_dbg(VFS, "%s: NetworkAddressOffset %u out of range [%zu, %td]\n",
+				 __func__, le16_to_cpu(ref->NetworkAddressOffset),
+				 sizeof(*ref), data_end - (char *)ref);
 			rc = -EINVAL;
 			goto parse_DFS_referrals_exit;
 		}
-- 
2.55.0


^ permalink raw reply related	[flat|nested] 16+ messages in thread

* [PATCH 05/11] smb: client: fix missing lower-bound on Next field in parse_server_interfaces()
  2026-08-23 18:57 [PATCH 00/11] smb: client: fix OOB reads and UAFs in SMB2/3 receive paths Frank Sorenson
                   ` (3 preceding siblings ...)
  2026-08-23 18:58 ` [PATCH 04/11] smb: client: fix missing lower-bound check on DFS referral string offsets Frank Sorenson
@ 2026-08-23 18:58 ` Frank Sorenson
  2026-08-23 18:58 ` [PATCH 06/11] smb: client: fix OOB struct field reads in move_smb2_ea_to_cifs() Frank Sorenson
                   ` (6 subsequent siblings)
  11 siblings, 0 replies; 16+ messages in thread
From: Frank Sorenson @ 2026-08-23 18:58 UTC (permalink / raw)
  To: linux-cifs; +Cc: pc, linkinjeon, stable

parse_server_interfaces() validates the server-supplied Next offset
against bytes_left but not against sizeof(*p).  A small non-zero value
passes the upper-bound check yet advances p by less than one struct,
causing the next iteration to read misaligned, overlapping fields.

Add next < sizeof(*p) to the existing bounds check.

Fixes: 7d34ec36abb8 ("smb3: fix for slab out of bounds on mount to ksmbd")
Cc: stable@vger.kernel.org
Signed-off-by: Frank Sorenson <sorenson@redhat.com>
---
 fs/smb/client/smb2ops.c | 6 +++---
 1 file changed, 3 insertions(+), 3 deletions(-)

diff --git a/fs/smb/client/smb2ops.c b/fs/smb/client/smb2ops.c
index 1bc3f8266eda..9065c94e5ec6 100644
--- a/fs/smb/client/smb2ops.c
+++ b/fs/smb/client/smb2ops.c
@@ -785,9 +785,9 @@ parse_server_interfaces(struct network_interface_info_ioctl_rsp *buf,
 			break;
 		}
 		/* Validate that Next doesn't point beyond the buffer */
-		if (next > bytes_left) {
-			cifs_dbg(VFS, "%s: invalid Next pointer %zu > %zd\n",
-				 __func__, next, bytes_left);
+		if (next < sizeof(*p) || next > bytes_left) {
+			cifs_dbg(VFS, "%s: invalid Next pointer %zu out of range [%zu, %zd]\n",
+				 __func__, next, sizeof(*p), bytes_left);
 			rc = -EINVAL;
 			goto out;
 		}
-- 
2.55.0


^ permalink raw reply related	[flat|nested] 16+ messages in thread

* [PATCH 06/11] smb: client: fix OOB struct field reads in move_smb2_ea_to_cifs()
  2026-08-23 18:57 [PATCH 00/11] smb: client: fix OOB reads and UAFs in SMB2/3 receive paths Frank Sorenson
                   ` (4 preceding siblings ...)
  2026-08-23 18:58 ` [PATCH 05/11] smb: client: fix missing lower-bound on Next field in parse_server_interfaces() Frank Sorenson
@ 2026-08-23 18:58 ` Frank Sorenson
  2026-08-25 22:13   ` [PATCH v2] " Frank Sorenson
  2026-08-23 18:58 ` [PATCH 07/11] smb: client: fix missing iov bounds check in parse_posix_sids() Frank Sorenson
                   ` (5 subsequent siblings)
  11 siblings, 1 reply; 16+ messages in thread
From: Frank Sorenson @ 2026-08-23 18:58 UTC (permalink / raw)
  To: linux-cifs; +Cc: pc, linkinjeon, stable

The while (src_size > 0) loop guard allows iteration after
next_entry_offset advances src past the point where a full struct fits
in src_size.  Reads of ea_name_length and ea_value_length on the next
iteration are then out-of-bounds.

Require src_size >= sizeof(*src) before reading any struct field, and
reject next_entry_offset values smaller than sizeof(*src).

For calls where the server returns an EA list with an invalid
next_entry_offset, the error returned to userspace changes from
-ENODATA (getxattr) or -ERANGE (listxattr) to -EIO, correctly
signalling a server protocol error rather than "attribute not present"
or "output buffer too small".

Fixes: 95907fea4fd8 ("cifs: Add support for reading attributes on SMB2+")
Cc: stable@vger.kernel.org
Signed-off-by: Frank Sorenson <sorenson@redhat.com>
---
 fs/smb/client/smb2ops.c | 19 +++++++++++--------
 fs/smb/client/trace.h   |  1 +
 2 files changed, 12 insertions(+), 8 deletions(-)

diff --git a/fs/smb/client/smb2ops.c b/fs/smb/client/smb2ops.c
index 9065c94e5ec6..37c1144f3d57 100644
--- a/fs/smb/client/smb2ops.c
+++ b/fs/smb/client/smb2ops.c
@@ -1053,8 +1053,9 @@ move_smb2_ea_to_cifs(char *dst, size_t dst_size,
 	char *name, *value;
 	size_t buf_size = dst_size;
 	size_t name_len, value_len, user_name_len;
+	u32 next_off;
 
-	while (src_size > 0) {
+	while (src_size >= sizeof(*src)) {
 		name_len = (size_t)src->ea_name_length;
 		value_len = (size_t)le16_to_cpu(src->ea_value_length);
 
@@ -1110,14 +1111,16 @@ move_smb2_ea_to_cifs(char *dst, size_t dst_size,
 		if (!src->next_entry_offset)
 			break;
 
-		if (src_size < le32_to_cpu(src->next_entry_offset)) {
-			/* stop before overrun buffer */
-			rc = -ERANGE;
-			break;
+		next_off = le32_to_cpu(src->next_entry_offset);
+		if (next_off < sizeof(*src) || src_size < next_off) {
+			cifs_dbg(FYI, "EA next_entry_offset %u out of range [%zu, %zu]\n",
+				 next_off, sizeof(*src), src_size);
+			rc = smb_EIO2(smb_eio_trace_ea_next_offset,
+				      next_off, src_size);
+			goto out;
 		}
-		src_size -= le32_to_cpu(src->next_entry_offset);
-		src = (void *)((char *)src +
-			       le32_to_cpu(src->next_entry_offset));
+		src_size -= next_off;
+		src = (void *)((char *)src + next_off);
 	}
 
 	/* didn't find the named attribute */
diff --git a/fs/smb/client/trace.h b/fs/smb/client/trace.h
index 12241abb8e2e..6da395abae14 100644
--- a/fs/smb/client/trace.h
+++ b/fs/smb/client/trace.h
@@ -27,6 +27,7 @@
 	EM(smb_eio_trace_copychunk_overcopy_c,		"copychunk_overcopy_c") \
 	EM(smb_eio_trace_create_rsp_too_small,		"create_rsp_too_small") \
 	EM(smb_eio_trace_dfsref_no_rsp,			"dfsref_no_rsp") \
+	EM(smb_eio_trace_ea_next_offset,		"ea_next_offset") \
 	EM(smb_eio_trace_ea_overrun,			"ea_overrun") \
 	EM(smb_eio_trace_extract_will_pin,		"extract_will_pin") \
 	EM(smb_eio_trace_forced_shutdown,		"forced_shutdown") \
-- 
2.55.0


^ permalink raw reply related	[flat|nested] 16+ messages in thread

* [PATCH 07/11] smb: client: fix missing iov bounds check in parse_posix_sids()
  2026-08-23 18:57 [PATCH 00/11] smb: client: fix OOB reads and UAFs in SMB2/3 receive paths Frank Sorenson
                   ` (5 preceding siblings ...)
  2026-08-23 18:58 ` [PATCH 06/11] smb: client: fix OOB struct field reads in move_smb2_ea_to_cifs() Frank Sorenson
@ 2026-08-23 18:58 ` Frank Sorenson
  2026-08-23 18:58 ` [PATCH 08/11] smb: client: fix underflow in is_valid_oplock_break() notify offset check Frank Sorenson
                   ` (4 subsequent siblings)
  11 siblings, 0 replies; 16+ messages in thread
From: Frank Sorenson @ 2026-08-23 18:58 UTC (permalink / raw)
  To: linux-cifs; +Cc: pc, linkinjeon, stable

parse_posix_sids() derives sidsbuf_end from the server-supplied out_len
without validating against the received iov:

    sidsbuf_end = sidsbuf + out_len - qi_len;

An inflated out_len places sidsbuf_end past the iov, defeating the
bounds guards in posix_info_sid_size() and allowing reads into adjacent
kernel memory.

Reject responses where out_len places sidsbuf_end past the received iov
or causes pointer wraparound.

Fixes: a90f37e3d7ac ("smb: client: parse owner/group when creating reparse points")
Cc: stable@vger.kernel.org
Signed-off-by: Frank Sorenson <sorenson@redhat.com>
---
 fs/smb/client/smb2inode.c | 11 +++++++++++
 1 file changed, 11 insertions(+)

diff --git a/fs/smb/client/smb2inode.c b/fs/smb/client/smb2inode.c
index 98ea5c6c34af..c7a2dddb3959 100644
--- a/fs/smb/client/smb2inode.c
+++ b/fs/smb/client/smb2inode.c
@@ -77,6 +77,17 @@ static int parse_posix_sids(struct cifs_open_info_data *data,
 
 	sidsbuf = (u8 *)qi + le16_to_cpu(qi->OutputBufferOffset) + qi_len;
 	sidsbuf_end = sidsbuf + out_len - qi_len;
+	if (sidsbuf_end < sidsbuf) {
+		cifs_dbg(VFS, "%s: server-supplied out_len %u caused pointer wraparound\n",
+			 __func__, out_len);
+		return -EINVAL;
+	}
+	if (sidsbuf_end > (u8 *)rsp_iov->iov_base + rsp_iov->iov_len) {
+		cifs_dbg(VFS, "%s: server-supplied out_len %u overruns iov by %td bytes\n",
+			 __func__, out_len,
+			 sidsbuf_end - ((u8 *)rsp_iov->iov_base + rsp_iov->iov_len));
+		return -EINVAL;
+	}
 
 	owner_len = posix_info_sid_size(sidsbuf, sidsbuf_end);
 	if (owner_len == -1)
-- 
2.55.0


^ permalink raw reply related	[flat|nested] 16+ messages in thread

* [PATCH 08/11] smb: client: fix underflow in is_valid_oplock_break() notify offset check
  2026-08-23 18:57 [PATCH 00/11] smb: client: fix OOB reads and UAFs in SMB2/3 receive paths Frank Sorenson
                   ` (6 preceding siblings ...)
  2026-08-23 18:58 ` [PATCH 07/11] smb: client: fix missing iov bounds check in parse_posix_sids() Frank Sorenson
@ 2026-08-23 18:58 ` Frank Sorenson
  2026-08-23 18:58 ` [PATCH 09/11] smb: client: fix potential OOB read in smb3_enum_snapshots() Frank Sorenson
                   ` (3 subsequent siblings)
  11 siblings, 0 replies; 16+ messages in thread
From: Frank Sorenson @ 2026-08-23 18:58 UTC (permalink / raw)
  To: linux-cifs; +Cc: pc, linkinjeon, stable

The check intended to bound data_offset against the received frame:

    if (data_offset > len - sizeof(struct file_notify_information))

underflows when len < sizeof(struct file_notify_information): the
subtraction wraps to a large value, the condition is false, and pnotify
is constructed from an unchecked data_offset.

Check len < sizeof(struct file_notify_information) first.

Fixes: 097f5863b1a0 ("cifs: read overflow in is_valid_oplock_break()")
Cc: stable@vger.kernel.org
Signed-off-by: Frank Sorenson <sorenson@redhat.com>
---
 fs/smb/client/smb1misc.c | 3 ++-
 1 file changed, 2 insertions(+), 1 deletion(-)

diff --git a/fs/smb/client/smb1misc.c b/fs/smb/client/smb1misc.c
index cdfbbff24b72..a39c9140ceca 100644
--- a/fs/smb/client/smb1misc.c
+++ b/fs/smb/client/smb1misc.c
@@ -86,7 +86,8 @@ is_valid_oplock_break(char *buffer, struct TCP_Server_Info *srv)
 		if (get_bcc(buf) > sizeof(struct file_notify_information)) {
 			data_offset = le32_to_cpu(pSMBr->DataOffset);
 
-			if (data_offset >
+			if (len < sizeof(struct file_notify_information) ||
+			    data_offset >
 			    len - sizeof(struct file_notify_information)) {
 				cifs_dbg(FYI, "Invalid data_offset %u\n",
 					 data_offset);
-- 
2.55.0


^ permalink raw reply related	[flat|nested] 16+ messages in thread

* [PATCH 09/11] smb: client: fix potential OOB read in smb3_enum_snapshots()
  2026-08-23 18:57 [PATCH 00/11] smb: client: fix OOB reads and UAFs in SMB2/3 receive paths Frank Sorenson
                   ` (7 preceding siblings ...)
  2026-08-23 18:58 ` [PATCH 08/11] smb: client: fix underflow in is_valid_oplock_break() notify offset check Frank Sorenson
@ 2026-08-23 18:58 ` Frank Sorenson
  2026-08-23 18:58 ` [PATCH 10/11] smb: client: fix incomplete bounds check on reparse buffer in cifs_query_reparse_point() Frank Sorenson
                   ` (2 subsequent siblings)
  11 siblings, 0 replies; 16+ messages in thread
From: Frank Sorenson @ 2026-08-23 18:58 UTC (permalink / raw)
  To: linux-cifs; +Cc: pc, linkinjeon, stable

When snapshot_array_size < GMT_TOKEN_SIZE, smb3_enum_snapshots()
unconditionally sets ret_data_len = sizeof(struct smb_snapshot_array),
silently inflating it beyond the actual server response size if the
server returned fewer bytes.  The subsequent copy_to_user() then reads
past the end of retbuf.

Reject responses shorter than sizeof(struct smb_snapshot_array) with -EIO.

Fixes: dcbf1c8af18a ("smb3: add ioctl to get a list of snapshots for volume or share")
Cc: stable@vger.kernel.org
Signed-off-by: Frank Sorenson <sorenson@redhat.com>
---
 fs/smb/client/smb2ops.c | 8 +++++++-
 1 file changed, 7 insertions(+), 1 deletion(-)

diff --git a/fs/smb/client/smb2ops.c b/fs/smb/client/smb2ops.c
index 37c1144f3d57..77ff285ce1b6 100644
--- a/fs/smb/client/smb2ops.c
+++ b/fs/smb/client/smb2ops.c
@@ -2376,8 +2376,14 @@ smb3_enum_snapshots(const unsigned int xid, struct cifs_tcon *tcon,
 		 * and retry the ioctl again with larger array size sufficient
 		 * to hold all of the snapshot GMT tokens on the second try.
 		 */
-		if (snapshot_in.snapshot_array_size < GMT_TOKEN_SIZE)
+		if (snapshot_in.snapshot_array_size < GMT_TOKEN_SIZE) {
+			if (ret_data_len < sizeof(struct smb_snapshot_array)) {
+				rc = -EIO;
+				kfree(retbuf);
+				return rc;
+			}
 			ret_data_len = sizeof(struct smb_snapshot_array);
+		}
 
 		/*
 		 * We return struct SRV_SNAPSHOT_ARRAY, followed by
-- 
2.55.0


^ permalink raw reply related	[flat|nested] 16+ messages in thread

* [PATCH 10/11] smb: client: fix incomplete bounds check on reparse buffer in cifs_query_reparse_point()
  2026-08-23 18:57 [PATCH 00/11] smb: client: fix OOB reads and UAFs in SMB2/3 receive paths Frank Sorenson
                   ` (8 preceding siblings ...)
  2026-08-23 18:58 ` [PATCH 09/11] smb: client: fix potential OOB read in smb3_enum_snapshots() Frank Sorenson
@ 2026-08-23 18:58 ` Frank Sorenson
  2026-08-23 18:58 ` [PATCH 11/11] smb: client: fix NameOffset and Next field validation in smb2_parse_contexts() Frank Sorenson
  2026-08-25 23:54 ` [PATCH 00/11] smb: client: fix OOB reads and UAFs in SMB2/3 receive paths Yunseong Kim
  11 siblings, 0 replies; 16+ messages in thread
From: Frank Sorenson @ 2026-08-23 18:58 UTC (permalink / raw)
  To: linux-cifs; +Cc: pc, linkinjeon, stable

The start >= end check before casting to struct reparse_data_buffer *
ensures the pointer is in range but not that end - start >= sizeof(*buf).
A server-supplied DataOffset leaving fewer than 8 bytes passes the check
but allows OOB reads of ReparseTag and ReparseDataLength.

Fold the minimum-size check into the existing condition using `end`.

Fixes: c13b779d26b3 ("cifs: Fix validation of SMB1 query reparse point response")
Cc: stable@vger.kernel.org
Signed-off-by: Frank Sorenson <sorenson@redhat.com>
---
 fs/smb/client/cifssmb.c | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/fs/smb/client/cifssmb.c b/fs/smb/client/cifssmb.c
index f5aad5f61dce..acb533383a3e 100644
--- a/fs/smb/client/cifssmb.c
+++ b/fs/smb/client/cifssmb.c
@@ -3064,7 +3064,7 @@ int cifs_query_reparse_point(const unsigned int xid,
 
 	end = 2 + get_bcc(&io_rsp->hdr) + (__u8 *)&io_rsp->ByteCount;
 	start = (__u8 *)&io_rsp->hdr.Protocol + data_offset;
-	if (start >= end) {
+	if (start >= end || (size_t)(end - start) < sizeof(*buf)) {
 		rc = smb_EIO2(smb_eio_trace_qreparse_data_area,
 			      (unsigned long)start - (unsigned long)io_rsp,
 			      (unsigned long)end - (unsigned long)io_rsp);
-- 
2.55.0


^ permalink raw reply related	[flat|nested] 16+ messages in thread

* [PATCH 11/11] smb: client: fix NameOffset and Next field validation in smb2_parse_contexts()
  2026-08-23 18:57 [PATCH 00/11] smb: client: fix OOB reads and UAFs in SMB2/3 receive paths Frank Sorenson
                   ` (9 preceding siblings ...)
  2026-08-23 18:58 ` [PATCH 10/11] smb: client: fix incomplete bounds check on reparse buffer in cifs_query_reparse_point() Frank Sorenson
@ 2026-08-23 18:58 ` Frank Sorenson
  2026-08-25 23:54 ` [PATCH 00/11] smb: client: fix OOB reads and UAFs in SMB2/3 receive paths Yunseong Kim
  11 siblings, 0 replies; 16+ messages in thread
From: Frank Sorenson @ 2026-08-23 18:58 UTC (permalink / raw)
  To: linux-cifs; +Cc: pc, linkinjeon, stable

Three related bounds issues in smb2_parse_contexts():

NameOffset: the existing 'noff + nlen > doff' check doesn't prevent
the name from starting inside the context header or extending past rem.
Replace it with noff >= sizeof(*cc) and noff + nlen <= rem, keeping
the name-before-data guard only when DataLength is non-zero.

Next: a non-zero Next smaller than sizeof(*cc) passes the existing
upper-bound check but creates an overlapping context pointer.  Add
off < sizeof(*cc) to reject it.

Dispatch: when DataLength is zero, calling a handler that reads the
data area (parse_lease_buf, parse_query_id_ctxt, parse_posix_ctxt)
reads stale memory past the declared context.  A rogue server can
craft a zero-DataLength context whose name matches a known tag (e.g.
'RqLs') and bypass the name-vs-data-offset guard, causing
parse_lease_buf to record a lease key and state from uninitialized
kmalloc content.  Gate all three dispatch paths on dlen > 0.

Fixes: af1689a9b770 ("smb: client: fix potential OOBs in smb2_parse_contexts()")
Cc: stable@vger.kernel.org
Signed-off-by: Frank Sorenson <sorenson@redhat.com>
---
 fs/smb/client/smb2pdu.c | 11 ++++++-----
 1 file changed, 6 insertions(+), 5 deletions(-)

diff --git a/fs/smb/client/smb2pdu.c b/fs/smb/client/smb2pdu.c
index dea05aeb53a1..dda7a1a88b44 100644
--- a/fs/smb/client/smb2pdu.c
+++ b/fs/smb/client/smb2pdu.c
@@ -2459,22 +2459,23 @@ int smb2_parse_contexts(struct TCP_Server_Info *server,
 
 		noff = le16_to_cpu(cc->NameOffset);
 		nlen = le16_to_cpu(cc->NameLength);
-		if (noff + nlen > doff)
+		if (noff < sizeof(*cc) || noff + nlen > rem ||
+		    (dlen && noff + nlen > doff))
 			return -EINVAL;
 
 		name = (char *)cc + noff;
 		switch (nlen) {
 		case 4:
-			if (!strncmp(name, SMB2_CREATE_REQUEST_LEASE, 4)) {
+			if (dlen && !strncmp(name, SMB2_CREATE_REQUEST_LEASE, 4)) {
 				*oplock = server->ops->parse_lease_buf(cc, epoch,
 								       lease_key);
-			} else if (buf &&
+			} else if (dlen && buf &&
 				   !strncmp(name, SMB2_CREATE_QUERY_ON_DISK_ID, 4)) {
 				parse_query_id_ctxt(cc, buf);
 			}
 			break;
 		case 16:
-			if (posix && !memcmp(name, smb3_create_tag_posix, 16))
+			if (dlen && posix && !memcmp(name, smb3_create_tag_posix, 16))
 				parse_posix_ctxt(cc, buf, posix);
 			break;
 		default:
@@ -2488,7 +2489,7 @@ int smb2_parse_contexts(struct TCP_Server_Info *server,
 		off = le32_to_cpu(cc->Next);
 		if (!off)
 			break;
-		if (check_sub_overflow(rem, off, &rem))
+		if (off < sizeof(*cc) || check_sub_overflow(rem, off, &rem))
 			return -EINVAL;
 		cc = (struct create_context *)((u8 *)cc + off);
 	}
-- 
2.55.0


^ permalink raw reply related	[flat|nested] 16+ messages in thread

* [PATCH v2] smb: client: fix OOB struct field reads in move_smb2_ea_to_cifs()
  2026-08-23 18:58 ` [PATCH 06/11] smb: client: fix OOB struct field reads in move_smb2_ea_to_cifs() Frank Sorenson
@ 2026-08-25 22:13   ` Frank Sorenson
  2026-08-26  0:59     ` Namjae Jeon
  0 siblings, 1 reply; 16+ messages in thread
From: Frank Sorenson @ 2026-08-25 22:13 UTC (permalink / raw)
  To: linux-cifs; +Cc: pc, linkinjeon, stable

The while (src_size > 0) loop guard allows iteration after
next_entry_offset advances src past the point where a full struct fits
in src_size.  Reads of ea_name_length and ea_value_length on the next
iteration are then out-of-bounds.

Require src_size >= sizeof(*src) before reading any struct field,
and reject next_entry_offset values that are smaller than
sizeof(*src) or that leave fewer than sizeof(*src) bytes remaining
after advancing.

For calls where the server returns an EA list with an invalid
next_entry_offset, the error returned to userspace changes from
-ENODATA (getxattr) or -ERANGE (listxattr) to -EIO, correctly
signalling a server protocol error rather than "attribute not present"
or "output buffer too small".

Fixes: 95907fea4fd8 ("cifs: Add support for reading attributes on SMB2+")
Cc: stable@vger.kernel.org
Signed-off-by: Frank Sorenson <sorenson@redhat.com>
---
v2 changes:
 -  reject next_entry_offset values that leave fewer than
    sizeof(*src) bytes remainint after advancing.

 fs/smb/client/smb2ops.c | 25 +++++++++++++++++--------
 fs/smb/client/trace.h   |  1 +
 2 files changed, 18 insertions(+), 8 deletions(-)

diff --git a/fs/smb/client/smb2ops.c b/fs/smb/client/smb2ops.c
index 4dd9dd55ab4d..c866d7c8c7dd 100644
--- a/fs/smb/client/smb2ops.c
+++ b/fs/smb/client/smb2ops.c
@@ -1053,8 +1053,9 @@ move_smb2_ea_to_cifs(char *dst, size_t dst_size,
 	char *name, *value;
 	size_t buf_size = dst_size;
 	size_t name_len, value_len, user_name_len;
+	u32 next_off;
 
-	while (src_size > 0) {
+	while (src_size >= sizeof(*src)) {
 		name_len = (size_t)src->ea_name_length;
 		value_len = (size_t)le16_to_cpu(src->ea_value_length);
 
@@ -1110,14 +1111,22 @@ move_smb2_ea_to_cifs(char *dst, size_t dst_size,
 		if (!src->next_entry_offset)
 			break;
 
-		if (src_size < le32_to_cpu(src->next_entry_offset)) {
-			/* stop before overrun buffer */
-			rc = -ERANGE;
-			break;
+		next_off = le32_to_cpu(src->next_entry_offset);
+		if (next_off < sizeof(*src) || src_size < next_off) {
+			cifs_dbg(FYI, "EA next_entry_offset %u out of range [%zu, %zu]\n",
+				 next_off, sizeof(*src), src_size);
+			rc = smb_EIO2(smb_eio_trace_ea_next_offset,
+				      next_off, src_size);
+			goto out;
+		}
+		src_size -= next_off;
+		src = (void *)((char *)src + next_off);
+		if (src_size > 0 && src_size < sizeof(*src)) {
+			cifs_dbg(FYI, "EA next_entry_offset %u left truncated entry (%zu bytes)\n",
+				 next_off, src_size);
+			rc = smb_EIO2(smb_eio_trace_ea_next_offset, next_off, src_size);
+			goto out;
 		}
-		src_size -= le32_to_cpu(src->next_entry_offset);
-		src = (void *)((char *)src +
-			       le32_to_cpu(src->next_entry_offset));
 	}
 
 	/* didn't find the named attribute */
diff --git a/fs/smb/client/trace.h b/fs/smb/client/trace.h
index 12241abb8e2e..6da395abae14 100644
--- a/fs/smb/client/trace.h
+++ b/fs/smb/client/trace.h
@@ -27,6 +27,7 @@
 	EM(smb_eio_trace_copychunk_overcopy_c,		"copychunk_overcopy_c") \
 	EM(smb_eio_trace_create_rsp_too_small,		"create_rsp_too_small") \
 	EM(smb_eio_trace_dfsref_no_rsp,			"dfsref_no_rsp") \
+	EM(smb_eio_trace_ea_next_offset,		"ea_next_offset") \
 	EM(smb_eio_trace_ea_overrun,			"ea_overrun") \
 	EM(smb_eio_trace_extract_will_pin,		"extract_will_pin") \
 	EM(smb_eio_trace_forced_shutdown,		"forced_shutdown") \
-- 
2.55.0


^ permalink raw reply related	[flat|nested] 16+ messages in thread

* Re: [PATCH 00/11] smb: client: fix OOB reads and UAFs in SMB2/3 receive paths
  2026-08-23 18:57 [PATCH 00/11] smb: client: fix OOB reads and UAFs in SMB2/3 receive paths Frank Sorenson
                   ` (10 preceding siblings ...)
  2026-08-23 18:58 ` [PATCH 11/11] smb: client: fix NameOffset and Next field validation in smb2_parse_contexts() Frank Sorenson
@ 2026-08-25 23:54 ` Yunseong Kim
  2026-08-26  2:18   ` Frank Sorenson
  11 siblings, 1 reply; 16+ messages in thread
From: Yunseong Kim @ 2026-08-25 23:54 UTC (permalink / raw)
  To: Frank Sorenson; +Cc: Yunseong Kim, linux-cifs, pc, linkinjeon, yunseong.kim

Hi Frank,

Thank you, for your security works!

On Sun, 23 Aug 2026 13:57:56 -0500 Frank Sorenson <sorenson@redhat.com> wrote:

> This series fixes eleven bounds-checking defects in the SMB2/3 client,
> all reachable from a malicious or compromised server.
> 
> Patches 1-3 address the compound encrypted frame processing path:
> 
> Patch 1 fixes four interacting bugs in receive_encrypted_standard():
> a missing lower bound on NextCommand, an off-by-one upper bound that
> admitted trailing slices too small for an SMB2 header (producing a
> write-after-free via next_buffer aliasing server->bigbuf), a stale
> next_buffer pointer not cleared before goto one_more, and use of the
> pre-decryption pdu_length instead of the plaintext extent for bounds
> checking.
> 
> Patch 2 adds smb2_min_pdu_len[], a per-command table of minimum response
> struct sizes, and uses it in smb2_check_message() to reject responses
> too short for smb2_get_data_area_len() to safely read command-specific
> struct fields.
> 
> Patch 3 fixes server->total_read tracking in receive_encrypted_standard()
> so that smb2_check_message() sees the actual per-sub-PDU size rather than
> the full remaining compound tail.  Without this, a rogue server can craft
> a compound frame where any non-last sub-PDU is shorter than its declared
> fixed struct, bypassing the guards added in patch 2.
> 
> The remaining patches fix lower-bound gaps and OOB reads in DFS referral
> parsing, EA list traversal, posix SID bounds, change-notify offset,
> snapshot enumeration, and SMB1 reparse point validation.
> 
> Note on overlap with a concurrent series: Zihan Xi's
> [PATCH v2 0/2] "smb: client: fix create context out-of-bounds reads"
> (Message-ID: <cover.1787486936.git.zihanx@nebusec.ai>) touches
> smb2_parse_contexts() and parse_posix_ctxt() independently.  Patch 11
> here addresses the same function (smb2_parse_contexts()) but focuses on
> complementary issues that their series does not cover: NameOffset
> validation (lower and upper bounds) and gating all three handler
> dispatches on a non-zero DataLength to prevent zero-DataLength contexts
> from exercising parse_lease_buf, parse_query_id_ctxt, or parse_posix_ctxt.
> Their per-context cc_len bounding and lease/QFid minimum-length checks
> are not duplicated here.  parse_posix_ctxt() DataLength validation is
> omitted from this series entirely since their patch 2/2 addresses it.
> 
> Frank Sorenson (11):
>   smb: client: fix NextCommand bounds and aliasing UAF in
>     receive_encrypted_standard()
>   smb: client: validate PDU length before smb2_get_data_area_len()
>     struct access
>   smb: client: fix server->total_read not tracking sub-PDU size in
>     receive_encrypted_standard()
>   smb: client: fix missing lower-bound check on DFS referral string
>     offsets
>   smb: client: fix missing lower-bound on Next field in
>     parse_server_interfaces()
>   smb: client: fix OOB struct field reads in move_smb2_ea_to_cifs()
>   smb: client: fix missing iov bounds check in parse_posix_sids()
>   smb: client: fix underflow in is_valid_oplock_break() notify offset
>     check
>   smb: client: fix potential OOB read in smb3_enum_snapshots()
>   smb: client: fix incomplete bounds check on reparse buffer in
>     cifs_query_reparse_point()
>   smb: client: fix NameOffset and Next field validation in
>     smb2_parse_contexts()
> 
>  fs/smb/client/cifssmb.c   |  2 +-
>  fs/smb/client/misc.c      | 12 +++++++--
>  fs/smb/client/smb1misc.c  |  3 ++-
>  fs/smb/client/smb2inode.c | 11 ++++++++
>  fs/smb/client/smb2misc.c  | 53 +++++++++++++++++++++++++++++++++++++++
>  fs/smb/client/smb2ops.c   | 45 +++++++++++++++++++++++----------
>  fs/smb/client/smb2pdu.c   | 11 ++++----
>  fs/smb/client/trace.h     |  1 +
>  8 files changed, 116 insertions(+), 22 deletions(-)
> 
> -- 
> 2.55.0
> 
> 

Just a small question: I wasn't able to verify the call stack from this
patch series alone. Is there a reproducible test case or script that triggers
the issue?

As a security researcher, I'd also like to independently verify the findings
and cross-check the behavior on my side. Any reproducer or additional details
would be greatly appreciated.

The reason I'm asking is that I've been working on CI coverage for SMB. I hope
we can integrate tests for this issue as well, so that similar regressions can
be detected and prevented in the future.

Best regards,
Yunseong

^ permalink raw reply	[flat|nested] 16+ messages in thread

* Re: [PATCH v2] smb: client: fix OOB struct field reads in move_smb2_ea_to_cifs()
  2026-08-25 22:13   ` [PATCH v2] " Frank Sorenson
@ 2026-08-26  0:59     ` Namjae Jeon
  0 siblings, 0 replies; 16+ messages in thread
From: Namjae Jeon @ 2026-08-26  0:59 UTC (permalink / raw)
  To: Frank Sorenson; +Cc: linux-cifs, pc, stable

On Wed, Aug 26, 2026 at 7:13 AM Frank Sorenson <sorenson@redhat.com> wrote:
>
> The while (src_size > 0) loop guard allows iteration after
> next_entry_offset advances src past the point where a full struct fits
> in src_size.  Reads of ea_name_length and ea_value_length on the next
> iteration are then out-of-bounds.
>
> Require src_size >= sizeof(*src) before reading any struct field,
> and reject next_entry_offset values that are smaller than
> sizeof(*src) or that leave fewer than sizeof(*src) bytes remaining
> after advancing.
>
> For calls where the server returns an EA list with an invalid
> next_entry_offset, the error returned to userspace changes from
> -ENODATA (getxattr) or -ERANGE (listxattr) to -EIO, correctly
> signalling a server protocol error rather than "attribute not present"
> or "output buffer too small".
>
> Fixes: 95907fea4fd8 ("cifs: Add support for reading attributes on SMB2+")
> Cc: stable@vger.kernel.org
> Signed-off-by: Frank Sorenson <sorenson@redhat.com>
> ---
> v2 changes:
>  -  reject next_entry_offset values that leave fewer than
>     sizeof(*src) bytes remainint after advancing.
Since maintainers might accidentally pick up the v1 patch, I think it
would be better to resend the entire patch-set as v2.

^ permalink raw reply	[flat|nested] 16+ messages in thread

* Re: [PATCH 00/11] smb: client: fix OOB reads and UAFs in SMB2/3 receive paths
  2026-08-25 23:54 ` [PATCH 00/11] smb: client: fix OOB reads and UAFs in SMB2/3 receive paths Yunseong Kim
@ 2026-08-26  2:18   ` Frank Sorenson
  0 siblings, 0 replies; 16+ messages in thread
From: Frank Sorenson @ 2026-08-26  2:18 UTC (permalink / raw)
  To: Yunseong Kim; +Cc: linux-cifs, pc, linkinjeon, yunseong.kim

On 8/25/26 6:54 PM, Yunseong Kim wrote:
> Hi Frank,
>
> Thank you, for your security works!
>
> On Sun, 23 Aug 2026 13:57:56 -0500 Frank Sorenson <sorenson@redhat.com> wrote:
...
> Just a small question: I wasn't able to verify the call stack from this
> patch series alone. Is there a reproducible test case or script that triggers
> the issue?
Not for most of them, although I'm sure I could come up with something.
> As a security researcher, I'd also like to independently verify the findings
> and cross-check the behavior on my side. Any reproducer or additional details
> would be greatly appreciated.
>
> The reason I'm asking is that I've been working on CI coverage for SMB. I hope
> we can integrate tests for this issue as well, so that similar regressions can
> be detected and prevented in the future.

I ran a static analysis tool on fs/smb/client looking for potential 
bounds issues, had Claude filter out false positives, then started 
looking at the findings for what could be fixed.

The bounds checker is at https://github.com/fsorenson/kernel_tools

# python3 bounds_checker/cli.py --kernel-source /home/src/linux 
--source-dir fs/smb/client

(add --llm to run the static analysis findings through an llm, but it'll 
really chew up your usage...  should work with Gemini & Claude, which 
it'll figure out from environment variables GOOGLE_CLOUD_PROJECT (Gemini 
via Vertex AI), GEMINI_API_KEY, ANTHROPIC_VERTEX_PROJECT_ID, 
ANTHROPIC_API_KEY)


Frank

-- 
Frank Sorenson
sorenson@redhat.com
Principal Software Maintenance Engineer, filesystems
Red Hat


^ permalink raw reply	[flat|nested] 16+ messages in thread

end of thread, other threads:[~2026-08-26  2:18 UTC | newest]

Thread overview: 16+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2026-08-23 18:57 [PATCH 00/11] smb: client: fix OOB reads and UAFs in SMB2/3 receive paths Frank Sorenson
2026-08-23 18:57 ` [PATCH 01/11] smb: client: fix NextCommand bounds and aliasing UAF in receive_encrypted_standard() Frank Sorenson
2026-08-23 18:57 ` [PATCH 02/11] smb: client: validate PDU length before smb2_get_data_area_len() struct access Frank Sorenson
2026-08-23 18:57 ` [PATCH 03/11] smb: client: fix server->total_read not tracking sub-PDU size in receive_encrypted_standard() Frank Sorenson
2026-08-23 18:58 ` [PATCH 04/11] smb: client: fix missing lower-bound check on DFS referral string offsets Frank Sorenson
2026-08-23 18:58 ` [PATCH 05/11] smb: client: fix missing lower-bound on Next field in parse_server_interfaces() Frank Sorenson
2026-08-23 18:58 ` [PATCH 06/11] smb: client: fix OOB struct field reads in move_smb2_ea_to_cifs() Frank Sorenson
2026-08-25 22:13   ` [PATCH v2] " Frank Sorenson
2026-08-26  0:59     ` Namjae Jeon
2026-08-23 18:58 ` [PATCH 07/11] smb: client: fix missing iov bounds check in parse_posix_sids() Frank Sorenson
2026-08-23 18:58 ` [PATCH 08/11] smb: client: fix underflow in is_valid_oplock_break() notify offset check Frank Sorenson
2026-08-23 18:58 ` [PATCH 09/11] smb: client: fix potential OOB read in smb3_enum_snapshots() Frank Sorenson
2026-08-23 18:58 ` [PATCH 10/11] smb: client: fix incomplete bounds check on reparse buffer in cifs_query_reparse_point() Frank Sorenson
2026-08-23 18:58 ` [PATCH 11/11] smb: client: fix NameOffset and Next field validation in smb2_parse_contexts() Frank Sorenson
2026-08-25 23:54 ` [PATCH 00/11] smb: client: fix OOB reads and UAFs in SMB2/3 receive paths Yunseong Kim
2026-08-26  2:18   ` Frank Sorenson

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox