[PATCH 3/4] mm/secretmem: zeroize secret pages before kdump
Jan Sebastian Götte
linux at jaseg.de
Fri Jul 31 16:27:38 UTC 2026
Register a CRASH_ZEROIZE notifier that wipes secretmem folios. As a
result, when CONFIG_CRASH_ZEROIZE is set, secretmem areas will be
cleared before the kdump kernel is kexec'ed.
Zeroization runs after the other CPUs have been stopped, so the page
cache cannot be mutated concurrently and the xarray may be walked
without taking the i_pages lock. This is a best effort, defense in depth
measure. s_inode_list_lock is taken with trylock only. If a CPU was
stopped mid-modification the list may be inconsistent, and this late
into the panic path, there's nothing we can do about it.
Signed-off-by: Jan Sebastian Götte <linux at jaseg.de>
---
mm/secretmem.c | 50 ++++++++++++++++++++++++++++++++++++++++++++++++++
1 file changed, 50 insertions(+)
diff --git a/mm/secretmem.c b/mm/secretmem.c
index d29865075b6e..53f629d6633c 100644
--- a/mm/secretmem.c
+++ b/mm/secretmem.c
@@ -13,9 +13,11 @@
#include <linux/bitops.h>
#include <linux/printk.h>
#include <linux/pagemap.h>
+#include <linux/notifier.h>
#include <linux/syscalls.h>
#include <linux/pseudo_fs.h>
#include <linux/secretmem.h>
+#include <linux/crash_core.h>
#include <linux/set_memory.h>
#include <linux/sched/signal.h>
@@ -187,6 +189,50 @@ static const struct inode_operations secretmem_iops = {
static struct vfsmount *secretmem_mnt;
+#ifdef CONFIG_CRASH_ZEROIZE
+/* Called far into vpanic from crash_core.c with other CPUs stopped and
+ * preemption disabled
+ */
+static int secretmem_crash_zeroize(struct notifier_block *nb, unsigned long
+ action, void *data)
+{
+ struct super_block *sb;
+ struct inode *inode;
+
+ if (!secretmem_mnt)
+ return NOTIFY_DONE;
+ sb = secretmem_mnt->mnt_sb;
+
+ /* If the list was modified in the exact moment we panic'ed, it might be
+ * in an inconsistent state that would be unsafe to iterate. If we can't
+ * get the lock, too bad, that's all we can do here.
+ */
+ if (!spin_trylock(&sb->s_inode_list_lock)) {
+ pr_crit("crash_zeroize: can't acquire secretmem superblock lock.\n"
+ "crash_zeroize: skipping zeroizing secretmem.\n");
+ return NOTIFY_DONE;
+ }
+
+ list_for_each_entry(inode, &sb->s_inodes, i_sb_list) {
+ XA_STATE(xas, &inode->i_mapping->i_pages, 0);
+ struct folio *folio;
+
+ /* no need for locks if we're burning down the house :) */
+ xas_for_each(&xas, folio, ULONG_MAX) {
+ if (xas_retry(&xas, folio) || xa_is_value(folio))
+ continue;
+ inode->i_mapping->a_ops->free_folio(folio);
+ }
+ }
+ /* off to kexec()! */
+ return NOTIFY_DONE;
+}
+
+static struct notifier_block secretmem_zeroize_nb = {
+ .notifier_call = secretmem_crash_zeroize
+};
+#endif /* CONFIG_CRASH_ZEROIZE */
+
static struct file *secretmem_file_create(unsigned long flags)
{
struct file *file;
@@ -263,6 +309,10 @@ static int __init secretmem_init(void)
if (IS_ERR(secretmem_mnt))
return PTR_ERR(secretmem_mnt);
+#ifdef CONFIG_CRASH_ZEROIZE
+ atomic_notifier_chain_register(&crash_zeroize_notifier_list, &secretmem_zeroize_nb);
+#endif
+
return 0;
}
fs_initcall(secretmem_init);
--
2.53.0
More information about the Linux-security-module-archive
mailing list