[PATCH v2] cifs: Remove intermediate object of failed create reparse call

Pali Rohár posted 1 patch 2 months ago
There is a newer version of this series
fs/smb/client/smb2inode.c | 22 ++++++++++++++++++++--
1 file changed, 20 insertions(+), 2 deletions(-)
[PATCH v2] cifs: Remove intermediate object of failed create reparse call
Posted by Pali Rohár 2 months ago
If CREATE was successful but SMB2_OP_SET_REPARSE failed then remove the
intermediate object created by CREATE. Otherwise empty object stay on the
server when reparse call failed.

This ensures that if the creating of special files is unsupported by the
server then no empty file stay on the server as a result of unsupported
operation.

Fixes: 102466f303ff ("smb: client: allow creating special files via reparse points")
Signed-off-by: Pali Rohár <pali@kernel.org>
---
Changes in v2:
* Increase out_buftype[] and out_iov[] members from 2 to 4 as required by smb2_compound_op
* Call free_rsp_buf() for all members of out_buftype[]/out_iov[]

I would like if you double check this smb2_compound_op() usage if there
is not some other memory issue. As V1 contained both memory leak and
buffer overflow (smb2_compound_op wrote out of those two arrays).

---
 fs/smb/client/smb2inode.c | 22 ++++++++++++++++++++--
 1 file changed, 20 insertions(+), 2 deletions(-)

