| From e02789a53d71334b067ad72eee5d4e88a0158083 Mon Sep 17 00:00:00 2001 |
| From: Steve French <stfrench@microsoft.com> |
| Date: Thu, 9 Aug 2018 14:33:12 -0500 |
| Subject: smb3: enumerating snapshots was leaving part of the data off end |
| |
| From: Steve French <stfrench@microsoft.com> |
| |
| 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 | 34 +++++++++++++++++++++++++++------- |
| 1 file changed, 27 insertions(+), 7 deletions(-) |
| |
| --- a/fs/cifs/smb2ops.c |
| +++ b/fs/cifs/smb2ops.c |
| @@ -1129,6 +1129,13 @@ smb3_set_integrity(const unsigned int xi |
| |
| } |
| |
| +/* 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 x |
| 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; |