diff options
author | Enzo Matsumiya <ematsumiya@suse.de> | 2021-09-09 18:46:45 -0300 |
---|---|---|
committer | Greg Kroah-Hartman <gregkh@linuxfoundation.org> | 2021-09-26 14:10:25 +0200 |
commit | d1217e40d082ebec361ba66852d2b3aa1aa854ae (patch) | |
tree | 34e4543fa5613dbb2174ea533f2e0eeff67ea5ca /fs | |
parent | cacfce79af9bc0f03a3b33377718d76a138651f5 (diff) |
cifs: properly invalidate cached root handle when closing it
[ Upstream commit 9351590f51cdda49d0265932a37f099950998504 ]
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>
Signed-off-by: Sasha Levin <sashal@kernel.org>
Diffstat (limited to 'fs')
-rw-r--r-- | fs/cifs/smb2ops.c | 20 |
1 files changed, 13 insertions, 7 deletions
diff --git a/fs/cifs/smb2ops.c b/fs/cifs/smb2ops.c index 2dfd0d8297eb..1b9de38a136a 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; } } |