CIFS caches cifs.idmap and cifs.spnego keys in the module-owned keyrings that are only destroyed in module_exit(). Once keys pin the kernel module of the key type, the cached entries will retain CIFS pinned after the last SMB session has been torned down. Serialize the session count transitions with a mutex and clear the idmap and SPNEGO cache keyrings when the last session is freed. Signed-off-by: Jarkko Sakkinen --- fs/smb/client/cifs_spnego.c | 12 ++++++++++++ fs/smb/client/cifsacl.c | 12 ++++++++++++ fs/smb/client/cifsproto.h | 8 ++++++++ fs/smb/client/misc.c | 19 +++++++++++++++++-- 4 files changed, 49 insertions(+), 2 deletions(-) diff --git a/fs/smb/client/cifs_spnego.c b/fs/smb/client/cifs_spnego.c index 44c407275680..e974f396e07f 100644 --- a/fs/smb/client/cifs_spnego.c +++ b/fs/smb/client/cifs_spnego.c @@ -15,6 +15,7 @@ #include #include #include +#include #include "cifsglob.h" #include "cifs_spnego.h" #include "cifs_debug.h" @@ -190,6 +191,17 @@ cifs_get_spnego_key(struct cifs_ses *sesInfo, return spnego_key; } +void +cifs_spnego_clear_cache(void) +{ + int ret; + + ret = keyring_clear(spnego_cred->thread_keyring); + if (ret) + pr_warn_ratelimited("Failed to clear SPNEGO cache: %d; cache pins remain\n", + ret); +} + int init_cifs_spnego(void) { diff --git a/fs/smb/client/cifsacl.c b/fs/smb/client/cifsacl.c index 346899da91eb..60c60c23dd34 100644 --- a/fs/smb/client/cifsacl.c +++ b/fs/smb/client/cifsacl.c @@ -13,6 +13,7 @@ #include #include #include +#include #include #include #include @@ -539,6 +540,17 @@ sid_to_id(struct cifs_sb_info *cifs_sb, struct smb_sid *psid, return 0; } +void +cifs_idmap_clear_cache(void) +{ + int ret; + + ret = keyring_clear(root_cred->thread_keyring); + if (ret) + pr_warn_ratelimited("Failed to clear idmap cache: %d; cache pins remain\n", + ret); +} + int init_cifs_idmap(void) { diff --git a/fs/smb/client/cifsproto.h b/fs/smb/client/cifsproto.h index f9ea35597bc2..2930a40d1b67 100644 --- a/fs/smb/client/cifsproto.h +++ b/fs/smb/client/cifsproto.h @@ -58,8 +58,16 @@ do { \ } while (0) int init_cifs_idmap(void); void exit_cifs_idmap(void); +void cifs_idmap_clear_cache(void); int init_cifs_spnego(void); void exit_cifs_spnego(void); +#ifdef CONFIG_CIFS_UPCALL +void cifs_spnego_clear_cache(void); +#else +static inline void cifs_spnego_clear_cache(void) +{ +} +#endif const char *build_path_from_dentry(struct dentry *direntry, void *page); char *__build_path_from_dentry_optional_prefix(struct dentry *direntry, void *page, const char *tree, diff --git a/fs/smb/client/misc.c b/fs/smb/client/misc.c index ec28f102ea08..11c0330be489 100644 --- a/fs/smb/client/misc.c +++ b/fs/smb/client/misc.c @@ -9,6 +9,8 @@ #include #include #include +#include +#include #include #include "cifsglob.h" #include "cifsproto.h" @@ -33,6 +35,9 @@ struct tcon_list { struct cifs_tcon *tcon; }; +/* Serialize session admission with the last session's cache clear. */ +static DEFINE_MUTEX(cifs_session_mutex); + /* The xid serves as a useful identifier for each incoming vfs request, in a similar way to the mid which is useful to track each sent smb, and CurrentXid can also provide a running counter (although it @@ -74,7 +79,6 @@ sesInfoAlloc(void) ret_buf = kzalloc_obj(struct cifs_ses); if (ret_buf) { - atomic_inc(&sesInfoAllocCount); spin_lock_init(&ret_buf->ses_lock); ret_buf->ses_status = SES_NEW; ++ret_buf->ses_count; @@ -84,6 +88,10 @@ sesInfoAlloc(void) spin_lock_init(&ret_buf->iface_lock); INIT_LIST_HEAD(&ret_buf->iface_list); spin_lock_init(&ret_buf->chan_lock); + + mutex_lock(&cifs_session_mutex); + atomic_inc(&sesInfoAllocCount); + mutex_unlock(&cifs_session_mutex); } return ret_buf; } @@ -99,7 +107,6 @@ sesInfoFree(struct cifs_ses *buf_to_free) } unload_nls(buf_to_free->local_nls); - atomic_dec(&sesInfoAllocCount); kfree(buf_to_free->serverOS); kfree(buf_to_free->serverDomain); kfree(buf_to_free->serverNOS); @@ -115,6 +122,14 @@ sesInfoFree(struct cifs_ses *buf_to_free) kref_put(&iface->refcount, release_iface); spin_unlock(&buf_to_free->iface_lock); kfree_sensitive(buf_to_free); + + mutex_lock(&cifs_session_mutex); + if (atomic_dec_and_test(&sesInfoAllocCount)) { + cifs_idmap_clear_cache(); + cifs_spnego_clear_cache(); + key_gc_flush(); + } + mutex_unlock(&cifs_session_mutex); } struct cifs_tcon * -- 2.47.3