]> git.ipfire.org Git - thirdparty/kernel/stable.git/commitdiff
smb311: failure to open files of length 1040 when mounting with SMB3.1.1 POSIX extensions
authorSteve French <stfrench@microsoft.com>
Mon, 17 Feb 2025 04:17:54 +0000 (22:17 -0600)
committerGreg Kroah-Hartman <gregkh@linuxfoundation.org>
Thu, 13 Mar 2025 12:01:40 +0000 (13:01 +0100)
[ Upstream commit 9df23801c83d3e12b4c09be39d37d2be385e52f9 ]

If a file size has bits 0x410 = ATTR_DIRECTORY | ATTR_REPARSE set
then during queryinfo (stat) the file is regarded as a directory
and subsequent opens can fail. A simple test example is trying
to open any file 1040 bytes long when mounting with "posix"
(SMB3.1.1 POSIX/Linux Extensions).

The cause of this bug is that Attributes field in smb2_file_all_info
struct occupies the same place that EndOfFile field in
smb311_posix_qinfo, and sometimes the latter struct is incorrectly
processed as if it was the first one.

Reported-by: Oleh Nykyforchyn <oleh.nyk@gmail.com>
Tested-by: Oleh Nykyforchyn <oleh.nyk@gmail.com>
Acked-by: Paulo Alcantara (Red Hat) <pc@manguebit.com>
Cc: stable@vger.kernel.org
Signed-off-by: Steve French <stfrench@microsoft.com>
Signed-off-by: Sasha Levin <sashal@kernel.org>
fs/smb/client/cifsglob.h
fs/smb/client/reparse.h
fs/smb/client/smb2inode.c
fs/smb/client/smb2ops.c

index 0979feb30bedba88b4c14d00b68e82a4c60be0bc..b630beb757a44aee17103a65feb91656409e6f3b 100644 (file)
@@ -210,6 +210,7 @@ struct cifs_cred {
 struct cifs_open_info_data {
        bool adjust_tz;
        bool reparse_point;
+       bool contains_posix_file_info;
        struct {
                /* ioctl response buffer */
                struct {
index ff05b0e75c92840668b3798843c8d624b4dcd498..f080f92cb1e741eac7e5dafd35dc5752904900b1 100644 (file)
@@ -97,14 +97,30 @@ static inline bool reparse_inode_match(struct inode *inode,
 
 static inline bool cifs_open_data_reparse(struct cifs_open_info_data *data)
 {
-       struct smb2_file_all_info *fi = &data->fi;
-       u32 attrs = le32_to_cpu(fi->Attributes);
+       u32 attrs;
        bool ret;
 
-       ret = data->reparse_point || (attrs & ATTR_REPARSE);
-       if (ret)
-               attrs |= ATTR_REPARSE;
-       fi->Attributes = cpu_to_le32(attrs);
+       if (data->contains_posix_file_info) {
+               struct smb311_posix_qinfo *fi = &data->posix_fi;
+
+               attrs = le32_to_cpu(fi->DosAttributes);
+               if (data->reparse_point) {
+                       attrs |= ATTR_REPARSE;
+                       fi->DosAttributes = cpu_to_le32(attrs);
+               }
+
+       } else {
+               struct smb2_file_all_info *fi = &data->fi;
+
+               attrs = le32_to_cpu(fi->Attributes);
+               if (data->reparse_point) {
+                       attrs |= ATTR_REPARSE;
+                       fi->Attributes = cpu_to_le32(attrs);
+               }
+       }
+
+       ret = attrs & ATTR_REPARSE;
+
        return ret;
 }
 
index 7dfd3eb3847b3355a742971f732f0f7b9cb49122..6048b3fed3e787065628f149b9702fef14b4db70 100644 (file)
@@ -648,6 +648,7 @@ finished:
                switch (cmds[i]) {
                case SMB2_OP_QUERY_INFO:
                        idata = in_iov[i].iov_base;
+                       idata->contains_posix_file_info = false;
                        if (rc == 0 && cfile && cfile->symlink_target) {
                                idata->symlink_target = kstrdup(cfile->symlink_target, GFP_KERNEL);
                                if (!idata->symlink_target)
@@ -671,6 +672,7 @@ finished:
                        break;
                case SMB2_OP_POSIX_QUERY_INFO:
                        idata = in_iov[i].iov_base;
+                       idata->contains_posix_file_info = true;
                        if (rc == 0 && cfile && cfile->symlink_target) {
                                idata->symlink_target = kstrdup(cfile->symlink_target, GFP_KERNEL);
                                if (!idata->symlink_target)
@@ -768,6 +770,7 @@ finished:
                                idata = in_iov[i].iov_base;
                                idata->reparse.io.iov = *iov;
                                idata->reparse.io.buftype = resp_buftype[i + 1];
+                               idata->contains_posix_file_info = false; /* BB VERIFY */
                                rbuf = reparse_buf_ptr(iov);
                                if (IS_ERR(rbuf)) {
                                        rc = PTR_ERR(rbuf);
@@ -789,6 +792,7 @@ finished:
                case SMB2_OP_QUERY_WSL_EA:
                        if (!rc) {
                                idata = in_iov[i].iov_base;
+                               idata->contains_posix_file_info = false;
                                qi_rsp = rsp_iov[i + 1].iov_base;
                                data[0] = (u8 *)qi_rsp + le16_to_cpu(qi_rsp->OutputBufferOffset);
                                size[0] = le32_to_cpu(qi_rsp->OutputBufferLength);
index e8da63d29a28f16ff3924559d02ef53060df30e5..516be8c0b2a9b41a61778872900a74d1565feb8f 100644 (file)
@@ -1001,6 +1001,7 @@ static int smb2_query_file_info(const unsigned int xid, struct cifs_tcon *tcon,
                if (!data->symlink_target)
                        return -ENOMEM;
        }
+       data->contains_posix_file_info = false;
        return SMB2_query_info(xid, tcon, fid->persistent_fid, fid->volatile_fid, &data->fi);
 }
 
@@ -5177,7 +5178,7 @@ int __cifs_sfu_make_node(unsigned int xid, struct inode *inode,
                             FILE_CREATE, CREATE_NOT_DIR |
                             CREATE_OPTION_SPECIAL, ACL_NO_MODE);
        oparms.fid = &fid;
-
+       idata.contains_posix_file_info = false;
        rc = server->ops->open(xid, &oparms, &oplock, &idata);
        if (rc)
                goto out;