[PATCH] smb: client: fix WARN_ON_ONCE() on malformed NFS reparse points

From: Yuanfu Xie

Date: Tue Sep 29 2026 - 02:20:46 EST


posix_reparse_to_fattr() re-checks fields taken straight from an
on-the-wire reparse_nfs_data_buffer and hits WARN_ON_ONCE() when
ReparseDataLength is too short or does not match the inode type, or
when the NFS inode type is unknown.

A malicious or buggy server can trigger any of the four warnings
with a plain stat() on a single name: parse_reparse_nfs() reports
the problem and rejects the buffer (-EIO or -EOPNOTSUPP), but
reparse_info_to_fattr() still builds the inode attributes from the
rejected buffer via cifs_open_info_to_fattr(), and
posix_reparse_to_fattr() warns on the very same server-controlled
data. With panic_on_warn this takes the whole system down.

parse_reparse_nfs() already logs each of these conditions, so fail
quietly instead of warning, without changing the existing error and
fallback behaviour.

Triggered on v6.13+ with panic_on_warn=1 by stat()ing a name for
which the server returns a CREATE response carrying
SMB2_CREATE_FLAG_REPARSE_POINT and FILE_ATTRIBUTE_REPARSE_POINT and
answers the following FSCTL_GET_REPARSE_POINT with an NFS reparse
buffer holding an unknown inode type (e.g. 0xdeadbeef):

CIFS: VFS: parse_reparse_nfs: unhandled inode type: 0xdeadbeef
------------[ cut here ]------------
WARNING: fs/smb/client/reparse.c:1267 at
cifs_reparse_point_to_fattr+0xd4e/0x1080
CPU: 0 UID: 0 PID: 896 Comm: stat Tainted: G N 7.3.0-rc4-00537-ga3ff15db6820
...
Call Trace:
cifs_open_info_to_fattr+0xa87/0x1520
reparse_info_to_fattr+0x5d1/0xdc0
cifs_get_fattr+0xa1d/0x1910
cifs_get_inode_info+0xc0/0x310
cifs_lookup+0x3a8/0xbd0
...
vfs_fstatat+0x77/0xe0
__do_sys_newfstatat+0x97/0x120

Cc: stable@xxxxxxxxxxxxxxx
Fixes: 6a832bc8bbb223 ("fs/smb/client: Implement new SMB3 POSIX type")
Signed-off-by: Yuanfu Xie <yuanfuxie@xxxxxxxxxxxxxx>
---

Hi Paulo, Namjae,

found this while testing a CIFS client against a server under my
control, so I'm sending the fix directly. Some notes that did
not belong in the commit message:

Still present in mainline v7.3-rc5 as of 2026-09-29; the code
shape first shipped in v6.13. A numbered recipe for the trigger
described in the commit message, on a v6.13+ client with
panic_on_warn=1:

1. mount a CIFS share from a server you control;
2. answer the client's SMB2 CREATE for some name with
STATUS_SUCCESS, SMB2_CREATE_FLAG_REPARSEPOINT and
FILE_ATTRIBUTE_REPARSE_POINT;
3. answer the following FSCTL_GET_REPARSE_POINT with an NFS
(IO_REPARSE_TAG_NFS) reparse buffer whose InodeType is none
of the NFS_SPECFILE_* values, e.g. 0xdeadbeef;
4. stat() that name.

Shrinking ReparseDataLength below sizeof(InodeType), or setting
InodeType to NFS_SPECFILE_CHR/BLK with a wrong trailing length,
reaches the other three warnings the same way.

I verified the warning fires with the recipe above on
7.3.0-rc4-00537-ga3ff15db6820 and never fires with the patch
applied, while the parse-time messages still appear.

fs/smb/client/reparse.c | 16 ++++++++--------
1 file changed, 8 insertions(+), 8 deletions(-)

diff --git a/fs/smb/client/reparse.c b/fs/smb/client/reparse.c
index 3a27773186ae4..e6ed00bc114c5 100644
--- a/fs/smb/client/reparse.c
+++ b/fs/smb/client/reparse.c
@@ -1232,25 +1232,26 @@ static bool posix_reparse_to_fattr(struct cifs_sb_info *cifs_sb,
if (buf == NULL)
return true;

+ /*
+ * parse_reparse_nfs() has already validated this buffer and
+ * reported anything wrong with it, but reparse_info_to_fattr()
+ * still builds attributes from the rejected buffer, so fail
+ * quietly on server-controlled data instead of warning.
+ */
if (le16_to_cpu(buf->ReparseDataLength) < sizeof(buf->InodeType)) {
- WARN_ON_ONCE(1);
return false;
}

switch (le64_to_cpu(buf->InodeType)) {
case NFS_SPECFILE_CHR:
- if (le16_to_cpu(buf->ReparseDataLength) != sizeof(buf->InodeType) + 8) {
- WARN_ON_ONCE(1);
+ if (le16_to_cpu(buf->ReparseDataLength) != sizeof(buf->InodeType) + 8)
return false;
- }
ftype = S_IFCHR;
fattr->cf_rdev = reparse_mkdev(buf->DataBuffer);
break;
case NFS_SPECFILE_BLK:
- if (le16_to_cpu(buf->ReparseDataLength) != sizeof(buf->InodeType) + 8) {
- WARN_ON_ONCE(1);
+ if (le16_to_cpu(buf->ReparseDataLength) != sizeof(buf->InodeType) + 8)
return false;
- }
ftype = S_IFBLK;
fattr->cf_rdev = reparse_mkdev(buf->DataBuffer);
break;
@@ -1264,7 +1265,6 @@ static bool posix_reparse_to_fattr(struct cifs_sb_info *cifs_sb,
ftype = S_IFLNK;
break;
default:
- WARN_ON_ONCE(1);
return false;
}
fattr->cf_mode = (fattr->cf_mode & ~S_IFMT) | ftype;
--
2.43.0