Skip to contentSkip to conversation

FAILED: patch "[PATCH] smb: client: fix OOB struct field reads in" failed to apply to 6.12-stable tree

PATCH New [email protected] 2 participants 2 messages Sep 22

Subsystems (2)
lore
Share & tools
lore

[email protected]

author maintainer
#0 lore raw

The patch below does not apply to the 6.12-stable tree.
If someone wants it applied there, or to any other stable or longterm tree, then please email the backport, including the original git commit id to <[email protected]>.

To reproduce the conflict and resubmit, you may use the following commands:

git fetch https://git.kernel.org/pub/scm/linux/kernel/git/stable/linux.git/ linux-6.12.y
git checkout FETCH_HEAD
git cherry-pick -x eeb5ef6083e1cefa2ef75041b5597ff228b8d7bb
# <resolve conflicts, build, test, etc.>
git commit -s
git send-email --to '<[email protected]>' --in-reply-to '2026092223-straining-class-bc2a@gregkh' --subject-prefix 'PATCH 6.12.y' 'HEAD^..'

Possible dependencies:

thanks,

greg k-h

------------------ original commit in Linus's tree ------------------

From eeb5ef6083e1cefa2ef75041b5597ff228b8d7bb Mon Sep 17 00:00:00 2001
From: Frank Sorenson <[email protected]>
Date: Wed, 16 Sep 2026 16:33:57 -0500
Subject: [PATCH] smb: client: fix OOB struct field reads in
move_smb2_ea_to_cifs()

In move_smb2_ea_to_cifs(), the while (src_size > 0) loop condition is insufficient. It allows iteration to continue even if the remaining src_size is too small to contain a complete smb2_ea_info structure. Consequently, reads of ea_name_length and ea_value_length can occur out-of-bounds.

Fix this by ensuring src_size >= sizeof(*src) before attempting to read any structure fields. Additionally, reject any next_entry_offset that is smaller than sizeof(*src) or that would advance the pointer beyond the available buffer.

Note that for calls where the server returns a malformed EA list, the error returned to userspace changes from -ENODATA (getxattr) or -ERANGE (listxattr) to -EIO. This correctly signals a server protocol error rather than misleadingly indicating "attribute not present" or "output buffer too small".

Fixes 95907fea4fd8 ("cifs: Add support for reading attributes on SMB2+")
fs/smb/client/smb2ops.c +17 -8
index bda940cb3784..ee3c98e3f316 100644
@@ -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 */
fs/smb/client/trace.h +1 -0
index b442cccd1530..bb8d0197cb54 100644
@@ -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") \
maintainer
#1 lore raw

PATCH 1/1 smb: client: fix OOB struct field reads in move_smb2_ea_to_cifs()

From: Frank Sorenson <[email protected]>

[ Upstream commit eeb5ef6083e1cefa2ef75041b5597ff228b8d7bb ]

In move_smb2_ea_to_cifs(), the while (src_size > 0) loop condition is insufficient. It allows iteration to continue even if the remaining src_size is too small to contain a complete smb2_ea_info structure. Consequently, reads of ea_name_length and ea_value_length can occur out-of-bounds.

Fix this by ensuring src_size >= sizeof(*src) before attempting to read any structure fields. Additionally, reject any next_entry_offset that is smaller than sizeof(*src) or that would advance the pointer beyond the available buffer.

Note that for calls where the server returns a malformed EA list, the error returned to userspace changes from -ENODATA (getxattr) or -ERANGE (listxattr) to -EIO. This correctly signals a server protocol error rather than misleadingly indicating "attribute not present" or "output buffer too small".

Fixes 95907fea4fd8 ("cifs: Add support for reading attributes on SMB2+")
[ Replaced unavailable smb_EIO2() tracing calls with -EIO. ]
---
 fs/smb/client/smb2ops.c | 24 ++++++++++++++++--------
 1 file changed, 16 insertions(+), 8 deletions(-)
fs/smb/client/smb2ops.c +16 -8
index 3f6b8745667d0..ccbbddea8f386 100644
@@ -1057,8 +1057,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);
 
@@ -1113,14 +1114,21 @@ 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 = -EIO;
+			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 = -EIO;
+			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 */
Signature · 1 line
2.53.0