public inbox for stable@vger.kernel.org
 help / color / mirror / Atom feed
From: Werner Kasselman <werner@verivus.ai>
To: "linux-cifs@vger.kernel.org" <linux-cifs@vger.kernel.org>
Cc: "sfrench@samba.org" <sfrench@samba.org>,
	"pc@manguebit.org" <pc@manguebit.org>,
	"ronniesahlberg@gmail.com" <ronniesahlberg@gmail.com>,
	"sprasad@microsoft.com" <sprasad@microsoft.com>,
	"tom@talpey.com" <tom@talpey.com>,
	"bharathsm@microsoft.com" <bharathsm@microsoft.com>,
	"samba-technical@lists.samba.org"
	<samba-technical@lists.samba.org>,
	"linux-kernel@vger.kernel.org" <linux-kernel@vger.kernel.org>,
	Werner Kasselman <werner@verivus.ai>,
	"stable@vger.kernel.org" <stable@vger.kernel.org>
Subject: [PATCH] smb: client: fix OOB read in symlink error response parsing
Date: Tue, 14 Apr 2026 11:50:42 +0000	[thread overview]
Message-ID: <20260414115040.552945-1-werner@verivus.com> (raw)

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


             reply	other threads:[~2026-04-14 11:50 UTC|newest]

Thread overview: 2+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2026-04-14 11:50 Werner Kasselman [this message]
2026-04-16  0:25 ` [PATCH] smb: client: fix OOB read in symlink error response parsing Henrique Carvalho

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=20260414115040.552945-1-werner@verivus.com \
    --to=werner@verivus.ai \
    --cc=bharathsm@microsoft.com \
    --cc=linux-cifs@vger.kernel.org \
    --cc=linux-kernel@vger.kernel.org \
    --cc=pc@manguebit.org \
    --cc=ronniesahlberg@gmail.com \
    --cc=samba-technical@lists.samba.org \
    --cc=sfrench@samba.org \
    --cc=sprasad@microsoft.com \
    --cc=stable@vger.kernel.org \
    --cc=tom@talpey.com \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox