summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorEnzo Matsumiya <ematsumiya@suse.de>2021-09-09 18:46:45 -0300
committerSteve French <stfrench@microsoft.com>2021-09-09 17:34:38 -0500
commit9351590f51cdda49d0265932a37f099950998504 (patch)
treec6ef9ed51281ca9388482d589a448a83ac9c0810
parent8d014f5fe98142b79dfa3bcd0d9483a5165f3570 (diff)
downloadlinux-9351590f51cdda49d0265932a37f099950998504.tar.gz
linux-9351590f51cdda49d0265932a37f099950998504.tar.xz
cifs: properly invalidate cached root handle when closing it
Cached root file was not being completely invalidated sometimes. Reproducing: - With a DFS share with 2 targets, one disabled and one enabled - start some I/O on the mount # while true; do ls /mnt/dfs; done - at the same time, disable the enabled target and enable the disabled one - wait for DFS cache to expire - on reconnect, the previous cached root handle should be invalid, but open_cached_dir_by_dentry() will still try to use it, but throws a use-after-free warning (kref_get()) Make smb2_close_cached_fid() invalidate all fields every time, but only send an SMB2_close() when the entry is still valid. Signed-off-by: Enzo Matsumiya <ematsumiya@suse.de> Reviewed-by: Paulo Alcantara (SUSE) <pc@cjr.nz> Signed-off-by: Steve French <stfrench@microsoft.com>
-rw-r--r--fs/cifs/smb2ops.c20
1 files changed, 13 insertions, 7 deletions
diff --git a/fs/cifs/smb2ops.c b/fs/cifs/smb2ops.c
index ddc0e8f97872..bda606dc72b1 100644
--- a/fs/cifs/smb2ops.c
+++ b/fs/cifs/smb2ops.c
@@ -689,13 +689,19 @@ smb2_close_cached_fid(struct kref *ref)
cifs_dbg(FYI, "clear cached root file handle\n");
SMB2_close(0, cfid->tcon, cfid->fid->persistent_fid,
cfid->fid->volatile_fid);
- cfid->is_valid = false;
- cfid->file_all_info_is_valid = false;
- cfid->has_lease = false;
- if (cfid->dentry) {
- dput(cfid->dentry);
- cfid->dentry = NULL;
- }
+ }
+
+ /*
+ * We only check validity above to send SMB2_close,
+ * but we still need to invalidate these entries
+ * when this function is called
+ */
+ cfid->is_valid = false;
+ cfid->file_all_info_is_valid = false;
+ cfid->has_lease = false;
+ if (cfid->dentry) {
+ dput(cfid->dentry);
+ cfid->dentry = NULL;
}
}