public inbox for stable@vger.kernel.org
 help / color / mirror / Atom feed
* [PATCH] smb: client: fix OOB read in symlink error response parsing
@ 2026-04-14 11:50 Werner Kasselman
  2026-04-16  0:25 ` Henrique Carvalho
  0 siblings, 1 reply; 2+ messages in thread
From: Werner Kasselman @ 2026-04-14 11:50 UTC (permalink / raw)
  To: linux-cifs@vger.kernel.org
  Cc: sfrench@samba.org, pc@manguebit.org, ronniesahlberg@gmail.com,
	sprasad@microsoft.com, tom@talpey.com, bharathsm@microsoft.com,
	samba-technical@lists.samba.org, linux-kernel@vger.kernel.org,
	Werner Kasselman, stable@vger.kernel.org

symlink_data() walks server-supplied SMB2 error contexts to locate the
smb2_symlink_err_rsp before returning it to smb2_parse_symlink_response().
When ErrorContextCount is non-zero, sym can land at an attacker-chosen
offset past the smb2_err_rsp header, bounded only by iov_len.

Reads of p->ErrorId and p->ErrorDataLength in the walk loop occur
without checking that the smb2_error_context_rsp header fits in the
response buffer, and sym is dereferenced for SymLinkErrorTag/ReparseTag
without checking that sym itself fits.  A context header placed near
iov_end produces an OOB read.

The bounds check in smb2_parse_symlink_response() uses the compile-time
SMB2_SYMLINK_STRUCT_SIZE as the base for SubstituteName and PrintName
ranges.  That only matches the fixed layout when ErrorContextCount is
zero; with contexts, the actual PathBuffer offset in iov is larger, and
the read of sym->PathBuffer + sub_offs for sub_len bytes can extend
past iov_len into adjacent slab memory.  The copied bytes reach
userspace via readlink() on data->symlink_target.

STATUS_STOPPED_ON_SYMLINK responses are served from the 448-byte small
buffer pool, so the overread reliably crosses the slab object boundary.

Bound each context header during the walk, verify sym fits in the
response before dereferencing its length fields, and compute the
PathBuffer bound from sym->PathBuffer's actual offset into iov.

Fixes: 76894f3e2f71 ("cifs: improve symlink handling for smb2+")
Cc: stable@vger.kernel.org
Signed-off-by: Werner Kasselman <werner@verivus.com>
---
 fs/smb/client/smb2file.c | 22 +++++++++++++++++-----
 1 file changed, 17 insertions(+), 5 deletions(-)

diff --git a/fs/smb/client/smb2file.c b/fs/smb/client/smb2file.c
index ed651c946251..6fda8ec7fe9b 100644
--- a/fs/smb/client/smb2file.c
+++ b/fs/smb/client/smb2file.c
@@ -41,6 +41,8 @@ static struct smb2_symlink_err_rsp *symlink_data(const struct kvec *iov)
 		p = (struct smb2_error_context_rsp *)err->ErrorData;
 		end = (struct smb2_error_context_rsp *)((u8 *)err + iov->iov_len);
 		do {
+			if ((u8 *)p + sizeof(*p) > (u8 *)end)
+				return ERR_PTR(-EINVAL);
 			if (le32_to_cpu(p->ErrorId) == SMB2_ERROR_ID_DEFAULT) {
 				sym = (struct smb2_symlink_err_rsp *)p->ErrorContextData;
 				break;
@@ -56,9 +58,15 @@ static struct smb2_symlink_err_rsp *symlink_data(const struct kvec *iov)
 		sym = (struct smb2_symlink_err_rsp *)err->ErrorData;
 	}
 
-	if (!IS_ERR(sym) && (le32_to_cpu(sym->SymLinkErrorTag) != SYMLINK_ERROR_TAG ||
-			     le32_to_cpu(sym->ReparseTag) != IO_REPARSE_TAG_SYMLINK))
-		sym = ERR_PTR(-EINVAL);
+	if (IS_ERR(sym))
+		return sym;
+
+	if ((u8 *)sym + sizeof(*sym) > (u8 *)err + iov->iov_len)
+		return ERR_PTR(-EINVAL);
+
+	if (le32_to_cpu(sym->SymLinkErrorTag) != SYMLINK_ERROR_TAG ||
+	    le32_to_cpu(sym->ReparseTag) != IO_REPARSE_TAG_SYMLINK)
+		return ERR_PTR(-EINVAL);
 
 	return sym;
 }
@@ -115,6 +123,7 @@ int smb2_parse_symlink_response(struct cifs_sb_info *cifs_sb, const struct kvec
 	struct smb2_symlink_err_rsp *sym;
 	unsigned int sub_offs, sub_len;
 	unsigned int print_offs, print_len;
+	size_t pathbuf_off;
 
 	if (!cifs_sb || !iov || !iov->iov_base || !iov->iov_len || !path)
 		return -EINVAL;
@@ -128,8 +137,11 @@ int smb2_parse_symlink_response(struct cifs_sb_info *cifs_sb, const struct kvec
 	print_len = le16_to_cpu(sym->PrintNameLength);
 	print_offs = le16_to_cpu(sym->PrintNameOffset);
 
-	if (iov->iov_len < SMB2_SYMLINK_STRUCT_SIZE + sub_offs + sub_len ||
-	    iov->iov_len < SMB2_SYMLINK_STRUCT_SIZE + print_offs + print_len)
+	pathbuf_off = (const u8 *)sym->PathBuffer - (const u8 *)iov->iov_base;
+
+	if (pathbuf_off > iov->iov_len ||
+	    iov->iov_len - pathbuf_off < sub_offs + sub_len ||
+	    iov->iov_len - pathbuf_off < print_offs + print_len)
 		return -EINVAL;
 
 	return smb2_parse_native_symlink(path,
-- 
2.43.0


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

* Re: [PATCH] smb: client: fix OOB read in symlink error response parsing
  2026-04-14 11:50 [PATCH] smb: client: fix OOB read in symlink error response parsing Werner Kasselman
@ 2026-04-16  0:25 ` Henrique Carvalho
  0 siblings, 0 replies; 2+ messages in thread
From: Henrique Carvalho @ 2026-04-16  0:25 UTC (permalink / raw)
  To: Werner Kasselman
  Cc: linux-cifs@vger.kernel.org, sfrench@samba.org, pc@manguebit.org,
	ronniesahlberg@gmail.com, sprasad@microsoft.com, tom@talpey.com,
	bharathsm@microsoft.com, samba-technical@lists.samba.org,
	linux-kernel@vger.kernel.org, stable@vger.kernel.org

Isn't all this fixed by this patch?

https://lore.kernel.org/linux-cifs/2026040636-icy-constable-9e17@gregkh/

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

end of thread, other threads:[~2026-04-16  0:25 UTC | newest]

Thread overview: 2+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2026-04-14 11:50 [PATCH] smb: client: fix OOB read in symlink error response parsing Werner Kasselman
2026-04-16  0:25 ` Henrique Carvalho

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