diff --git a/fs/smb/client/smb2inode.c b/fs/smb/client/smb2inode.c
index 11a1c53c64e0..6e69a3b98be3 100644
--- a/fs/smb/client/smb2inode.c
+++ b/fs/smb/client/smb2inode.c
@@ -1205,9 +1205,12 @@ struct inode *smb2_get_reparse_inode(struct cifs_open_info_data *data,
 	struct cifs_sb_info *cifs_sb = CIFS_SB(sb);
 	struct cifsFileInfo *cfile;
 	struct inode *new = NULL;
+	int out_buftype[4] = {};
+	struct kvec out_iov[4] = {};
 	struct kvec in_iov[2];
 	int cmds[2];
 	int rc;
+	int i;
 
 	oparms = CIFS_OPARMS(cifs_sb, tcon, full_path,
 			     SYNCHRONIZE | DELETE |
@@ -1228,7 +1231,7 @@ struct inode *smb2_get_reparse_inode(struct cifs_open_info_data *data,
 		cmds[1] = SMB2_OP_POSIX_QUERY_INFO;
 		cifs_get_writable_path(tcon, full_path, FIND_WR_ANY, &cfile);
 		rc = smb2_compound_op(xid, tcon, cifs_sb, full_path, &oparms,
-				      in_iov, cmds, 2, cfile, NULL, NULL, NULL);
+				      in_iov, cmds, 2, cfile, out_iov, out_buftype, NULL);
 		if (!rc) {
 			rc = smb311_posix_get_inode_info(&new, full_path,
 							 data, sb, xid);
@@ -1237,12 +1240,27 @@ struct inode *smb2_get_reparse_inode(struct cifs_open_info_data *data,
 		cmds[1] = SMB2_OP_QUERY_INFO;
 		cifs_get_writable_path(tcon, full_path, FIND_WR_ANY, &cfile);
 		rc = smb2_compound_op(xid, tcon, cifs_sb, full_path, &oparms,
-				      in_iov, cmds, 2, cfile, NULL, NULL, NULL);
+				      in_iov, cmds, 2, cfile, out_iov, out_buftype, NULL);
 		if (!rc) {
 			rc = cifs_get_inode_info(&new, full_path,
 						 data, sb, xid, NULL);
 		}
 	}
+
+	if (rc) {
+		/*
+		 * If CREATE was successful but SMB2_OP_SET_REPARSE failed then
+		 * remove the intermediate object created by CREATE. Otherwise
+		 * empty object stay on the server when reparse call failed.
+		 */
+		if (((struct smb2_hdr *)out_iov[0].iov_base)->Status == STATUS_SUCCESS &&
+		    ((struct smb2_hdr *)out_iov[1].iov_base)->Status != STATUS_SUCCESS)
+			smb2_unlink(xid, tcon, full_path, cifs_sb, NULL);
+	}
+
+	for (i = 0; i < ARRAY_SIZE(out_buftype); i++)
+		free_rsp_buf(out_buftype[i], out_iov[i].iov_base);
+
 	return rc ? ERR_PTR(rc) : new;
 }
 
-- 
2.20.1

Re: [PATCH v2] cifs: Remove intermediate object of failed create reparse call
Posted by Steve French 2 months ago
Running regression tests on these currently.  Let me know if
additional patches to add for these experiments

1c2fcb28ce99 (HEAD -> for-next, origin/for-next, origin/HEAD) cifs: Do
not convert delimiter when parsing NFS-style symlinks
92484193d70a cifs: Validate content of NFS reparse point buffer
c77a8e49f2d3 cifs: Fix buffer overflow when parsing NFS reparse points
ab7d68fd4bcc cifs: Remove intermediate object of failed create reparse call
ab50485ea1b4 smb: Update comments about some reparse point tags
1600fe2d42a1 smb: client: stop flooding dmesg with automounts
f7a33d56e52f smb: client: stop flooding dmesg on failed session setups
e1b72ef3ba03 cifs: Check for UTF-16 null codepoint in SFU symlink
target location
9717d5343849 Merge tag 'v6.12-rc-ksmbd-server-fixes' of
git://git.samba.org/ksmbd

On Sun, Sep 29, 2024 at 9:04 AM Pali Rohár <pali@kernel.org> wrote:
>
> If CREATE was successful but SMB2_OP_SET_REPARSE failed then remove the
> intermediate object created by CREATE. Otherwise empty object stay on the
> server when reparse call failed.
>
> This ensures that if the creating of special files is unsupported by the
> server then no empty file stay on the server as a result of unsupported
> operation.
>
> Fixes: 102466f303ff ("smb: client: allow creating special files via reparse points")
> Signed-off-by: Pali Rohár <pali@kernel.org>
> ---
> Changes in v2:
> * Increase out_buftype[] and out_iov[] members from 2 to 4 as required by smb2_compound_op
> * Call free_rsp_buf() for all members of out_buftype[]/out_iov[]
>
> I would like if you double check this smb2_compound_op() usage if there
> is not some other memory issue. As V1 contained both memory leak and
> buffer overflow (smb2_compound_op wrote out of those two arrays).
>
> ---
>  fs/smb/client/smb2inode.c | 22 ++++++++++++++++++++--
>  1 file changed, 20 insertions(+), 2 deletions(-)
>
> diff --git a/fs/smb/client/smb2inode.c b/fs/smb/client/smb2inode.c
> index 11a1c53c64e0..6e69a3b98be3 100644
> --- a/fs/smb/client/smb2inode.c
> +++ b/fs/smb/client/smb2inode.c
> @@ -1205,9 +1205,12 @@ struct inode *smb2_get_reparse_inode(struct cifs_open_info_data *data,
>         struct cifs_sb_info *cifs_sb = CIFS_SB(sb);
>         struct cifsFileInfo *cfile;
>         struct inode *new = NULL;
> +       int out_buftype[4] = {};
> +       struct kvec out_iov[4] = {};
>         struct kvec in_iov[2];
>         int cmds[2];
>         int rc;
> +       int i;
>
>         oparms = CIFS_OPARMS(cifs_sb, tcon, full_path,
>                              SYNCHRONIZE | DELETE |
> @@ -1228,7 +1231,7 @@ struct inode *smb2_get_reparse_inode(struct cifs_open_info_data *data,
>                 cmds[1] = SMB2_OP_POSIX_QUERY_INFO;
>                 cifs_get_writable_path(tcon, full_path, FIND_WR_ANY, &cfile);
>                 rc = smb2_compound_op(xid, tcon, cifs_sb, full_path, &oparms,
> -                                     in_iov, cmds, 2, cfile, NULL, NULL, NULL);
> +                                     in_iov, cmds, 2, cfile, out_iov, out_buftype, NULL);
>                 if (!rc) {
>                         rc = smb311_posix_get_inode_info(&new, full_path,
>                                                          data, sb, xid);
> @@ -1237,12 +1240,27 @@ struct inode *smb2_get_reparse_inode(struct cifs_open_info_data *data,
>                 cmds[1] = SMB2_OP_QUERY_INFO;
>                 cifs_get_writable_path(tcon, full_path, FIND_WR_ANY, &cfile);
>                 rc = smb2_compound_op(xid, tcon, cifs_sb, full_path, &oparms,
> -                                     in_iov, cmds, 2, cfile, NULL, NULL, NULL);
> +                                     in_iov, cmds, 2, cfile, out_iov, out_buftype, NULL);
>                 if (!rc) {
>                         rc = cifs_get_inode_info(&new, full_path,
>                                                  data, sb, xid, NULL);
>                 }
>         }
> +
> +       if (rc) {
> +               /*
> +                * If CREATE was successful but SMB2_OP_SET_REPARSE failed then
> +                * remove the intermediate object created by CREATE. Otherwise
> +                * empty object stay on the server when reparse call failed.
> +                */
> +               if (((struct smb2_hdr *)out_iov[0].iov_base)->Status == STATUS_SUCCESS &&
> +                   ((struct smb2_hdr *)out_iov[1].iov_base)->Status != STATUS_SUCCESS)
> +                       smb2_unlink(xid, tcon, full_path, cifs_sb, NULL);
> +       }
> +
> +       for (i = 0; i < ARRAY_SIZE(out_buftype); i++)
> +               free_rsp_buf(out_buftype[i], out_iov[i].iov_base);
> +
>         return rc ? ERR_PTR(rc) : new;
>  }
>
> --
> 2.20.1
>
>


-- 
Thanks,

Steve