[RFC PATCH v2 05/11] smb: client: Clear idmap/SPNEGO caches when the last session is unloaded
From: Jarkko Sakkinen
Date: Mon Nov 02 2026 - 22:03:56 EST
cifs caches cifs.idmap and cifs.spnego keys in module-owned thread
keyrings that are only destroyed in module_exit(). Once live keys pin
the key type, cached entries keep cifs pinned after the last SMB session
is torn down.
Serialize 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 <jarkko@xxxxxxxxxx>
---
fs/smb/client/cifs_debug.c | 2 +-
fs/smb/client/cifs_spnego.c | 8 ++++++++
fs/smb/client/cifsacl.c | 8 ++++++++
fs/smb/client/cifsfs.c | 4 ++--
fs/smb/client/cifsglob.h | 2 +-
fs/smb/client/cifsproto.h | 8 ++++++++
fs/smb/client/misc.c | 19 +++++++++++++++++--
7 files changed, 45 insertions(+), 6 deletions(-)
diff --git a/fs/smb/client/cifs_debug.c b/fs/smb/client/cifs_debug.c
index 577f0ddede90..2eecd2516dfe 100644
--- a/fs/smb/client/cifs_debug.c
+++ b/fs/smb/client/cifs_debug.c
@@ -790,7 +790,7 @@ static int cifs_stats_proc_show(struct seq_file *m, void *v)
struct cifs_tcon *tcon;
seq_printf(m, "Resources in use\nCIFS Session: %d\n",
- sesInfoAllocCount.counter);
+ cifs_ses_alloc_count.counter);
seq_printf(m, "Share (unique mount targets): %d\n",
tconInfoAllocCount.counter);
seq_printf(m, "SMB Request/Response Buffer: %d Pool size: %d\n",
diff --git a/fs/smb/client/cifs_spnego.c b/fs/smb/client/cifs_spnego.c
index 44c407275680..3bd272c7d9fe 100644
--- a/fs/smb/client/cifs_spnego.c
+++ b/fs/smb/client/cifs_spnego.c
@@ -242,6 +242,14 @@ init_cifs_spnego(void)
return ret;
}
+void cifs_spnego_clear_cache(void)
+{
+ int ret;
+
+ ret = keyring_clear(spnego_cred->thread_keyring);
+ WARN_ON(ret);
+}
+
void
exit_cifs_spnego(void)
{
diff --git a/fs/smb/client/cifsacl.c b/fs/smb/client/cifsacl.c
index 346899da91eb..7504cedf6417 100644
--- a/fs/smb/client/cifsacl.c
+++ b/fs/smb/client/cifsacl.c
@@ -590,6 +590,14 @@ init_cifs_idmap(void)
return ret;
}
+void cifs_idmap_clear_cache(void)
+{
+ int ret;
+
+ ret = keyring_clear(root_cred->thread_keyring);
+ WARN_ON(ret);
+}
+
void
exit_cifs_idmap(void)
{
diff --git a/fs/smb/client/cifsfs.c b/fs/smb/client/cifsfs.c
index 9788052599ce..f8a9a6f7dc49 100644
--- a/fs/smb/client/cifsfs.c
+++ b/fs/smb/client/cifsfs.c
@@ -84,7 +84,7 @@ DEFINE_SPINLOCK(GlobalMid_Lock); /* protects above & list operations on midQ ent
/*
* Global counters, updated atomically
*/
-atomic_t sesInfoAllocCount;
+atomic_t cifs_ses_alloc_count;
atomic_t tconInfoAllocCount;
atomic_t tcpSesNextId;
atomic_t tcpSesAllocCount;
@@ -2024,7 +2024,7 @@ init_cifs(void)
/*
* Initialize Global counters
*/
- atomic_set(&sesInfoAllocCount, 0);
+ atomic_set(&cifs_ses_alloc_count, 0);
atomic_set(&tconInfoAllocCount, 0);
atomic_set(&tcpSesNextId, 0);
atomic_set(&tcpSesAllocCount, 0);
diff --git a/fs/smb/client/cifsglob.h b/fs/smb/client/cifsglob.h
index d3ca688b6aba..c6e8d1340517 100644
--- a/fs/smb/client/cifsglob.h
+++ b/fs/smb/client/cifsglob.h
@@ -2095,7 +2095,7 @@ extern spinlock_t GlobalMid_Lock; /* protects above & list operations on midQ en
/*
* Global counters, updated atomically
*/
-extern atomic_t sesInfoAllocCount;
+extern atomic_t cifs_ses_alloc_count;
extern atomic_t tconInfoAllocCount;
extern atomic_t tcpSesNextId;
extern atomic_t tcpSesAllocCount;
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..4503434942f9 100644
--- a/fs/smb/client/misc.c
+++ b/fs/smb/client/misc.c
@@ -9,6 +9,8 @@
#include <linux/slab.h>
#include <linux/ctype.h>
#include <linux/mempool.h>
+#include <linux/key.h>
+#include <linux/mutex.h>
#include <linux/vmalloc.h>
#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(&cifs_ses_alloc_count);
+ 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(&cifs_ses_alloc_count)) {
+ cifs_idmap_clear_cache();
+ cifs_spnego_clear_cache();
+ key_gc_flush();
+ }
+ mutex_unlock(&cifs_session_mutex);
}
struct cifs_tcon *
--
2.47.3