smb3: enumerating snapshots was leaving part of the data off end
authorSteve French <stfrench@microsoft.com>
Thu, 9 Aug 2018 19:33:12 +0000 (14:33 -0500)
committerGreg Kroah-Hartman <gregkh@linuxfoundation.org>
Wed, 5 Sep 2018 07:26:33 +0000 (09:26 +0200)
commit e02789a53d71334b067ad72eee5d4e88a0158083 upstream.

When enumerating snapshots, the last few bytes of the final
snapshot could be left off since we were miscalculating the
length returned (leaving off the sizeof struct SRV_SNAPSHOT_ARRAY)
See MS-SMB2 section 2.2.32.2. In addition fixup the length used
to allow smaller buffer to be passed in, in order to allow
returning the size of the whole snapshot array more easily.

Sample userspace output with a kernel patched with this
(mounted to a Windows volume with two snapshots).
Before this patch, the second snapshot would be missing a
few bytes at the end.

~/cifs-2.6# ~/enum-snapshots /mnt/file
press enter to issue the ioctl to retrieve snapshot information ...

size of snapshot array = 102
Num snapshots: 2 Num returned: 2 Array Size: 102

Snapshot 0:@GMT-2018.06.30-19.34.17
Snapshot 1:@GMT-2018.06.30-19.33.37

CC: Stable <stable@vger.kernel.org>
Signed-off-by: Steve French <stfrench@microsoft.com>
Reviewed-by: Pavel Shilovsky <pshilov@microsoft.com>
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
fs/cifs/smb2ops.c

index 83267ac3a3f0274fdc936e42bf786c4f5aa581b4..badbb50140b1d727be58fb97253ca86210d14992 100644 (file)
@@ -1129,6 +1129,13 @@ smb3_set_integrity(const unsigned int xid, struct cifs_tcon *tcon,
 
 }
 
+/* GMT Token is @GMT-YYYY.MM.DD-HH.MM.SS Unicode which is 48 bytes + null */
+#define GMT_TOKEN_SIZE 50
+
+/*
+ * Input buffer contains (empty) struct smb_snapshot array with size filled in
+ * For output see struct SRV_SNAPSHOT_ARRAY in MS-SMB2 section 2.2.32.2
+ */
 static int
 smb3_enum_snapshots(const unsigned int xid, struct cifs_tcon *tcon,
                   struct cifsFileInfo *cfile, void __user *ioc_buf)
@@ -1158,14 +1165,27 @@ smb3_enum_snapshots(const unsigned int xid, struct cifs_tcon *tcon,
                        kfree(retbuf);
                        return rc;
                }
-               if (snapshot_in.snapshot_array_size < sizeof(struct smb_snapshot_array)) {
-                       rc = -ERANGE;
-                       kfree(retbuf);
-                       return rc;
-               }
 
-               if (ret_data_len > snapshot_in.snapshot_array_size)
-                       ret_data_len = snapshot_in.snapshot_array_size;
+               /*
+                * Check for min size, ie not large enough to fit even one GMT
+                * token (snapshot).  On the first ioctl some users may pass in
+                * smaller size (or zero) to simply get the size of the array
+                * so the user space caller can allocate sufficient memory
+                * and retry the ioctl again with larger array size sufficient
+                * to hold all of the snapshot GMT tokens on the second try.
+                */
+               if (snapshot_in.snapshot_array_size < GMT_TOKEN_SIZE)
+                       ret_data_len = sizeof(struct smb_snapshot_array);
+
+               /*
+                * We return struct SRV_SNAPSHOT_ARRAY, followed by
+                * the snapshot array (of 50 byte GMT tokens) each
+                * representing an available previous version of the data
+                */
+               if (ret_data_len > (snapshot_in.snapshot_array_size +
+                                       sizeof(struct smb_snapshot_array)))
+                       ret_data_len = snapshot_in.snapshot_array_size +
+                                       sizeof(struct smb_snapshot_array);
 
                if (copy_to_user(ioc_buf, retbuf, ret_data_len))
                        rc = -EFAULT;