Skip to content

Commit

Permalink
smb311: failure to open files of length 1040 when mounting with SMB3.…
Browse files Browse the repository at this point in the history
…1.1 POSIX extensions

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>
  • Loading branch information
Steve French committed Feb 19, 2025
1 parent 7330195 commit 9df2380
Show file tree
Hide file tree
Showing 4 changed files with 29 additions and 7 deletions.
1 change: 1 addition & 0 deletions fs/smb/client/cifsglob.h
Original file line number Diff line number Diff line change
Expand Up @@ -253,6 +253,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 {
Expand Down
28 changes: 22 additions & 6 deletions fs/smb/client/reparse.h
Original file line number Diff line number Diff line change
Expand Up @@ -99,14 +99,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;
}

Expand Down
4 changes: 4 additions & 0 deletions fs/smb/client/smb2inode.c
Original file line number Diff line number Diff line change
Expand Up @@ -650,6 +650,7 @@ static int smb2_compound_op(const unsigned int xid, struct cifs_tcon *tcon,
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)
Expand All @@ -673,6 +674,7 @@ static int smb2_compound_op(const unsigned int xid, struct cifs_tcon *tcon,
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)
Expand Down Expand Up @@ -770,6 +772,7 @@ static int smb2_compound_op(const unsigned int xid, struct cifs_tcon *tcon,
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);
Expand All @@ -791,6 +794,7 @@ static int smb2_compound_op(const unsigned int xid, struct cifs_tcon *tcon,
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);
Expand Down
3 changes: 2 additions & 1 deletion fs/smb/client/smb2ops.c
Original file line number Diff line number Diff line change
Expand Up @@ -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);
}

Expand Down Expand Up @@ -5146,7 +5147,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;
Expand Down

0 comments on commit 9df2380

Please sign in to comment.