* [PATCH v2 00/13] CRASH_WIPE_SECRETS: Wipe secrets before kdump (was: CRASH_ZEROIZE)
@ 2026-08-11 17:52 Jan Sebastian Götte
2026-08-11 17:52 ` [PATCH v2 01/13] kexec: add CRASH_WIPE_SECRETS to wipe secrets before kdump Jan Sebastian Götte
` (13 more replies)
0 siblings, 14 replies; 15+ messages in thread
From: Jan Sebastian Götte @ 2026-08-11 17:52 UTC (permalink / raw)
To: Andrew Morton, Baoquan He, Mike Rapoport, Pasha Tatashin,
Pratyush Yadav, Dave Young, Catalin Marinas, Will Deacon,
David Howells, Jarkko Sakkinen, Jonathan Corbet, Shuah Khan,
Paul Moore, James Morris, Serge E. Hallyn, Lukas Wunner,
Ignat Korchagin, Herbert Xu, David S. Miller, Keith Busch,
Jens Axboe, Christoph Hellwig, Sagi Grimberg, Trond Myklebust,
Anna Schumaker, Mimi Zohar, James Bottomley, Marc Dionne,
Eric Dumazet, Jakub Kicinski, Paolo Abeni, Simon Horman,
Eric Biggers, Theodore Y. Ts'o, Jaegeuk Kim, Alexander Viro,
Christian Brauner, Jan Kara, Alasdair Kergon, Mike Snitzer,
Mikulas Patocka, Benjamin Marzinski
Cc: kexec, linux-kernel, linux-arm-kernel, linux-mm, keyrings,
linux-doc, linux-security-module, linux-crypto, linux-nvme,
linux-nfs, linux-integrity, linux-afs, netdev, linux-fscrypt,
linux-fsdevel, dm-devel, Jan Sebastian Götte
I'm using linux on an embedded target in a Hardware Security Module-like
application. One requirement is that I want the system to be able to
quickly erase its memory when it detects physical tampering. I'm
approaching that by using kdump to load into a small payload that
instead of dumping RAM, erases RAM from start to end. However, writing
all of RAM, especially on an embedded target, is rather slow. For this
reason, I propose the mechanism in this patch series:
Add CONFIG_CRASH_ZEROIZE (default off), which when enabled makes various
subsystems handling secret data do a quick, targeted wipe of these
secrets before kdump. This behavior might also be interesting in cases
where you run a normal kdump kernel but you still want to keep things
like fde crypto keys out of these dumps.
CONFIG_CRASH_ZEROIZE is a best effort, defense in depth solution. There
are circumstances, such as when a panic is triggered after memory
corruption, or when a panic interrupts some operation that mutates data
structures under locks, when the kernel cannot safely wipe some memory
areas. The handlers proposed in this series will just print a warning
and skip the affected areas in this case.
This series introduces handlers for the major locations I found where
having this sort of thing makes sense. Notable omissions right now are
the Ceph and CIFS subsystems. I have WIP patches for these, but since I
can't easily test them right now, I omitted them from this patch set for
now. Currently included locations are:
* various key types in security/keys
* rxrpc
* fscrypt
* dm-crypt
* crypto tfm instances
* secretmem (which I'm going to start using in my application)
I've verified this patch series on an ARM64 target using the helper code
at https://codeberg.org/yasec/crash-wipe-test . This code stuffs the
affected kernel subsystems with keys and secret data, then crashes the
system, takes a RAM dump and verifies the dump is clean of secrets. Note
that the helper code is partially LLM-generated, so read with care. It
passes a positive control test with the config option disabled.
The patch series applies on top of linux-next but should work on 7.0.0,
too. I've tested the patches on a Arduino uno Q (Qualcomm QRB2210,
ARM64) embedded target.
Signed-off-by: Jan Sebastian Götte <linux@jaseg.de>
---
Changes in v2:
- New keyring key types
- New handlers: rxrpc, fscrypt, dm-crypt and crypto tfm
- Renamed from "zeroize" to "wipe"
- Add ARM64-specific cache flush logic
- Link to v1: https://patch.msgid.link/20260731162739.158320-1-linux@jaseg.de
To: Andrew Morton <akpm@linux-foundation.org>
To: Baoquan He <baoquan.he@linux.dev>
To: Mike Rapoport <rppt@kernel.org>
To: Pasha Tatashin <pasha.tatashin@soleen.com>
To: Pratyush Yadav <pratyush@kernel.org>
To: Dave Young <ruirui.yang@linux.dev>
To: Catalin Marinas <catalin.marinas@arm.com>
To: Will Deacon <will@kernel.org>
To: David Howells <dhowells@redhat.com>
To: Jarkko Sakkinen <jarkko@kernel.org>
To: Jonathan Corbet <corbet@lwn.net>
To: Shuah Khan <skhan@linuxfoundation.org>
To: Paul Moore <paul@paul-moore.com>
To: James Morris <jmorris@namei.org>
To: "Serge E. Hallyn" <serge@hallyn.com>
To: Lukas Wunner <lukas@wunner.de>
To: Ignat Korchagin <ignat@linux.win>
To: Herbert Xu <herbert@gondor.apana.org.au>
To: "David S. Miller" <davem@davemloft.net>
To: Keith Busch <kbusch@kernel.org>
To: Jens Axboe <axboe@kernel.dk>
To: Christoph Hellwig <hch@lst.de>
To: Sagi Grimberg <sagi@grimberg.me>
To: Trond Myklebust <trondmy@kernel.org>
To: Anna Schumaker <anna@kernel.org>
To: Mimi Zohar <zohar@linux.ibm.com>
To: James Bottomley <James.Bottomley@HansenPartnership.com>
To: Marc Dionne <marc.dionne@auristor.com>
To: Eric Dumazet <edumazet@google.com>
To: Jakub Kicinski <kuba@kernel.org>
To: Paolo Abeni <pabeni@redhat.com>
To: Simon Horman <horms@kernel.org>
To: Eric Biggers <ebiggers@kernel.org>
To: "Theodore Y. Ts'o" <tytso@mit.edu>
To: Jaegeuk Kim <jaegeuk@kernel.org>
To: Alexander Viro <viro@zeniv.linux.org.uk>
To: Christian Brauner <brauner@kernel.org>
To: Jan Kara <jack@suse.cz>
To: Alasdair Kergon <agk@redhat.com>
To: Mike Snitzer <snitzer@kernel.org>
To: Mikulas Patocka <mpatocka@redhat.com>
To: Benjamin Marzinski <bmarzins@redhat.com>
Cc: kexec@lists.infradead.org
Cc: linux-kernel@vger.kernel.org
Cc: linux-arm-kernel@lists.infradead.org
Cc: linux-mm@kvack.org
Cc: keyrings@vger.kernel.org
Cc: linux-doc@vger.kernel.org
Cc: linux-security-module@vger.kernel.org
Cc: linux-crypto@vger.kernel.org
Cc: linux-nvme@lists.infradead.org
Cc: linux-nfs@vger.kernel.org
Cc: linux-integrity@vger.kernel.org
Cc: linux-afs@lists.infradead.org
Cc: netdev@vger.kernel.org
Cc: linux-fscrypt@vger.kernel.org
Cc: linux-fsdevel@vger.kernel.org
Cc: dm-devel@lists.linux.dev
---
Jan Sebastian Götte (13):
kexec: add CRASH_WIPE_SECRETS to wipe secrets before kdump
crash-core: Flush caches on CRASH_WIPE_SECRETS
arm64/mm: add set_direct_map_default_nosplit()
mm/secretmem: wipe secret pages before kdump
security/keys: wipe key payloads before kdump
security/keys: implement wipe op for user-type keys
security/keys: implement wipe op for big_key
security/keys: implement wipe op for trusted and encrypted keys
security/keys: implement wipe op for asymmetric keys
rxrpc: implement wipe op for rxrpc keys
fscrypt: wipe master keys before kdump
crypto: api - wipe tfm contexts before kdump
dm crypt: wipe key material before kdump
Documentation/security/keys/core.rst | 13 +++
arch/arm64/include/asm/set_memory.h | 2 +
arch/arm64/kernel/machine_kexec.c | 20 ++++
arch/arm64/mm/pageattr.c | 21 +++++
crypto/api.c | 78 +++++++++++++--
crypto/asymmetric_keys/asymmetric_type.c | 11 +++
crypto/asymmetric_keys/pkcs7_key_type.c | 1 +
crypto/asymmetric_keys/public_key.c | 15 +++
drivers/md/dm-crypt.c | 151 ++++++++++++++++++++++++++----
drivers/nvme/common/keyring.c | 1 +
fs/crypto/block.c | 10 ++
fs/crypto/fscrypt_private.h | 15 +++
fs/crypto/keyring.c | 68 +++++++++++++-
fs/crypto/keysetup_v1.c | 15 +++
fs/nfs/nfs4idmap.c | 2 +
fs/super.c | 29 ++++++
include/keys/asymmetric-subtype.h | 5 +
include/keys/user-type.h | 1 +
include/linux/crash_core.h | 37 ++++++++
include/linux/crypto.h | 11 +++
include/linux/fs.h | 4 +
include/linux/key-type.h | 9 ++
include/linux/set_memory.h | 17 +++-
kernel/Kconfig.kexec | 16 ++++
kernel/crash_core.c | 52 ++++++++++
mm/secretmem.c | 53 +++++++++++
net/rxrpc/ar-internal.h | 5 +
net/rxrpc/key.c | 34 +++++++
net/rxrpc/rxgk.c | 11 +++
net/rxrpc/rxkad.c | 14 +++
net/rxrpc/server_key.c | 11 +++
security/keys/big_key.c | 17 ++++
security/keys/encrypted-keys/encrypted.c | 13 +++
security/keys/key.c | 43 +++++++++
security/keys/trusted-keys/trusted_core.c | 15 +++
security/keys/user_defined.c | 15 +++
36 files changed, 808 insertions(+), 27 deletions(-)
---
base-commit: a0dbb7d3457bdebe2dbe198bd08bf9690be5f1ec
change-id: 20260811-crash-zeroize-rework-bb1a5d917577
Best regards,
--
Jan Sebastian Götte <linux@jaseg.de>
^ permalink raw reply [flat|nested] 15+ messages in thread
* [PATCH v2 01/13] kexec: add CRASH_WIPE_SECRETS to wipe secrets before kdump
2026-08-11 17:52 [PATCH v2 00/13] CRASH_WIPE_SECRETS: Wipe secrets before kdump (was: CRASH_ZEROIZE) Jan Sebastian Götte
@ 2026-08-11 17:52 ` Jan Sebastian Götte
2026-08-11 17:52 ` [PATCH v2 02/13] crash-core: Flush caches on CRASH_WIPE_SECRETS Jan Sebastian Götte
` (12 subsequent siblings)
13 siblings, 0 replies; 15+ messages in thread
From: Jan Sebastian Götte @ 2026-08-11 17:52 UTC (permalink / raw)
To: Andrew Morton, Baoquan He, Mike Rapoport, Pasha Tatashin,
Pratyush Yadav, Dave Young, Catalin Marinas, Will Deacon,
David Howells, Jarkko Sakkinen, Jonathan Corbet, Shuah Khan,
Paul Moore, James Morris, Serge E. Hallyn, Lukas Wunner,
Ignat Korchagin, Herbert Xu, David S. Miller, Keith Busch,
Jens Axboe, Christoph Hellwig, Sagi Grimberg, Trond Myklebust,
Anna Schumaker, Mimi Zohar, James Bottomley, Marc Dionne,
Eric Dumazet, Jakub Kicinski, Paolo Abeni, Simon Horman,
Eric Biggers, Theodore Y. Ts'o, Jaegeuk Kim, Alexander Viro,
Christian Brauner, Jan Kara, Alasdair Kergon, Mike Snitzer,
Mikulas Patocka, Benjamin Marzinski
Cc: kexec, linux-kernel, linux-arm-kernel, linux-mm, keyrings,
linux-doc, linux-security-module, linux-crypto, linux-nvme,
linux-nfs, linux-integrity, linux-afs, netdev, linux-fscrypt,
linux-fsdevel, dm-devel, Jan Sebastian Götte
When kdump is used to capture system memory after a panic(), any secret
keys currently in RAM end up in the dump. Add an opt-in atomic notifier
chain, crash_wipe_secrets_notifier_list, invoked late into
__crash_kexec(). Subsystems holding secrets can register a callback to
wipe them.
Callbacks are run after machine_crash_shutdown() has already stopped the
other CPUs and disabled preemption. Callbacks must not wait on locks,
which will never be released.
This is a best-effort, defence-in-depth measure, not a guarantee.
Secrets in flight on the stack, in registers, in DMA buffers, or in
other places in memory are out of scope.
Signed-off-by: Jan Sebastian Götte <linux@jaseg.de>
---
include/linux/crash_core.h | 16 ++++++++++++++++
kernel/Kconfig.kexec | 16 ++++++++++++++++
kernel/crash_core.c | 29 +++++++++++++++++++++++++++++
3 files changed, 61 insertions(+)
diff --git a/include/linux/crash_core.h b/include/linux/crash_core.h
index bc087124cd78..4230463f3faa 100644
--- a/include/linux/crash_core.h
+++ b/include/linux/crash_core.h
@@ -5,6 +5,7 @@
#include <linux/linkage.h>
#include <linux/elfcore.h>
#include <linux/elf.h>
+#include <linux/notifier.h>
struct kimage;
@@ -34,6 +35,21 @@ static inline void arch_kexec_protect_crashkres(void) { }
static inline void arch_kexec_unprotect_crashkres(void) { }
#endif
+#ifdef CONFIG_CRASH_WIPE_SECRETS
+int crash_wipe_secrets_register(struct notifier_block *nb);
+int crash_wipe_secrets_unregister(struct notifier_block *nb);
+#else
+static inline int crash_wipe_secrets_register(struct notifier_block *nb)
+{
+ return 0;
+}
+
+static inline int crash_wipe_secrets_unregister(struct notifier_block *nb)
+{
+ return 0;
+}
+#endif
+
#ifndef arch_crash_handle_hotplug_event
static inline void arch_crash_handle_hotplug_event(struct kimage *image, void *arg) { }
#endif
diff --git a/kernel/Kconfig.kexec b/kernel/Kconfig.kexec
index 15632358bcf7..1d2d273145df 100644
--- a/kernel/Kconfig.kexec
+++ b/kernel/Kconfig.kexec
@@ -179,4 +179,20 @@ config CRASH_MAX_MEMORY_RANGES
the computation behind the value provided through the
/sys/kernel/crash_elfcorehdr_size attribute.
+config CRASH_WIPE_SECRETS
+ bool "Wipe secrets before kdump"
+ depends on CRASH_DUMP
+ help
+ Wipe secrets (e.g. kernel keyring and memfd_secret pages) on crash or
+ panic. This is a best effort, defense-in-depth feature: If the panic
+ happens at a really bad time, or if copies of the secrets are present
+ in places like on the stack, in I/O buffers, or in userspace memory
+ not allocated through memfd_secret, they may still be leaked.
+
+ Note that enabling this feature carries some risk of crashing the
+ system during the wipe process if the kernel was already unstable
+ when the panic happened.
+
+ If unsure, say N.
+
endmenu
diff --git a/kernel/crash_core.c b/kernel/crash_core.c
index 2b36aa9fade0..95f5c0415e60 100644
--- a/kernel/crash_core.c
+++ b/kernel/crash_core.c
@@ -23,6 +23,7 @@
#include <linux/objtool.h>
#include <linux/delay.h>
#include <linux/panic.h>
+#include <linux/timekeeping.h>
#include <asm/page.h>
#include <asm/sections.h>
@@ -33,6 +34,33 @@
/* Per cpu memory for storing cpu states in case of system crash. */
note_buf_t __percpu *crash_notes;
+#ifdef CONFIG_CRASH_WIPE_SECRETS
+ATOMIC_NOTIFIER_HEAD(crash_wipe_secrets_notifier_list);
+
+int crash_wipe_secrets_register(struct notifier_block *nb)
+{
+ return atomic_notifier_chain_register(
+ &crash_wipe_secrets_notifier_list, nb);
+}
+EXPORT_SYMBOL_GPL(crash_wipe_secrets_register);
+
+int crash_wipe_secrets_unregister(struct notifier_block *nb)
+{
+ return atomic_notifier_chain_unregister(
+ &crash_wipe_secrets_notifier_list, nb);
+}
+EXPORT_SYMBOL_GPL(crash_wipe_secrets_unregister);
+
+static void crash_wipe_secrets(void)
+{
+ pr_info("Wiping sensitive secrets...\n");
+ atomic_notifier_call_chain(&crash_wipe_secrets_notifier_list, 0, NULL);
+ pr_info("Done wiping secrets.\n");
+}
+#else
+static inline void crash_wipe_secrets(void) { }
+#endif /* CONFIG_CRASH_WIPE_SECRETS */
+
/* time to wait for possible DMA to finish before starting the kdump kernel
* when a CMA reservation is used
*/
@@ -142,6 +170,7 @@ void __noclone __crash_kexec(struct pt_regs *regs)
crash_save_vmcoreinfo();
machine_crash_shutdown(&fixed_regs);
crash_cma_clear_pending_dma();
+ crash_wipe_secrets();
machine_kexec(kexec_crash_image);
}
kexec_unlock();
--
2.53.0
^ permalink raw reply related [flat|nested] 15+ messages in thread
* [PATCH v2 02/13] crash-core: Flush caches on CRASH_WIPE_SECRETS
2026-08-11 17:52 [PATCH v2 00/13] CRASH_WIPE_SECRETS: Wipe secrets before kdump (was: CRASH_ZEROIZE) Jan Sebastian Götte
2026-08-11 17:52 ` [PATCH v2 01/13] kexec: add CRASH_WIPE_SECRETS to wipe secrets before kdump Jan Sebastian Götte
@ 2026-08-11 17:52 ` Jan Sebastian Götte
2026-08-11 17:52 ` [PATCH v2 03/13] arm64/mm: add set_direct_map_default_nosplit() Jan Sebastian Götte
` (11 subsequent siblings)
13 siblings, 0 replies; 15+ messages in thread
From: Jan Sebastian Götte @ 2026-08-11 17:52 UTC (permalink / raw)
To: Andrew Morton, Baoquan He, Mike Rapoport, Pasha Tatashin,
Pratyush Yadav, Dave Young, Catalin Marinas, Will Deacon,
David Howells, Jarkko Sakkinen, Jonathan Corbet, Shuah Khan,
Paul Moore, James Morris, Serge E. Hallyn, Lukas Wunner,
Ignat Korchagin, Herbert Xu, David S. Miller, Keith Busch,
Jens Axboe, Christoph Hellwig, Sagi Grimberg, Trond Myklebust,
Anna Schumaker, Mimi Zohar, James Bottomley, Marc Dionne,
Eric Dumazet, Jakub Kicinski, Paolo Abeni, Simon Horman,
Eric Biggers, Theodore Y. Ts'o, Jaegeuk Kim, Alexander Viro,
Christian Brauner, Jan Kara, Alasdair Kergon, Mike Snitzer,
Mikulas Patocka, Benjamin Marzinski
Cc: kexec, linux-kernel, linux-arm-kernel, linux-mm, keyrings,
linux-doc, linux-security-module, linux-crypto, linux-nvme,
linux-nfs, linux-integrity, linux-afs, netdev, linux-fscrypt,
linux-fsdevel, dm-devel, Jan Sebastian Götte
Replace memzero_expclit with our custom wrapper that additionally
flushes the target address ranges from CPU caches.
On ARM64, while memzero_explicit is already reasonably reliable for
wiping secrets from memory during kdump, it can theoretically leave
residue in DRAM when the last memzero_explicit writes are still in
flight in caches when shutting down the caches in machine_kexec.
We need to swap every memzero_explicit call because ARM64 do not have a
wholesale "flush all caches" primitive and only support flushes targeted
to a particular address range.
Architectures other than ARM64, notably x86-64, are not affected by this
cache flush issue.
Signed-off-by: Jan Sebastian Götte <linux@jaseg.de>
---
arch/arm64/kernel/machine_kexec.c | 20 ++++++++++++++++++++
include/linux/crash_core.h | 21 +++++++++++++++++++++
kernel/crash_core.c | 23 +++++++++++++++++++++++
3 files changed, 64 insertions(+)
diff --git a/arch/arm64/kernel/machine_kexec.c b/arch/arm64/kernel/machine_kexec.c
index c5693a32e49b..12bf3c90f519 100644
--- a/arch/arm64/kernel/machine_kexec.c
+++ b/arch/arm64/kernel/machine_kexec.c
@@ -6,6 +6,7 @@
* Copyright (C) Huawei Futurewei Technologies.
*/
+#include <linux/crash_core.h>
#include <linux/interrupt.h>
#include <linux/irq.h>
#include <linux/kernel.h>
@@ -15,6 +16,7 @@
#include <linux/set_memory.h>
#include <linux/smp.h>
+#include <asm/barrier.h>
#include <asm/cacheflush.h>
#include <asm/cpu_ops.h>
#include <asm/daifflags.h>
@@ -221,6 +223,24 @@ void machine_crash_shutdown(struct pt_regs *regs)
pr_info("Starting crashdump kernel...\n");
}
+#ifdef CONFIG_CRASH_WIPE_SECRETS
+/*
+ * Queue each wiped range for cleaning to the point of coherency. Deferring
+ * the barrier until the notifier chain is complete avoids one DSB per range.
+ */
+void arch_crash_wipe_range(void *addr, size_t size)
+{
+ unsigned long start = (unsigned long)addr;
+
+ dcache_clean_poc_nosync(start, start + size);
+}
+
+void arch_crash_wipe_flush(void)
+{
+ dsb(sy);
+}
+#endif
+
#if defined(CONFIG_CRASH_DUMP) && defined(CONFIG_HIBERNATION)
/*
* To preserve the crash dump kernel image, the relevant memory segments
diff --git a/include/linux/crash_core.h b/include/linux/crash_core.h
index 4230463f3faa..d3e4192b7e6d 100644
--- a/include/linux/crash_core.h
+++ b/include/linux/crash_core.h
@@ -6,6 +6,7 @@
#include <linux/elfcore.h>
#include <linux/elf.h>
#include <linux/notifier.h>
+#include <linux/string.h>
struct kimage;
@@ -15,6 +16,26 @@ struct crash_mem {
struct range ranges[] __counted_by(max_nr_ranges);
};
+#ifdef CONFIG_CRASH_WIPE_SECRETS
+/*
+ * Record a range that has already been wiped, or wipe and record it in one
+ * operation. Architectures may use the ranges to push the wipes out to
+ * memory before kexec disables the caches.
+ */
+void crash_wipe_cache_range(void *addr, size_t size);
+void crash_wipe_memzero(void *addr, size_t size);
+
+void arch_crash_wipe_range(void *addr, size_t size);
+void arch_crash_wipe_flush(void);
+#else
+static inline void crash_wipe_cache_range(void *addr, size_t size) { }
+
+static inline void crash_wipe_memzero(void *addr, size_t size)
+{
+ memzero_explicit(addr, size);
+}
+#endif
+
#ifdef CONFIG_CRASH_DUMP
int crash_shrink_memory(unsigned long new_size);
diff --git a/kernel/crash_core.c b/kernel/crash_core.c
index 95f5c0415e60..42faf8d0a4c4 100644
--- a/kernel/crash_core.c
+++ b/kernel/crash_core.c
@@ -51,10 +51,33 @@ int crash_wipe_secrets_unregister(struct notifier_block *nb)
}
EXPORT_SYMBOL_GPL(crash_wipe_secrets_unregister);
+/*
+ * Some kexec paths disable the data cache without first cleaning it. Give
+ * architectures valid virtual ranges for the wiped data, then let them defer
+ * any completion barrier until all crash-wipe callbacks have run.
+ */
+void __weak arch_crash_wipe_range(void *addr, size_t size) { }
+void __weak arch_crash_wipe_flush(void) { }
+
+void crash_wipe_cache_range(void *addr, size_t size)
+{
+ if (size)
+ arch_crash_wipe_range(addr, size);
+}
+EXPORT_SYMBOL_GPL(crash_wipe_cache_range);
+
+void crash_wipe_memzero(void *addr, size_t size)
+{
+ memzero_explicit(addr, size);
+ crash_wipe_cache_range(addr, size);
+}
+EXPORT_SYMBOL_GPL(crash_wipe_memzero);
+
static void crash_wipe_secrets(void)
{
pr_info("Wiping sensitive secrets...\n");
atomic_notifier_call_chain(&crash_wipe_secrets_notifier_list, 0, NULL);
+ arch_crash_wipe_flush();
pr_info("Done wiping secrets.\n");
}
#else
--
2.53.0
^ permalink raw reply related [flat|nested] 15+ messages in thread
* [PATCH v2 03/13] arm64/mm: add set_direct_map_default_nosplit()
2026-08-11 17:52 [PATCH v2 00/13] CRASH_WIPE_SECRETS: Wipe secrets before kdump (was: CRASH_ZEROIZE) Jan Sebastian Götte
2026-08-11 17:52 ` [PATCH v2 01/13] kexec: add CRASH_WIPE_SECRETS to wipe secrets before kdump Jan Sebastian Götte
2026-08-11 17:52 ` [PATCH v2 02/13] crash-core: Flush caches on CRASH_WIPE_SECRETS Jan Sebastian Götte
@ 2026-08-11 17:52 ` Jan Sebastian Götte
2026-08-11 17:52 ` [PATCH v2 04/13] mm/secretmem: wipe secret pages before kdump Jan Sebastian Götte
` (10 subsequent siblings)
13 siblings, 0 replies; 15+ messages in thread
From: Jan Sebastian Götte @ 2026-08-11 17:52 UTC (permalink / raw)
To: Andrew Morton, Baoquan He, Mike Rapoport, Pasha Tatashin,
Pratyush Yadav, Dave Young, Catalin Marinas, Will Deacon,
David Howells, Jarkko Sakkinen, Jonathan Corbet, Shuah Khan,
Paul Moore, James Morris, Serge E. Hallyn, Lukas Wunner,
Ignat Korchagin, Herbert Xu, David S. Miller, Keith Busch,
Jens Axboe, Christoph Hellwig, Sagi Grimberg, Trond Myklebust,
Anna Schumaker, Mimi Zohar, James Bottomley, Marc Dionne,
Eric Dumazet, Jakub Kicinski, Paolo Abeni, Simon Horman,
Eric Biggers, Theodore Y. Ts'o, Jaegeuk Kim, Alexander Viro,
Christian Brauner, Jan Kara, Alasdair Kergon, Mike Snitzer,
Mikulas Patocka, Benjamin Marzinski
Cc: kexec, linux-kernel, linux-arm-kernel, linux-mm, keyrings,
linux-doc, linux-security-module, linux-crypto, linux-nvme,
linux-nfs, linux-integrity, linux-afs, netdev, linux-fscrypt,
linux-fsdevel, dm-devel, Jan Sebastian Götte
set_direct_map_default_noflush() goes through split_kernel_leaf_mapping(),
which takes pgtable_split_lock and may sleep. That makes it unusable from
atomic contexts such as the panic path.
Add set_direct_map_default_nosplit(), which skips the split and only walks
the page tables to apply the permission change. Callers must guarantee the
page is already mapped at page granularity. This holds for any page that
had set_direct_map_invalid_noflush() applied to it, as that call performed
the split.
Architectures that do not need to split the linear map get a generic
fallback to set_direct_map_default_noflush().
Signed-off-by: Jan Sebastian Götte <linux@jaseg.de>
---
arch/arm64/include/asm/set_memory.h | 2 ++
arch/arm64/mm/pageattr.c | 21 +++++++++++++++++++++
include/linux/set_memory.h | 17 ++++++++++++++++-
3 files changed, 39 insertions(+), 1 deletion(-)
diff --git a/arch/arm64/include/asm/set_memory.h b/arch/arm64/include/asm/set_memory.h
index 90f61b17275e..cb5f44bb5a94 100644
--- a/arch/arm64/include/asm/set_memory.h
+++ b/arch/arm64/include/asm/set_memory.h
@@ -13,6 +13,8 @@ int set_memory_valid(unsigned long addr, int numpages, int enable);
int set_direct_map_invalid_noflush(struct page *page);
int set_direct_map_default_noflush(struct page *page);
+int set_direct_map_default_nosplit(struct page *page);
+#define set_direct_map_default_nosplit set_direct_map_default_nosplit
int set_direct_map_valid_noflush(struct page *page, unsigned nr, bool valid);
bool kernel_page_present(struct page *page);
diff --git a/arch/arm64/mm/pageattr.c b/arch/arm64/mm/pageattr.c
index bbe98ac9ad8c..a7bb6bfa12ed 100644
--- a/arch/arm64/mm/pageattr.c
+++ b/arch/arm64/mm/pageattr.c
@@ -275,6 +275,27 @@ int set_direct_map_default_noflush(struct page *page)
PAGE_SIZE, set_mask, clear_mask);
}
+/* Caller must guarantee @page is already mapped at page granularity. */
+int set_direct_map_default_nosplit(struct page *page)
+{
+ struct page_change_data data = {
+ .set_mask = __pgprot(PTE_PRESENT_VALID_KERNEL | PTE_WRITE),
+ .clear_mask = __pgprot(PTE_PRESENT_INVALID | PTE_RDONLY),
+ };
+ unsigned long addr = (unsigned long)page_address(page);
+ int ret;
+
+ if (!can_set_direct_map())
+ return 0;
+
+ lazy_mmu_mode_enable();
+ ret = walk_kernel_page_table_range_lockless(addr, addr + PAGE_SIZE,
+ &pageattr_ops, NULL, &data);
+ lazy_mmu_mode_disable();
+
+ return ret;
+}
+
static int __set_memory_enc_dec(unsigned long addr,
int numpages,
bool encrypt)
diff --git a/include/linux/set_memory.h b/include/linux/set_memory.h
index 3030d9245f5a..65d0fa010368 100644
--- a/include/linux/set_memory.h
+++ b/include/linux/set_memory.h
@@ -44,7 +44,22 @@ static inline bool kernel_page_present(struct page *page)
{
return true;
}
-#else /* CONFIG_ARCH_HAS_SET_DIRECT_MAP */
+#endif /* !CONFIG_ARCH_HAS_SET_DIRECT_MAP */
+
+/*
+ * Variant of set_direct_map_default_noflush() that is safe to call from atomic
+ * context, for pages the caller knows are already mapped at page granularity.
+ * Architectures that need to split the linear map, and can therefore sleep,
+ * override this.
+ */
+#ifndef set_direct_map_default_nosplit
+static inline int set_direct_map_default_nosplit(struct page *page)
+{
+ return set_direct_map_default_noflush(page);
+}
+#endif
+
+#ifdef CONFIG_ARCH_HAS_SET_DIRECT_MAP
/*
* Some architectures, e.g. ARM64 can disable direct map modifications at
* boot time. Let them overrive this query.
--
2.53.0
^ permalink raw reply related [flat|nested] 15+ messages in thread
* [PATCH v2 04/13] mm/secretmem: wipe secret pages before kdump
2026-08-11 17:52 [PATCH v2 00/13] CRASH_WIPE_SECRETS: Wipe secrets before kdump (was: CRASH_ZEROIZE) Jan Sebastian Götte
` (2 preceding siblings ...)
2026-08-11 17:52 ` [PATCH v2 03/13] arm64/mm: add set_direct_map_default_nosplit() Jan Sebastian Götte
@ 2026-08-11 17:52 ` Jan Sebastian Götte
2026-08-11 17:52 ` [PATCH v2 05/13] security/keys: wipe key payloads " Jan Sebastian Götte
` (9 subsequent siblings)
13 siblings, 0 replies; 15+ messages in thread
From: Jan Sebastian Götte @ 2026-08-11 17:52 UTC (permalink / raw)
To: Andrew Morton, Baoquan He, Mike Rapoport, Pasha Tatashin,
Pratyush Yadav, Dave Young, Catalin Marinas, Will Deacon,
David Howells, Jarkko Sakkinen, Jonathan Corbet, Shuah Khan,
Paul Moore, James Morris, Serge E. Hallyn, Lukas Wunner,
Ignat Korchagin, Herbert Xu, David S. Miller, Keith Busch,
Jens Axboe, Christoph Hellwig, Sagi Grimberg, Trond Myklebust,
Anna Schumaker, Mimi Zohar, James Bottomley, Marc Dionne,
Eric Dumazet, Jakub Kicinski, Paolo Abeni, Simon Horman,
Eric Biggers, Theodore Y. Ts'o, Jaegeuk Kim, Alexander Viro,
Christian Brauner, Jan Kara, Alasdair Kergon, Mike Snitzer,
Mikulas Patocka, Benjamin Marzinski
Cc: kexec, linux-kernel, linux-arm-kernel, linux-mm, keyrings,
linux-doc, linux-security-module, linux-crypto, linux-nvme,
linux-nfs, linux-integrity, linux-afs, netdev, linux-fscrypt,
linux-fsdevel, dm-devel, Jan Sebastian Götte
Register a CRASH_WIPE_SECRETS notifier that wipes secretmem folios. As a
result, when CONFIG_CRASH_WIPE_SECRETS is set, secretmem areas will be
cleared before the kdump kernel is kexec'ed.
The notifier 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@jaseg.de>
---
mm/secretmem.c | 53 +++++++++++++++++++++++++++++++++++++++++++++++++++++
1 file changed, 53 insertions(+)
diff --git a/mm/secretmem.c b/mm/secretmem.c
index d29865075b6e..db00b7d06080 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,54 @@ static const struct inode_operations secretmem_iops = {
static struct vfsmount *secretmem_mnt;
+/* Called far into vpanic from crash_core.c with other CPUs stopped and
+ * preemption disabled
+ */
+static int secretmem_crash_wipe(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_wipe_secrets: can't acquire secretmem superblock lock.\n"
+ "crash_wipe_secrets: 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;
+
+ /* secretmem_fault() already split the linear map */
+ set_direct_map_default_nosplit(folio_page(folio, 0));
+ folio_zero_segment(folio, 0, folio_size(folio));
+ crash_wipe_cache_range(folio_address(folio),
+ folio_size(folio));
+ }
+ }
+ spin_unlock(&sb->s_inode_list_lock);
+ /* off to kexec()! */
+ return NOTIFY_DONE;
+}
+
+static struct notifier_block secretmem_wipe_nb = {
+ .notifier_call = secretmem_crash_wipe
+};
+
static struct file *secretmem_file_create(unsigned long flags)
{
struct file *file;
@@ -212,6 +262,8 @@ static struct file *secretmem_file_create(unsigned long flags)
inode->i_mode |= S_IFREG;
inode->i_size = 0;
+ inode_sb_list_add(inode);
+
atomic_inc(&secretmem_users);
return file;
@@ -263,6 +315,7 @@ static int __init secretmem_init(void)
if (IS_ERR(secretmem_mnt))
return PTR_ERR(secretmem_mnt);
+ crash_wipe_secrets_register(&secretmem_wipe_nb);
return 0;
}
fs_initcall(secretmem_init);
--
2.53.0
^ permalink raw reply related [flat|nested] 15+ messages in thread
* [PATCH v2 05/13] security/keys: wipe key payloads before kdump
2026-08-11 17:52 [PATCH v2 00/13] CRASH_WIPE_SECRETS: Wipe secrets before kdump (was: CRASH_ZEROIZE) Jan Sebastian Götte
` (3 preceding siblings ...)
2026-08-11 17:52 ` [PATCH v2 04/13] mm/secretmem: wipe secret pages before kdump Jan Sebastian Götte
@ 2026-08-11 17:52 ` Jan Sebastian Götte
2026-08-11 17:52 ` [PATCH v2 06/13] security/keys: implement wipe op for user-type keys Jan Sebastian Götte
` (8 subsequent siblings)
13 siblings, 0 replies; 15+ messages in thread
From: Jan Sebastian Götte @ 2026-08-11 17:52 UTC (permalink / raw)
To: Andrew Morton, Baoquan He, Mike Rapoport, Pasha Tatashin,
Pratyush Yadav, Dave Young, Catalin Marinas, Will Deacon,
David Howells, Jarkko Sakkinen, Jonathan Corbet, Shuah Khan,
Paul Moore, James Morris, Serge E. Hallyn, Lukas Wunner,
Ignat Korchagin, Herbert Xu, David S. Miller, Keith Busch,
Jens Axboe, Christoph Hellwig, Sagi Grimberg, Trond Myklebust,
Anna Schumaker, Mimi Zohar, James Bottomley, Marc Dionne,
Eric Dumazet, Jakub Kicinski, Paolo Abeni, Simon Horman,
Eric Biggers, Theodore Y. Ts'o, Jaegeuk Kim, Alexander Viro,
Christian Brauner, Jan Kara, Alasdair Kergon, Mike Snitzer,
Mikulas Patocka, Benjamin Marzinski
Cc: kexec, linux-kernel, linux-arm-kernel, linux-mm, keyrings,
linux-doc, linux-security-module, linux-crypto, linux-nvme,
linux-nfs, linux-integrity, linux-afs, netdev, linux-fscrypt,
linux-fsdevel, dm-devel, Jan Sebastian Götte
When CONFIG_CRASH_WIPE_SECRETS is set, try to erase key payloads on
panic before jumping to the kdump kernel.
CRASH_WIPE_SECRETS notifiers run during panic() with other CPUs stopped
and preemption disabled. In this state, we can't rely on free()'ing
being safe, so we define a new `wipe` key op.
Signed-off-by: Jan Sebastian Götte <linux@jaseg.de>
---
Documentation/security/keys/core.rst | 13 +++++++++++
include/linux/key-type.h | 9 ++++++++
security/keys/key.c | 43 ++++++++++++++++++++++++++++++++++++
3 files changed, 65 insertions(+)
diff --git a/Documentation/security/keys/core.rst b/Documentation/security/keys/core.rst
index 326b8a973828..35cb00a87c46 100644
--- a/Documentation/security/keys/core.rst
+++ b/Documentation/security/keys/core.rst
@@ -1596,6 +1596,19 @@ The structure has a number of fields, some of which are mandatory:
It is not safe to sleep in this method; the caller may hold spinlocks.
+ * ``void (*wipe)(struct key *key);``
+
+ This method is optional. It is called from the panic path when
+ CONFIG_CRASH_WIPE_SECRETS is enabled, to erase the key material from
+ memory before the kdump kernel is started, so that it does not end up in
+ the crash dump. Unlike destroy(), it must only clear the payload, not
+ free it.
+
+ This method is called with all other CPUs stopped and preemption
+ disabled, and only for positively instantiated keys. It must not sleep,
+ allocate, free or take locks, as they will never be released.
+
+
* ``void (*describe)(const struct key *key, struct seq_file *p);``
This method is optional. It is called during /proc/keys reading to
diff --git a/include/linux/key-type.h b/include/linux/key-type.h
index bb97bd3e5af4..21e07db0c5f2 100644
--- a/include/linux/key-type.h
+++ b/include/linux/key-type.h
@@ -122,6 +122,15 @@ struct key_type {
/* clear the data from a key (optional) */
void (*destroy)(struct key *key);
+ /* wipe the key material without free'ing (optional)
+ * - used from CONFIG_CRASH_WIPE_SECRETS during panic to keep keys out
+ * of crash dumps
+ * - called from the panic path with other CPUs stopped and preemption
+ * disabled
+ * - must not sleep, allocate, free or take locks
+ */
+ void (*wipe)(struct key *key);
+
/* describe a key */
void (*describe)(const struct key *key, struct seq_file *p);
diff --git a/security/keys/key.c b/security/keys/key.c
index b34a64d81d47..213e6f1d5d83 100644
--- a/security/keys/key.c
+++ b/security/keys/key.c
@@ -12,6 +12,7 @@
#include <linux/slab.h>
#include <linux/security.h>
#include <linux/workqueue.h>
+#include <linux/crash_core.h>
#include <linux/random.h>
#include <linux/err.h>
#include "internal.h"
@@ -1268,6 +1269,47 @@ void unregister_key_type(struct key_type *ktype)
}
EXPORT_SYMBOL(unregister_key_type);
+/* Called far into vpanic from crash_core.c with other CPUs stopped and
+ * preemption disabled
+ */
+static int key_crash_wipe(struct notifier_block *nb, unsigned long action,
+ void *data)
+{
+ struct rb_node *node;
+
+ /* If we can't acquire the lock, the rbtree might be in an inconsistent
+ * state. That's all we can do then, as there's no point to waiting
+ * at this stage.
+ */
+ if (!spin_trylock(&key_serial_lock)) {
+ pr_crit("crash_wipe_secrets: can't acquire key_serial_lock. skipping keyrings.\n");
+ return NOTIFY_DONE;
+ }
+
+ for (node = rb_first(&key_serial_tree); node; node = rb_next(node)) {
+ struct key *key = rb_entry(node, struct key, serial_node);
+
+ /* Negatively instantiated keys have key->state < 0 and never
+ * had a payload attached, so only wipe positive ones.
+ */
+ if (key->type == &key_type_keyring || !key_is_positive(key))
+ continue;
+
+ /* We have a dedicated wipe callback for this since free'ing
+ * isn't safe at this point
+ */
+ if (key->type->wipe)
+ key->type->wipe(key);
+ }
+ spin_unlock(&key_serial_lock);
+ /* off to kexec()! */
+ return NOTIFY_DONE;
+}
+
+static struct notifier_block key_crash_wipe_nb = {
+ .notifier_call = key_crash_wipe
+};
+
/*
* Initialise the key management state.
*/
@@ -1290,4 +1332,5 @@ void __init key_init(void)
rb_insert_color(&root_key_user.node,
&key_user_tree);
+ crash_wipe_secrets_register(&key_crash_wipe_nb);
}
--
2.53.0
^ permalink raw reply related [flat|nested] 15+ messages in thread
* [PATCH v2 06/13] security/keys: implement wipe op for user-type keys
2026-08-11 17:52 [PATCH v2 00/13] CRASH_WIPE_SECRETS: Wipe secrets before kdump (was: CRASH_ZEROIZE) Jan Sebastian Götte
` (4 preceding siblings ...)
2026-08-11 17:52 ` [PATCH v2 05/13] security/keys: wipe key payloads " Jan Sebastian Götte
@ 2026-08-11 17:52 ` Jan Sebastian Götte
2026-08-11 17:52 ` [PATCH v2 07/13] security/keys: implement wipe op for big_key Jan Sebastian Götte
` (7 subsequent siblings)
13 siblings, 0 replies; 15+ messages in thread
From: Jan Sebastian Götte @ 2026-08-11 17:52 UTC (permalink / raw)
To: Andrew Morton, Baoquan He, Mike Rapoport, Pasha Tatashin,
Pratyush Yadav, Dave Young, Catalin Marinas, Will Deacon,
David Howells, Jarkko Sakkinen, Jonathan Corbet, Shuah Khan,
Paul Moore, James Morris, Serge E. Hallyn, Lukas Wunner,
Ignat Korchagin, Herbert Xu, David S. Miller, Keith Busch,
Jens Axboe, Christoph Hellwig, Sagi Grimberg, Trond Myklebust,
Anna Schumaker, Mimi Zohar, James Bottomley, Marc Dionne,
Eric Dumazet, Jakub Kicinski, Paolo Abeni, Simon Horman,
Eric Biggers, Theodore Y. Ts'o, Jaegeuk Kim, Alexander Viro,
Christian Brauner, Jan Kara, Alasdair Kergon, Mike Snitzer,
Mikulas Patocka, Benjamin Marzinski
Cc: kexec, linux-kernel, linux-arm-kernel, linux-mm, keyrings,
linux-doc, linux-security-module, linux-crypto, linux-nvme,
linux-nfs, linux-integrity, linux-afs, netdev, linux-fscrypt,
linux-fsdevel, dm-devel, Jan Sebastian Götte
Wipe the payload of "user" and "logon" keys, and of the key types reusing
the user-type ops: s390 cert_store, NVMe TLS PSK, NFS idmap and PKCS#7.
Used by CONFIG_CRASH_WIPE_SECRETS.
Signed-off-by: Jan Sebastian Götte <linux@jaseg.de>
---
crypto/asymmetric_keys/pkcs7_key_type.c | 1 +
drivers/nvme/common/keyring.c | 1 +
fs/nfs/nfs4idmap.c | 2 ++
include/keys/user-type.h | 1 +
security/keys/user_defined.c | 15 +++++++++++++++
5 files changed, 20 insertions(+)
diff --git a/crypto/asymmetric_keys/pkcs7_key_type.c b/crypto/asymmetric_keys/pkcs7_key_type.c
index b930d3bbf1af..663a246742f7 100644
--- a/crypto/asymmetric_keys/pkcs7_key_type.c
+++ b/crypto/asymmetric_keys/pkcs7_key_type.c
@@ -74,6 +74,7 @@ static struct key_type key_type_pkcs7 = {
.instantiate = generic_key_instantiate,
.revoke = user_revoke,
.destroy = user_destroy,
+ .wipe = user_wipe,
.describe = user_describe,
.read = user_read,
};
diff --git a/drivers/nvme/common/keyring.c b/drivers/nvme/common/keyring.c
index 32d16c53133b..33303f035fb1 100644
--- a/drivers/nvme/common/keyring.c
+++ b/drivers/nvme/common/keyring.c
@@ -84,6 +84,7 @@ static struct key_type nvme_tls_psk_key_type = {
.instantiate = generic_key_instantiate,
.revoke = user_revoke,
.destroy = user_destroy,
+ .wipe = user_wipe,
.describe = nvme_tls_psk_describe,
.read = user_read,
};
diff --git a/fs/nfs/nfs4idmap.c b/fs/nfs/nfs4idmap.c
index bc397110d977..26f579ae2c73 100644
--- a/fs/nfs/nfs4idmap.c
+++ b/fs/nfs/nfs4idmap.c
@@ -190,6 +190,7 @@ static struct key_type key_type_id_resolver = {
.instantiate = generic_key_instantiate,
.revoke = user_revoke,
.destroy = user_destroy,
+ .wipe = user_wipe,
.describe = user_describe,
.read = user_read,
};
@@ -412,6 +413,7 @@ static struct key_type key_type_id_resolver_legacy = {
.instantiate = generic_key_instantiate,
.revoke = user_revoke,
.destroy = user_destroy,
+ .wipe = user_wipe,
.describe = user_describe,
.read = user_read,
.request_key = nfs_idmap_legacy_upcall,
diff --git a/include/keys/user-type.h b/include/keys/user-type.h
index 386c31432789..685fa054b0ac 100644
--- a/include/keys/user-type.h
+++ b/include/keys/user-type.h
@@ -40,6 +40,7 @@ extern void user_free_preparse(struct key_preparsed_payload *prep);
extern int user_update(struct key *key, struct key_preparsed_payload *prep);
extern void user_revoke(struct key *key);
extern void user_destroy(struct key *key);
+extern void user_wipe(struct key *key);
extern void user_describe(const struct key *user, struct seq_file *m);
extern long user_read(const struct key *key, char *buffer, size_t buflen);
diff --git a/security/keys/user_defined.c b/security/keys/user_defined.c
index 6f88b507f927..f4897c19c2dd 100644
--- a/security/keys/user_defined.c
+++ b/security/keys/user_defined.c
@@ -6,6 +6,7 @@
*/
#include <linux/export.h>
+#include <linux/crash_core.h>
#include <linux/init.h>
#include <linux/slab.h>
#include <linux/seq_file.h>
@@ -28,6 +29,7 @@ struct key_type key_type_user = {
.update = user_update,
.revoke = user_revoke,
.destroy = user_destroy,
+ .wipe = user_wipe,
.describe = user_describe,
.read = user_read,
};
@@ -48,6 +50,7 @@ struct key_type key_type_logon = {
.update = user_update,
.revoke = user_revoke,
.destroy = user_destroy,
+ .wipe = user_wipe,
.describe = user_describe,
.vet_description = logon_vet_description,
};
@@ -152,6 +155,18 @@ void user_destroy(struct key *key)
EXPORT_SYMBOL_GPL(user_destroy);
+void user_wipe(struct key *key)
+{
+ struct user_key_payload *upayload = key->payload.data[0];
+
+ if (upayload)
+ crash_wipe_memzero(upayload->data, upayload->datalen);
+ key->payload.data[1] = NULL;
+ key->payload.data[2] = NULL;
+ key->payload.data[3] = NULL;
+}
+EXPORT_SYMBOL_GPL(user_wipe);
+
/*
* describe the user key
*/
--
2.53.0
^ permalink raw reply related [flat|nested] 15+ messages in thread
* [PATCH v2 07/13] security/keys: implement wipe op for big_key
2026-08-11 17:52 [PATCH v2 00/13] CRASH_WIPE_SECRETS: Wipe secrets before kdump (was: CRASH_ZEROIZE) Jan Sebastian Götte
` (5 preceding siblings ...)
2026-08-11 17:52 ` [PATCH v2 06/13] security/keys: implement wipe op for user-type keys Jan Sebastian Götte
@ 2026-08-11 17:52 ` Jan Sebastian Götte
2026-08-11 17:52 ` [PATCH v2 08/13] security/keys: implement wipe op for trusted and encrypted keys Jan Sebastian Götte
` (6 subsequent siblings)
13 siblings, 0 replies; 15+ messages in thread
From: Jan Sebastian Götte @ 2026-08-11 17:52 UTC (permalink / raw)
To: Andrew Morton, Baoquan He, Mike Rapoport, Pasha Tatashin,
Pratyush Yadav, Dave Young, Catalin Marinas, Will Deacon,
David Howells, Jarkko Sakkinen, Jonathan Corbet, Shuah Khan,
Paul Moore, James Morris, Serge E. Hallyn, Lukas Wunner,
Ignat Korchagin, Herbert Xu, David S. Miller, Keith Busch,
Jens Axboe, Christoph Hellwig, Sagi Grimberg, Trond Myklebust,
Anna Schumaker, Mimi Zohar, James Bottomley, Marc Dionne,
Eric Dumazet, Jakub Kicinski, Paolo Abeni, Simon Horman,
Eric Biggers, Theodore Y. Ts'o, Jaegeuk Kim, Alexander Viro,
Christian Brauner, Jan Kara, Alasdair Kergon, Mike Snitzer,
Mikulas Patocka, Benjamin Marzinski
Cc: kexec, linux-kernel, linux-arm-kernel, linux-mm, keyrings,
linux-doc, linux-security-module, linux-crypto, linux-nvme,
linux-nfs, linux-integrity, linux-afs, netdev, linux-fscrypt,
linux-fsdevel, dm-devel, Jan Sebastian Götte
For keys stored in shmem only the ChaCha20-Poly1305 key is reachable here,
so wiping it is enough to render the payload unreadable.
Used by CONFIG_CRASH_WIPE_SECRETS.
Signed-off-by: Jan Sebastian Götte <linux@jaseg.de>
---
security/keys/big_key.c | 17 +++++++++++++++++
1 file changed, 17 insertions(+)
diff --git a/security/keys/big_key.c b/security/keys/big_key.c
index 268f702df380..c01a212bec90 100644
--- a/security/keys/big_key.c
+++ b/security/keys/big_key.c
@@ -8,6 +8,7 @@
#define pr_fmt(fmt) "big_key: "fmt
#include <linux/init.h>
+#include <linux/crash_core.h>
#include <linux/seq_file.h>
#include <linux/file.h>
#include <linux/shmem_fs.h>
@@ -35,6 +36,8 @@ struct big_key_payload {
*/
#define BIG_KEY_FILE_THRESHOLD (sizeof(struct inode) + sizeof(struct dentry))
+static void big_key_wipe(struct key *key);
+
/*
* big_key defined keys take an arbitrary string as the description and an
* arbitrary blob of data as the payload
@@ -46,6 +49,7 @@ struct key_type key_type_big_key = {
.instantiate = generic_key_instantiate,
.revoke = big_key_revoke,
.destroy = big_key_destroy,
+ .wipe = big_key_wipe,
.describe = big_key_describe,
.read = big_key_read,
.update = big_key_update,
@@ -279,6 +283,19 @@ long big_key_read(const struct key *key, char *buffer, size_t buflen)
return ret;
}
+static void big_key_wipe(struct key *key)
+{
+ struct big_key_payload *payload = to_big_key_payload(key->payload);
+
+ if (payload->data) {
+ if (payload->length > BIG_KEY_FILE_THRESHOLD)
+ crash_wipe_memzero(payload->data,
+ CHACHA20POLY1305_KEY_SIZE);
+ else
+ crash_wipe_memzero(payload->data, payload->length);
+ }
+}
+
/*
* Register key type
*/
--
2.53.0
^ permalink raw reply related [flat|nested] 15+ messages in thread
* [PATCH v2 08/13] security/keys: implement wipe op for trusted and encrypted keys
2026-08-11 17:52 [PATCH v2 00/13] CRASH_WIPE_SECRETS: Wipe secrets before kdump (was: CRASH_ZEROIZE) Jan Sebastian Götte
` (6 preceding siblings ...)
2026-08-11 17:52 ` [PATCH v2 07/13] security/keys: implement wipe op for big_key Jan Sebastian Götte
@ 2026-08-11 17:52 ` Jan Sebastian Götte
2026-08-11 17:52 ` [PATCH v2 09/13] security/keys: implement wipe op for asymmetric keys Jan Sebastian Götte
` (5 subsequent siblings)
13 siblings, 0 replies; 15+ messages in thread
From: Jan Sebastian Götte @ 2026-08-11 17:52 UTC (permalink / raw)
To: Andrew Morton, Baoquan He, Mike Rapoport, Pasha Tatashin,
Pratyush Yadav, Dave Young, Catalin Marinas, Will Deacon,
David Howells, Jarkko Sakkinen, Jonathan Corbet, Shuah Khan,
Paul Moore, James Morris, Serge E. Hallyn, Lukas Wunner,
Ignat Korchagin, Herbert Xu, David S. Miller, Keith Busch,
Jens Axboe, Christoph Hellwig, Sagi Grimberg, Trond Myklebust,
Anna Schumaker, Mimi Zohar, James Bottomley, Marc Dionne,
Eric Dumazet, Jakub Kicinski, Paolo Abeni, Simon Horman,
Eric Biggers, Theodore Y. Ts'o, Jaegeuk Kim, Alexander Viro,
Christian Brauner, Jan Kara, Alasdair Kergon, Mike Snitzer,
Mikulas Patocka, Benjamin Marzinski
Cc: kexec, linux-kernel, linux-arm-kernel, linux-mm, keyrings,
linux-doc, linux-security-module, linux-crypto, linux-nvme,
linux-nfs, linux-integrity, linux-afs, netdev, linux-fscrypt,
linux-fsdevel, dm-devel, Jan Sebastian Götte
Wipe the decrypted key material and the sealed blob held in the payload.
Used by CONFIG_CRASH_WIPE_SECRETS.
Signed-off-by: Jan Sebastian Götte <linux@jaseg.de>
---
security/keys/encrypted-keys/encrypted.c | 13 +++++++++++++
security/keys/trusted-keys/trusted_core.c | 15 +++++++++++++++
2 files changed, 28 insertions(+)
diff --git a/security/keys/encrypted-keys/encrypted.c b/security/keys/encrypted-keys/encrypted.c
index 59cb77b237b3..9d6fe08ecb7a 100644
--- a/security/keys/encrypted-keys/encrypted.c
+++ b/security/keys/encrypted-keys/encrypted.c
@@ -12,6 +12,7 @@
*/
#include <linux/uaccess.h>
+#include <linux/crash_core.h>
#include <linux/module.h>
#include <linux/hex.h>
#include <linux/init.h>
@@ -970,11 +971,23 @@ static void encrypted_destroy(struct key *key)
kfree_sensitive(key->payload.data[0]);
}
+static void encrypted_wipe(struct key *key)
+{
+ struct encrypted_key_payload *epayload = key->payload.data[0];
+
+ if (!epayload)
+ return;
+
+ crash_wipe_memzero(epayload->payload_data,
+ epayload->payload_datalen + epayload->datablob_len);
+}
+
struct key_type key_type_encrypted = {
.name = "encrypted",
.instantiate = encrypted_instantiate,
.update = encrypted_update,
.destroy = encrypted_destroy,
+ .wipe = encrypted_wipe,
.describe = user_describe,
.read = encrypted_read,
};
diff --git a/security/keys/trusted-keys/trusted_core.c b/security/keys/trusted-keys/trusted_core.c
index 0509d9955f2a..7b220c6381b5 100644
--- a/security/keys/trusted-keys/trusted_core.c
+++ b/security/keys/trusted-keys/trusted_core.c
@@ -14,6 +14,7 @@
#include <keys/trusted_tpm.h>
#include <keys/trusted_pkwm.h>
#include <linux/capability.h>
+#include <linux/crash_core.h>
#include <linux/err.h>
#include <linux/hex.h>
#include <linux/init.h>
@@ -325,11 +326,25 @@ static void trusted_destroy(struct key *key)
kfree_sensitive(key->payload.data[0]);
}
+static void trusted_wipe(struct key *key)
+{
+ struct trusted_key_payload *p = key->payload.data[0];
+
+ if (!p)
+ return;
+
+ crash_wipe_memzero(p->key, sizeof(p->key));
+ crash_wipe_memzero(p->blob, sizeof(p->blob));
+ p->key_len = 0;
+ p->blob_len = 0;
+}
+
struct key_type key_type_trusted = {
.name = "trusted",
.instantiate = trusted_instantiate,
.update = trusted_update,
.destroy = trusted_destroy,
+ .wipe = trusted_wipe,
.describe = user_describe,
.read = trusted_read,
};
--
2.53.0
^ permalink raw reply related [flat|nested] 15+ messages in thread
* [PATCH v2 09/13] security/keys: implement wipe op for asymmetric keys
2026-08-11 17:52 [PATCH v2 00/13] CRASH_WIPE_SECRETS: Wipe secrets before kdump (was: CRASH_ZEROIZE) Jan Sebastian Götte
` (7 preceding siblings ...)
2026-08-11 17:52 ` [PATCH v2 08/13] security/keys: implement wipe op for trusted and encrypted keys Jan Sebastian Götte
@ 2026-08-11 17:52 ` Jan Sebastian Götte
2026-08-11 17:52 ` [PATCH v2 10/13] rxrpc: implement wipe op for rxrpc keys Jan Sebastian Götte
` (4 subsequent siblings)
13 siblings, 0 replies; 15+ messages in thread
From: Jan Sebastian Götte @ 2026-08-11 17:52 UTC (permalink / raw)
To: Andrew Morton, Baoquan He, Mike Rapoport, Pasha Tatashin,
Pratyush Yadav, Dave Young, Catalin Marinas, Will Deacon,
David Howells, Jarkko Sakkinen, Jonathan Corbet, Shuah Khan,
Paul Moore, James Morris, Serge E. Hallyn, Lukas Wunner,
Ignat Korchagin, Herbert Xu, David S. Miller, Keith Busch,
Jens Axboe, Christoph Hellwig, Sagi Grimberg, Trond Myklebust,
Anna Schumaker, Mimi Zohar, James Bottomley, Marc Dionne,
Eric Dumazet, Jakub Kicinski, Paolo Abeni, Simon Horman,
Eric Biggers, Theodore Y. Ts'o, Jaegeuk Kim, Alexander Viro,
Christian Brauner, Jan Kara, Alasdair Kergon, Mike Snitzer,
Mikulas Patocka, Benjamin Marzinski
Cc: kexec, linux-kernel, linux-arm-kernel, linux-mm, keyrings,
linux-doc, linux-security-module, linux-crypto, linux-nvme,
linux-nfs, linux-integrity, linux-afs, netdev, linux-fscrypt,
linux-fsdevel, dm-devel, Jan Sebastian Götte
Add wipe op to the subtype and implement it for public_key.
Used by CONFIG_CRASH_WIPE_SECRETS.
Signed-off-by: Jan Sebastian Götte <linux@jaseg.de>
---
crypto/asymmetric_keys/asymmetric_type.c | 11 +++++++++++
crypto/asymmetric_keys/public_key.c | 15 +++++++++++++++
include/keys/asymmetric-subtype.h | 5 +++++
3 files changed, 31 insertions(+)
diff --git a/crypto/asymmetric_keys/asymmetric_type.c b/crypto/asymmetric_keys/asymmetric_type.c
index 16a7ae16593c..a0a68ebd0746 100644
--- a/crypto/asymmetric_keys/asymmetric_type.c
+++ b/crypto/asymmetric_keys/asymmetric_type.c
@@ -481,6 +481,16 @@ static void asymmetric_key_destroy(struct key *key)
asymmetric_key_free_kids(kids);
}
+/* wipe the key without freeing. used by CONFIG_CRASH_WIPE_SECRETS. */
+static void asymmetric_key_wipe(struct key *key)
+{
+ struct asymmetric_key_subtype *subtype = asymmetric_key_subtype(key);
+
+ if (subtype && subtype->wipe)
+ subtype->wipe(key->payload.data[asym_crypto],
+ key->payload.data[asym_auth]);
+}
+
static struct key_restriction *asymmetric_restriction_alloc(
key_restrict_link_func_t check,
struct key *key)
@@ -612,6 +622,7 @@ struct key_type key_type_asymmetric = {
.match_preparse = asymmetric_key_match_preparse,
.match_free = asymmetric_key_match_free,
.destroy = asymmetric_key_destroy,
+ .wipe = asymmetric_key_wipe,
.describe = asymmetric_key_describe,
.lookup_restriction = asymmetric_lookup_restriction,
.asym_query = query_asymmetric_key,
diff --git a/crypto/asymmetric_keys/public_key.c b/crypto/asymmetric_keys/public_key.c
index 09a0b83d5d77..9931d56337e5 100644
--- a/crypto/asymmetric_keys/public_key.c
+++ b/crypto/asymmetric_keys/public_key.c
@@ -13,6 +13,7 @@
#include <crypto/sig.h>
#include <keys/asymmetric-subtype.h>
#include <linux/asn1.h>
+#include <linux/crash_core.h>
#include <linux/err.h>
#include <linux/kernel.h>
#include <linux/module.h>
@@ -58,6 +59,19 @@ static void public_key_destroy(void *payload0, void *payload3)
public_key_signature_free(payload3);
}
+/* wipe the key without freeing. used by CONFIG_CRASH_WIPE_SECRETS. */
+static void public_key_wipe(void *payload0, void *payload3)
+{
+ struct public_key *key = payload0;
+
+ if (key) {
+ if (key->key)
+ crash_wipe_memzero(key->key, key->keylen);
+ if (key->params)
+ crash_wipe_memzero(key->params, key->paramlen);
+ }
+}
+
/*
* Given a public_key, and an encoding and hash_algo to be used for signing
* and/or verification with that key, determine the name of the corresponding
@@ -464,6 +478,7 @@ struct asymmetric_key_subtype public_key_subtype = {
.name_len = sizeof("public_key") - 1,
.describe = public_key_describe,
.destroy = public_key_destroy,
+ .wipe = public_key_wipe,
.query = software_key_query,
.eds_op = software_key_eds_op,
.verify_signature = public_key_verify_signature_2,
diff --git a/include/keys/asymmetric-subtype.h b/include/keys/asymmetric-subtype.h
index d55171f640a0..17c74070c101 100644
--- a/include/keys/asymmetric-subtype.h
+++ b/include/keys/asymmetric-subtype.h
@@ -32,6 +32,11 @@ struct asymmetric_key_subtype {
/* Destroy a key of this subtype */
void (*destroy)(void *payload_crypto, void *payload_auth);
+ /* Wipe a key of this subtype without freeing it (optional). Used by
+ * CONFIG_CRASH_WIPE_SECRETS from the panic path.
+ */
+ void (*wipe)(void *payload_crypto, void *payload_auth);
+
int (*query)(const struct kernel_pkey_params *params,
struct kernel_pkey_query *info);
--
2.53.0
^ permalink raw reply related [flat|nested] 15+ messages in thread
* [PATCH v2 10/13] rxrpc: implement wipe op for rxrpc keys
2026-08-11 17:52 [PATCH v2 00/13] CRASH_WIPE_SECRETS: Wipe secrets before kdump (was: CRASH_ZEROIZE) Jan Sebastian Götte
` (8 preceding siblings ...)
2026-08-11 17:52 ` [PATCH v2 09/13] security/keys: implement wipe op for asymmetric keys Jan Sebastian Götte
@ 2026-08-11 17:52 ` Jan Sebastian Götte
2026-08-11 17:53 ` [PATCH v2 11/13] fscrypt: wipe master keys before kdump Jan Sebastian Götte
` (3 subsequent siblings)
13 siblings, 0 replies; 15+ messages in thread
From: Jan Sebastian Götte @ 2026-08-11 17:52 UTC (permalink / raw)
To: Andrew Morton, Baoquan He, Mike Rapoport, Pasha Tatashin,
Pratyush Yadav, Dave Young, Catalin Marinas, Will Deacon,
David Howells, Jarkko Sakkinen, Jonathan Corbet, Shuah Khan,
Paul Moore, James Morris, Serge E. Hallyn, Lukas Wunner,
Ignat Korchagin, Herbert Xu, David S. Miller, Keith Busch,
Jens Axboe, Christoph Hellwig, Sagi Grimberg, Trond Myklebust,
Anna Schumaker, Mimi Zohar, James Bottomley, Marc Dionne,
Eric Dumazet, Jakub Kicinski, Paolo Abeni, Simon Horman,
Eric Biggers, Theodore Y. Ts'o, Jaegeuk Kim, Alexander Viro,
Christian Brauner, Jan Kara, Alasdair Kergon, Mike Snitzer,
Mikulas Patocka, Benjamin Marzinski
Cc: kexec, linux-kernel, linux-arm-kernel, linux-mm, keyrings,
linux-doc, linux-security-module, linux-crypto, linux-nvme,
linux-nfs, linux-integrity, linux-afs, netdev, linux-fscrypt,
linux-fsdevel, dm-devel, Jan Sebastian Götte
Wipe session keys and tickets of client key tokens, and add a server key
wipe op implemented by rxkad and rxgk.
Used by CONFIG_CRASH_WIPE_SECRETS.
Signed-off-by: Jan Sebastian Götte <linux@jaseg.de>
---
net/rxrpc/ar-internal.h | 5 +++++
net/rxrpc/key.c | 34 ++++++++++++++++++++++++++++++++++
net/rxrpc/rxgk.c | 11 +++++++++++
net/rxrpc/rxkad.c | 14 ++++++++++++++
net/rxrpc/server_key.c | 11 +++++++++++
5 files changed, 75 insertions(+)
diff --git a/net/rxrpc/ar-internal.h b/net/rxrpc/ar-internal.h
index 865f05fe37ab..6466eb5a9929 100644
--- a/net/rxrpc/ar-internal.h
+++ b/net/rxrpc/ar-internal.h
@@ -282,6 +282,11 @@ struct rxrpc_security {
/* Destroy the payload of a server key */
void (*destroy_server_key)(struct key *);
+ /* Wipe the payload of a server key without freeing it. Used by
+ * CONFIG_CRASH_WIPE_SECRETS from the panic path.
+ */
+ void (*wipe_server_key)(struct key *);
+
/* Describe a server key */
void (*describe_server_key)(const struct key *, struct seq_file *);
diff --git a/net/rxrpc/key.c b/net/rxrpc/key.c
index a0aa78d89289..e612b7a8df13 100644
--- a/net/rxrpc/key.c
+++ b/net/rxrpc/key.c
@@ -11,6 +11,7 @@
#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
#include <linux/module.h>
+#include <linux/crash_core.h>
#include <linux/net.h>
#include <linux/overflow.h>
#include <linux/skbuff.h>
@@ -26,6 +27,7 @@
static int rxrpc_preparse(struct key_preparsed_payload *);
static void rxrpc_free_preparse(struct key_preparsed_payload *);
static void rxrpc_destroy(struct key *);
+static void rxrpc_wipe(struct key *);
static void rxrpc_describe(const struct key *, struct seq_file *);
static long rxrpc_read(const struct key *, char *, size_t);
@@ -40,6 +42,7 @@ struct key_type key_type_rxrpc = {
.free_preparse = rxrpc_free_preparse,
.instantiate = generic_key_instantiate,
.destroy = rxrpc_destroy,
+ .wipe = rxrpc_wipe,
.describe = rxrpc_describe,
.read = rxrpc_read,
};
@@ -570,6 +573,31 @@ static void rxrpc_free_token_list(struct rxrpc_key_token *token)
}
}
+static void rxrpc_wipe_token_list(struct rxrpc_key_token *token)
+{
+ struct rxrpc_key_token *next;
+
+ for (; token; token = next) {
+ next = token->next;
+ switch (token->security_index) {
+ case RXRPC_SECURITY_RXKAD:
+ crash_wipe_memzero(token->kad->session_key,
+ sizeof(token->kad->session_key));
+ crash_wipe_memzero(token->kad->ticket,
+ token->kad->ticket_len);
+ break;
+ case RXRPC_SECURITY_YFS_RXGK:
+ crash_wipe_memzero(token->rxgk->key.data,
+ token->rxgk->key.len);
+ crash_wipe_memzero(token->rxgk->ticket.data,
+ token->rxgk->ticket.len);
+ break;
+ default:
+ break;
+ }
+ }
+}
+
/*
* Clean up preparse data.
*/
@@ -586,6 +614,12 @@ static void rxrpc_destroy(struct key *key)
rxrpc_free_token_list(key->payload.data[0]);
}
+/* wipe the key without freeing. used by CONFIG_CRASH_WIPE_SECRETS. */
+static void rxrpc_wipe(struct key *key)
+{
+ rxrpc_wipe_token_list(key->payload.data[0]);
+}
+
/*
* describe the rxrpc key
*/
diff --git a/net/rxrpc/rxgk.c b/net/rxrpc/rxgk.c
index 77a67ace1d24..51c428fb3a60 100644
--- a/net/rxrpc/rxgk.c
+++ b/net/rxrpc/rxgk.c
@@ -8,6 +8,7 @@
#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
#include <linux/net.h>
+#include <linux/crash_core.h>
#include <linux/skbuff.h>
#include <linux/slab.h>
#include <linux/key-type.h>
@@ -68,6 +69,15 @@ static void rxgk_destroy_server_key(struct key *key)
rxgk_free_server_key(&key->payload);
}
+/* wipe the key without freeing. used by CONFIG_CRASH_WIPE_SECRETS. */
+static void rxgk_wipe_server_key(struct key *key)
+{
+ struct krb5_buffer *server_key = (void *)&key->payload.data[2];
+
+ if (server_key->data)
+ crash_wipe_memzero(server_key->data, server_key->len);
+}
+
static void rxgk_describe_server_key(const struct key *key, struct seq_file *m)
{
const struct krb5_enctype *krb5 = key->payload.data[0];
@@ -1338,6 +1348,7 @@ const struct rxrpc_security rxgk_yfs = {
.preparse_server_key = rxgk_preparse_server_key,
.free_preparse_server_key = rxgk_free_preparse_server_key,
.destroy_server_key = rxgk_destroy_server_key,
+ .wipe_server_key = rxgk_wipe_server_key,
.describe_server_key = rxgk_describe_server_key,
.init_connection_security = rxgk_init_connection_security,
.alloc_txbuf = rxgk_alloc_txbuf,
diff --git a/net/rxrpc/rxkad.c b/net/rxrpc/rxkad.c
index ca9f0e82cb9a..054ab67aca32 100644
--- a/net/rxrpc/rxkad.c
+++ b/net/rxrpc/rxkad.c
@@ -10,6 +10,7 @@
#include <crypto/des.h>
#include <kunit/visibility.h>
#include <linux/export.h>
+#include <linux/crash_core.h>
#include <linux/fips.h>
#include <linux/module.h>
#include <linux/net.h>
@@ -91,6 +92,18 @@ static void rxkad_destroy_server_key(struct key *key)
key->payload.data[0] = NULL;
}
+/* wipe the key without freeing. used by CONFIG_CRASH_WIPE_SECRETS. */
+static void rxkad_wipe_server_key(struct key *key)
+{
+ struct des_ctx *des_key = key->payload.data[0];
+
+ if (des_key)
+ crash_wipe_memzero(des_key, sizeof(*des_key));
+
+ /* the raw 8-byte key is kept inline in the payload union */
+ crash_wipe_memzero(&key->payload.data[2], 8);
+}
+
/*
* initialise connection security
*/
@@ -1127,6 +1140,7 @@ const struct rxrpc_security rxkad = {
.preparse_server_key = rxkad_preparse_server_key,
.free_preparse_server_key = rxkad_free_preparse_server_key,
.destroy_server_key = rxkad_destroy_server_key,
+ .wipe_server_key = rxkad_wipe_server_key,
.init_connection_security = rxkad_init_connection_security,
.alloc_txbuf = rxkad_alloc_txbuf,
.secure_packet = rxkad_secure_packet,
diff --git a/net/rxrpc/server_key.c b/net/rxrpc/server_key.c
index 3efe104b1930..a8e736d58793 100644
--- a/net/rxrpc/server_key.c
+++ b/net/rxrpc/server_key.c
@@ -26,6 +26,7 @@ static int rxrpc_vet_description_s(const char *);
static int rxrpc_preparse_s(struct key_preparsed_payload *);
static void rxrpc_free_preparse_s(struct key_preparsed_payload *);
static void rxrpc_destroy_s(struct key *);
+static void rxrpc_wipe_s(struct key *);
static void rxrpc_describe_s(const struct key *, struct seq_file *);
/*
@@ -40,6 +41,7 @@ struct key_type key_type_rxrpc_s = {
.free_preparse = rxrpc_free_preparse_s,
.instantiate = generic_key_instantiate,
.destroy = rxrpc_destroy_s,
+ .wipe = rxrpc_wipe_s,
.describe = rxrpc_describe_s,
};
@@ -105,6 +107,15 @@ static void rxrpc_destroy_s(struct key *key)
sec->destroy_server_key(key);
}
+/* wipe the key without freeing. used by CONFIG_CRASH_WIPE_SECRETS. */
+static void rxrpc_wipe_s(struct key *key)
+{
+ const struct rxrpc_security *sec = key->payload.data[1];
+
+ if (sec && sec->wipe_server_key)
+ sec->wipe_server_key(key);
+}
+
static void rxrpc_describe_s(const struct key *key, struct seq_file *m)
{
const struct rxrpc_security *sec = key->payload.data[1];
--
2.53.0
^ permalink raw reply related [flat|nested] 15+ messages in thread
* [PATCH v2 11/13] fscrypt: wipe master keys before kdump
2026-08-11 17:52 [PATCH v2 00/13] CRASH_WIPE_SECRETS: Wipe secrets before kdump (was: CRASH_ZEROIZE) Jan Sebastian Götte
` (9 preceding siblings ...)
2026-08-11 17:52 ` [PATCH v2 10/13] rxrpc: implement wipe op for rxrpc keys Jan Sebastian Götte
@ 2026-08-11 17:53 ` Jan Sebastian Götte
2026-08-11 17:53 ` [PATCH v2 12/13] crypto: api - wipe tfm contexts " Jan Sebastian Götte
` (2 subsequent siblings)
13 siblings, 0 replies; 15+ messages in thread
From: Jan Sebastian Götte @ 2026-08-11 17:53 UTC (permalink / raw)
To: Andrew Morton, Baoquan He, Mike Rapoport, Pasha Tatashin,
Pratyush Yadav, Dave Young, Catalin Marinas, Will Deacon,
David Howells, Jarkko Sakkinen, Jonathan Corbet, Shuah Khan,
Paul Moore, James Morris, Serge E. Hallyn, Lukas Wunner,
Ignat Korchagin, Herbert Xu, David S. Miller, Keith Busch,
Jens Axboe, Christoph Hellwig, Sagi Grimberg, Trond Myklebust,
Anna Schumaker, Mimi Zohar, James Bottomley, Marc Dionne,
Eric Dumazet, Jakub Kicinski, Paolo Abeni, Simon Horman,
Eric Biggers, Theodore Y. Ts'o, Jaegeuk Kim, Alexander Viro,
Christian Brauner, Jan Kara, Alasdair Kergon, Mike Snitzer,
Mikulas Patocka, Benjamin Marzinski
Cc: kexec, linux-kernel, linux-arm-kernel, linux-mm, keyrings,
linux-doc, linux-security-module, linux-crypto, linux-nvme,
linux-nfs, linux-integrity, linux-afs, netdev, linux-fscrypt,
linux-fsdevel, dm-devel, Jan Sebastian Götte
fscrypt master keys hang off the superblock rather than a keyring of their
own key type, so register a separate notifier that walks the live
superblocks via the new super_blocks_crash_wipe() helper.
Signed-off-by: Jan Sebastian Götte <linux@jaseg.de>
---
fs/crypto/block.c | 10 +++++++
fs/crypto/fscrypt_private.h | 15 ++++++++++
fs/crypto/keyring.c | 68 ++++++++++++++++++++++++++++++++++++++++++++-
fs/crypto/keysetup_v1.c | 15 ++++++++++
fs/super.c | 29 +++++++++++++++++++
include/linux/fs.h | 4 +++
6 files changed, 140 insertions(+), 1 deletion(-)
diff --git a/fs/crypto/block.c b/fs/crypto/block.c
index 5193f8ba3ee0..a2fa1469b3c4 100644
--- a/fs/crypto/block.c
+++ b/fs/crypto/block.c
@@ -15,6 +15,7 @@
#include <linux/blk-crypto.h>
#include <linux/blkdev.h>
+#include <linux/crash_core.h>
#include <linux/export.h>
#include <linux/sched/mm.h>
#include <linux/slab.h>
@@ -144,6 +145,15 @@ int fscrypt_prepare_inline_crypt_key(struct fscrypt_prepared_key *prep_key,
return err;
}
+#ifdef CONFIG_CRASH_WIPE_SECRETS
+void fscrypt_crash_wipe_inline_crypt_key(struct fscrypt_prepared_key *prep_key)
+{
+ if (prep_key->blk_key)
+ crash_wipe_memzero(prep_key->blk_key->bytes,
+ sizeof(prep_key->blk_key->bytes));
+}
+#endif /* CONFIG_CRASH_WIPE_SECRETS */
+
void fscrypt_destroy_inline_crypt_key(struct super_block *sb,
struct fscrypt_prepared_key *prep_key)
{
diff --git a/fs/crypto/fscrypt_private.h b/fs/crypto/fscrypt_private.h
index 74329e0953d1..95cb50e5cbcd 100644
--- a/fs/crypto/fscrypt_private.h
+++ b/fs/crypto/fscrypt_private.h
@@ -413,6 +413,10 @@ int fscrypt_prepare_inline_crypt_key(struct fscrypt_prepared_key *prep_key,
void fscrypt_destroy_inline_crypt_key(struct super_block *sb,
struct fscrypt_prepared_key *prep_key);
+#ifdef CONFIG_CRASH_WIPE_SECRETS /* && CONFIG_FS_ENCRYPTION_INLINE_CRYPT */
+void fscrypt_crash_wipe_inline_crypt_key(struct fscrypt_prepared_key *prep_key);
+#endif
+
int fscrypt_derive_sw_secret(struct super_block *sb,
const u8 *wrapped_key, size_t wrapped_key_size,
u8 sw_secret[BLK_CRYPTO_SW_SECRET_SIZE]);
@@ -454,6 +458,13 @@ fscrypt_destroy_inline_crypt_key(struct super_block *sb,
{
}
+#ifdef CONFIG_CRASH_WIPE_SECRETS /* && !CONFIG_FS_ENCRYPTION_INLINE_CRYPT */
+static inline void
+fscrypt_crash_wipe_inline_crypt_key(struct fscrypt_prepared_key *prep_key)
+{
+}
+#endif
+
static inline int
fscrypt_derive_sw_secret(struct super_block *sb,
const u8 *wrapped_key, size_t wrapped_key_size,
@@ -760,6 +771,10 @@ static inline int fscrypt_require_key(struct inode *inode)
void fscrypt_put_direct_key(struct fscrypt_direct_key *dk);
+#ifdef CONFIG_CRASH_WIPE_SECRETS
+void fscrypt_crash_wipe_direct_keys(void);
+#endif
+
int fscrypt_setup_v1_file_key(struct fscrypt_inode_info *ci,
const u8 *raw_master_key);
diff --git a/fs/crypto/keyring.c b/fs/crypto/keyring.c
index 76e28d1e0064..5ac302146eb2 100644
--- a/fs/crypto/keyring.c
+++ b/fs/crypto/keyring.c
@@ -19,6 +19,7 @@
*/
#include <crypto/skcipher.h>
+#include <linux/crash_core.h>
#include <linux/export.h>
#include <linux/key-type.h>
#include <linux/once.h>
@@ -239,8 +240,9 @@ void fscrypt_destroy_keyring(struct super_block *sb)
fscrypt_initiate_key_removal(sb, mk);
}
}
+ /* Stop panic-time walkers from finding @keyring before it is freed. */
+ smp_store_release(&sb->s_master_keys, NULL);
kfree_sensitive(keyring);
- sb->s_master_keys = NULL;
}
static struct hlist_head *
@@ -640,6 +642,13 @@ static void fscrypt_provisioning_key_destroy(struct key *key)
kfree_sensitive(key->payload.data[0]);
}
+/* wipe the key without freeing. used by CONFIG_CRASH_WIPE_SECRETS. */
+static void fscrypt_provisioning_key_wipe(struct key *key)
+{
+ if (key->payload.data[0])
+ crash_wipe_memzero(key->payload.data[0], key->datalen);
+}
+
static struct key_type key_type_fscrypt_provisioning = {
.name = "fscrypt-provisioning",
.preparse = fscrypt_provisioning_key_preparse,
@@ -647,6 +656,7 @@ static struct key_type key_type_fscrypt_provisioning = {
.instantiate = generic_key_instantiate,
.describe = fscrypt_provisioning_key_describe,
.destroy = fscrypt_provisioning_key_destroy,
+ .wipe = fscrypt_provisioning_key_wipe,
};
/*
@@ -1220,6 +1230,58 @@ int fscrypt_ioctl_get_key_status(struct file *filp, void __user *uarg)
}
EXPORT_SYMBOL_GPL(fscrypt_ioctl_get_key_status);
+#ifdef CONFIG_CRASH_WIPE_SECRETS
+/*
+ * Wipe the master keys of one superblock. The master keys don't live on a
+ * keyring of their own key_type, so the keyrings core can't reach them.
+ */
+static void fscrypt_crash_wipe_sb(struct super_block *sb)
+{
+ struct fscrypt_keyring *keyring = sb->s_master_keys;
+ size_t i;
+
+ if (!keyring)
+ return;
+
+ for (i = 0; i < ARRAY_SIZE(keyring->key_hashtable); i++) {
+ struct fscrypt_master_key *mk;
+
+ hlist_for_each_entry(mk, &keyring->key_hashtable[i], mk_node) {
+ struct fscrypt_inode_info *ci;
+ struct fscrypt_mode_key *node;
+
+ crash_wipe_memzero(&mk->mk_secret,
+ sizeof(mk->mk_secret));
+
+ list_for_each_entry(node, &mk->mk_mode_keys, link)
+ fscrypt_crash_wipe_inline_crypt_key(&node->key);
+
+ list_for_each_entry(ci, &mk->mk_decrypted_inodes,
+ ci_master_key_link)
+ fscrypt_crash_wipe_inline_crypt_key(&ci->ci_enc_key);
+ }
+ }
+}
+
+/* Called far into vpanic from crash_core.c with other CPUs stopped and
+ * preemption disabled
+ */
+static int fscrypt_crash_wipe(struct notifier_block *nb, unsigned long action,
+ void *data)
+{
+ if (!super_blocks_crash_wipe(fscrypt_crash_wipe_sb))
+ pr_crit("crash_wipe_secrets: can't acquire sb_lock. skipping fscrypt keys.\n");
+
+ fscrypt_crash_wipe_direct_keys();
+
+ return NOTIFY_DONE;
+}
+
+static struct notifier_block fscrypt_crash_wipe_nb = {
+ .notifier_call = fscrypt_crash_wipe
+};
+#endif /* CONFIG_CRASH_WIPE_SECRETS */
+
void __init fscrypt_init_keyring(void)
{
int err;
@@ -1235,4 +1297,8 @@ void __init fscrypt_init_keyring(void)
if (err)
panic("failed to register fscrypt-provisioning key type (%d)",
err);
+
+#ifdef CONFIG_CRASH_WIPE_SECRETS
+ crash_wipe_secrets_register(&fscrypt_crash_wipe_nb);
+#endif
}
diff --git a/fs/crypto/keysetup_v1.c b/fs/crypto/keysetup_v1.c
index 87fe13ccb253..08dd0a682a8f 100644
--- a/fs/crypto/keysetup_v1.c
+++ b/fs/crypto/keysetup_v1.c
@@ -23,6 +23,7 @@
#include <crypto/aes.h>
#include <crypto/utils.h>
#include <keys/user-type.h>
+#include <linux/crash_core.h>
#include <linux/hashtable.h>
#include "fscrypt_private.h"
@@ -118,6 +119,20 @@ void fscrypt_put_direct_key(struct fscrypt_direct_key *dk)
free_direct_key(dk);
}
+#ifdef CONFIG_CRASH_WIPE_SECRETS
+void fscrypt_crash_wipe_direct_keys(void)
+{
+ struct fscrypt_direct_key *dk;
+ unsigned int i;
+
+ /* No locking: all other CPUs are stopped, so nothing can race with us. */
+ hash_for_each(fscrypt_direct_keys, i, dk, dk_node) {
+ crash_wipe_memzero(dk->dk_raw, sizeof(dk->dk_raw));
+ fscrypt_crash_wipe_inline_crypt_key(&dk->dk_key);
+ }
+}
+#endif /* CONFIG_CRASH_WIPE_SECRETS */
+
/*
* Find/insert the given key into the fscrypt_direct_keys table. If found, it
* is returned with elevated refcount, and 'to_insert' is freed if non-NULL. If
diff --git a/fs/super.c b/fs/super.c
index 5feecf5d9038..cd51e9cfe93b 100644
--- a/fs/super.c
+++ b/fs/super.c
@@ -2463,3 +2463,32 @@ int sb_init_dio_done_wq(struct super_block *sb)
return 0;
}
EXPORT_SYMBOL_GPL(sb_init_dio_done_wq);
+
+#ifdef CONFIG_CRASH_WIPE_SECRETS
+/**
+ * super_blocks_crash_wipe - run @wipe against every live superblock
+ * @wipe: callback to invoke for each superblock
+ *
+ * Called from the panic path with other CPUs stopped and preemption disabled.
+ * The callback must not sleep, allocate, free or take locks.
+ *
+ * Returns false without doing anything if @sb_lock could not be acquired, in
+ * which case the list may be inconsistent and walking it is unsafe.
+ */
+bool super_blocks_crash_wipe(void (*wipe)(struct super_block *sb))
+{
+ struct super_block *sb;
+
+ /* There is no point in waiting for a lock that will never be released
+ * at this stage.
+ */
+ if (!spin_trylock(&sb_lock))
+ return false;
+
+ list_for_each_entry(sb, &super_blocks, s_list)
+ wipe(sb);
+
+ spin_unlock(&sb_lock);
+ return true;
+}
+#endif
diff --git a/include/linux/fs.h b/include/linux/fs.h
index 072d8cd09a0b..7832a77908d2 100644
--- a/include/linux/fs.h
+++ b/include/linux/fs.h
@@ -2378,6 +2378,10 @@ extern __printf(2, 3)
int super_setup_bdi_name(struct super_block *sb, char *fmt, ...);
extern int super_setup_bdi(struct super_block *sb);
+#ifdef CONFIG_CRASH_WIPE_SECRETS
+bool super_blocks_crash_wipe(void (*wipe)(struct super_block *sb));
+#endif
+
static inline void super_set_uuid(struct super_block *sb, const u8 *uuid, unsigned len)
{
if (WARN_ON(len > sizeof(sb->s_uuid)))
--
2.53.0
^ permalink raw reply related [flat|nested] 15+ messages in thread
* [PATCH v2 12/13] crypto: api - wipe tfm contexts before kdump
2026-08-11 17:52 [PATCH v2 00/13] CRASH_WIPE_SECRETS: Wipe secrets before kdump (was: CRASH_ZEROIZE) Jan Sebastian Götte
` (10 preceding siblings ...)
2026-08-11 17:53 ` [PATCH v2 11/13] fscrypt: wipe master keys before kdump Jan Sebastian Götte
@ 2026-08-11 17:53 ` Jan Sebastian Götte
2026-08-11 17:53 ` [PATCH v2 13/13] dm crypt: wipe key material " Jan Sebastian Götte
2026-08-11 18:20 ` [PATCH v2 00/13] CRASH_WIPE_SECRETS: Wipe secrets before kdump (was: CRASH_ZEROIZE) Eric Biggers
13 siblings, 0 replies; 15+ messages in thread
From: Jan Sebastian Götte @ 2026-08-11 17:53 UTC (permalink / raw)
To: Andrew Morton, Baoquan He, Mike Rapoport, Pasha Tatashin,
Pratyush Yadav, Dave Young, Catalin Marinas, Will Deacon,
David Howells, Jarkko Sakkinen, Jonathan Corbet, Shuah Khan,
Paul Moore, James Morris, Serge E. Hallyn, Lukas Wunner,
Ignat Korchagin, Herbert Xu, David S. Miller, Keith Busch,
Jens Axboe, Christoph Hellwig, Sagi Grimberg, Trond Myklebust,
Anna Schumaker, Mimi Zohar, James Bottomley, Marc Dionne,
Eric Dumazet, Jakub Kicinski, Paolo Abeni, Simon Horman,
Eric Biggers, Theodore Y. Ts'o, Jaegeuk Kim, Alexander Viro,
Christian Brauner, Jan Kara, Alasdair Kergon, Mike Snitzer,
Mikulas Patocka, Benjamin Marzinski
Cc: kexec, linux-kernel, linux-arm-kernel, linux-mm, keyrings,
linux-doc, linux-security-module, linux-crypto, linux-nvme,
linux-nfs, linux-integrity, linux-afs, netdev, linux-fscrypt,
linux-fsdevel, dm-devel, Jan Sebastian Götte
tfm structs contain key material like expanded key schedules. Under
CONFIG_CRASH_WIPE_SECRETS, wipe all tfms before kdump to prevent
leakage. Copies outside the tfm context, e.g. on the stack or in
hardware key registers, are not covered.
Also change two kfree() calls to kfree_sensitive() for good measure.
Signed-off-by: Jan Sebastian Götte <linux@jaseg.de>
---
crypto/api.c | 78 ++++++++++++++++++++++++++++++++++++++++++++------
include/linux/crypto.h | 11 +++++++
2 files changed, 81 insertions(+), 8 deletions(-)
diff --git a/crypto/api.c b/crypto/api.c
index 24227582cfcf..f919f52e8b5f 100644
--- a/crypto/api.c
+++ b/crypto/api.c
@@ -10,13 +10,16 @@
* and Nettle, by Niels Möller.
*/
+#include <linux/crash_core.h>
#include <linux/err.h>
#include <linux/errno.h>
#include <linux/jump_label.h>
#include <linux/kernel.h>
#include <linux/kmod.h>
#include <linux/module.h>
+#include <linux/notifier.h>
#include <linux/param.h>
+#include <linux/rculist.h>
#include <linux/sched/signal.h>
#include <linux/slab.h>
#include <linux/string.h>
@@ -397,6 +400,60 @@ static unsigned int crypto_ctxsize(struct crypto_alg *alg, u32 type, u32 mask)
return len;
}
+#ifdef CONFIG_CRASH_WIPE_SECRETS
+/*
+ * tfm allocations are tracked on crypto_tfm_list so that they can be wiped
+ * before kdump.
+ */
+static HLIST_HEAD(crypto_tfm_list);
+static DEFINE_SPINLOCK(crypto_tfm_list_lock);
+
+static int crypto_crash_wipe(struct notifier_block *nb, unsigned long action,
+ void *data)
+{
+ struct crypto_tfm *tfm;
+
+ rcu_read_lock();
+ hlist_for_each_entry_rcu(tfm, &crypto_tfm_list, wipe_list)
+ crash_wipe_memzero(tfm->__crt_ctx, tfm->wipe_size);
+ rcu_read_unlock();
+
+ /* off to kexec()! */
+ return NOTIFY_DONE;
+}
+
+static struct notifier_block crypto_wipe_nb = {
+ .notifier_call = crypto_crash_wipe
+};
+
+static int __init crypto_tfm_track_init(void)
+{
+ crash_wipe_secrets_register(&crypto_wipe_nb);
+ return 0;
+}
+core_initcall(crypto_tfm_track_init);
+
+/* @size is the size of __crt_ctx */
+static void crypto_track_tfm(struct crypto_tfm *tfm, size_t size)
+{
+ tfm->wipe_size = size;
+
+ spin_lock(&crypto_tfm_list_lock);
+ hlist_add_head_rcu(&tfm->wipe_list, &crypto_tfm_list);
+ spin_unlock(&crypto_tfm_list_lock);
+}
+
+static void crypto_untrack_tfm(struct crypto_tfm *tfm)
+{
+ spin_lock(&crypto_tfm_list_lock);
+ hlist_del_rcu(&tfm->wipe_list);
+ spin_unlock(&crypto_tfm_list_lock);
+}
+#else
+static void crypto_track_tfm(struct crypto_tfm *tfm, size_t size) { }
+static void crypto_untrack_tfm(struct crypto_tfm *tfm) { }
+#endif /* CONFIG_CRASH_WIPE_SECRETS */
+
void crypto_shoot_alg(struct crypto_alg *alg)
{
down_write(&crypto_alg_sem);
@@ -409,15 +466,16 @@ struct crypto_tfm *__crypto_alloc_tfm(struct crypto_alg *alg, u32 type,
u32 mask)
{
struct crypto_tfm *tfm;
- unsigned int tfm_size;
+ unsigned int ctx_size;
int err = -ENOMEM;
- tfm_size = sizeof(*tfm) + crypto_ctxsize(alg, type, mask);
- tfm = kzalloc(tfm_size, GFP_KERNEL);
+ ctx_size = crypto_ctxsize(alg, type, mask);
+ tfm = kzalloc(sizeof(*tfm) + ctx_size, GFP_KERNEL);
if (tfm == NULL)
goto out_err;
tfm->__crt_alg = alg;
+ crypto_track_tfm(tfm, ctx_size);
if (!tfm->exit && alg->cra_init && (err = alg->cra_init(tfm)))
goto cra_init_failed;
@@ -428,7 +486,8 @@ struct crypto_tfm *__crypto_alloc_tfm(struct crypto_alg *alg, u32 type,
crypto_exit_ops(tfm);
if (err == -EAGAIN)
crypto_shoot_alg(alg);
- kfree(tfm);
+ crypto_untrack_tfm(tfm);
+ kfree_sensitive(tfm);
out_err:
tfm = ERR_PTR(err);
out:
@@ -497,12 +556,12 @@ void *crypto_create_tfm_node(struct crypto_alg *alg,
int node)
{
struct crypto_tfm *tfm;
- size_t size;
char *mem;
int err;
+ size_t ctx_size = frontend->extsize(alg);
- size = frontend->tfmsize + sizeof(*tfm) + frontend->extsize(alg);
- mem = kzalloc_node(size, GFP_KERNEL, node);
+ mem = kzalloc_node(frontend->tfmsize + sizeof(*tfm) + ctx_size,
+ GFP_KERNEL, node);
if (!mem)
return ERR_PTR(-ENOMEM);
@@ -510,6 +569,7 @@ void *crypto_create_tfm_node(struct crypto_alg *alg,
tfm->__crt_alg = alg;
tfm->node = node;
tfm->fb = tfm;
+ crypto_track_tfm(tfm, ctx_size);
err = frontend->init_tfm(tfm);
if (err)
@@ -525,7 +585,8 @@ void *crypto_create_tfm_node(struct crypto_alg *alg,
out_free_tfm:
if (err == -EAGAIN)
crypto_shoot_alg(alg);
- kfree(mem);
+ crypto_untrack_tfm(tfm);
+ kfree_sensitive(mem);
mem = ERR_PTR(err);
out:
return mem;
@@ -627,6 +688,7 @@ void crypto_destroy_tfm(void *mem, struct crypto_tfm *tfm)
alg->cra_exit(tfm);
crypto_exit_ops(tfm);
crypto_mod_put(alg);
+ crypto_untrack_tfm(tfm);
kfree_sensitive(mem);
}
EXPORT_SYMBOL_GPL(crypto_destroy_tfm);
diff --git a/include/linux/crypto.h b/include/linux/crypto.h
index b7c97f1c47c9..59d6d62180fd 100644
--- a/include/linux/crypto.h
+++ b/include/linux/crypto.h
@@ -14,6 +14,7 @@
#include <linux/completion.h>
#include <linux/errno.h>
+#include <linux/list.h>
#include <linux/refcount_types.h>
#include <linux/slab.h>
#include <linux/types.h>
@@ -420,6 +421,16 @@ struct crypto_tfm {
struct crypto_alg *__crt_alg;
+#ifdef CONFIG_CRASH_WIPE_SECRETS
+ /*
+ * Link on crypto_tfm_list, used to wipe the expanded key schedule in
+ * __crt_ctx before kdump. wipe_size covers __crt_ctx only, excluding
+ * this struct and any frontend preceding it.
+ */
+ struct hlist_node wipe_list;
+ size_t wipe_size;
+#endif
+
void *__crt_ctx[] CRYPTO_MINALIGN_ATTR;
};
--
2.53.0
^ permalink raw reply related [flat|nested] 15+ messages in thread
* [PATCH v2 13/13] dm crypt: wipe key material before kdump
2026-08-11 17:52 [PATCH v2 00/13] CRASH_WIPE_SECRETS: Wipe secrets before kdump (was: CRASH_ZEROIZE) Jan Sebastian Götte
` (11 preceding siblings ...)
2026-08-11 17:53 ` [PATCH v2 12/13] crypto: api - wipe tfm contexts " Jan Sebastian Götte
@ 2026-08-11 17:53 ` Jan Sebastian Götte
2026-08-11 18:20 ` [PATCH v2 00/13] CRASH_WIPE_SECRETS: Wipe secrets before kdump (was: CRASH_ZEROIZE) Eric Biggers
13 siblings, 0 replies; 15+ messages in thread
From: Jan Sebastian Götte @ 2026-08-11 17:53 UTC (permalink / raw)
To: Andrew Morton, Baoquan He, Mike Rapoport, Pasha Tatashin,
Pratyush Yadav, Dave Young, Catalin Marinas, Will Deacon,
David Howells, Jarkko Sakkinen, Jonathan Corbet, Shuah Khan,
Paul Moore, James Morris, Serge E. Hallyn, Lukas Wunner,
Ignat Korchagin, Herbert Xu, David S. Miller, Keith Busch,
Jens Axboe, Christoph Hellwig, Sagi Grimberg, Trond Myklebust,
Anna Schumaker, Mimi Zohar, James Bottomley, Marc Dionne,
Eric Dumazet, Jakub Kicinski, Paolo Abeni, Simon Horman,
Eric Biggers, Theodore Y. Ts'o, Jaegeuk Kim, Alexander Viro,
Christian Brauner, Jan Kara, Alasdair Kergon, Mike Snitzer,
Mikulas Patocka, Benjamin Marzinski
Cc: kexec, linux-kernel, linux-arm-kernel, linux-mm, keyrings,
linux-doc, linux-security-module, linux-crypto, linux-nvme,
linux-nfs, linux-integrity, linux-afs, netdev, linux-fscrypt,
linux-fsdevel, dm-devel, Jan Sebastian Götte
Wipe volume key/iv copies kept by dm-crypt with
CONFIG_CRASH_WIPE_SECRETS. The backend tfms are already handled
separately.
Add a list tracking struct crypt_config instances when
CONFIG_CRASH_WIPE_SECRETS is set. Structs are tracked here to avoid
having to enumerate them through some roundabout way before kdump, when
we can't safely take locks anymore.
Use custom wipe handlers even for things like ivs that have existing
wipe functions elsewhere because we need to use crash_wipe_memzero
instead of memzero_explicit. The crash_wipe helper memzero_explicit's
the target buffers and flushes data caches. On ARM64, missing that cache
flush could lead to the zeros not being written to DRAM before the kdump
code turns off the data caches moments later.
Signed-off-by: Jan Sebastian Götte <linux@jaseg.de>
---
drivers/md/dm-crypt.c | 151 ++++++++++++++++++++++++++++++++++++++++++++------
1 file changed, 134 insertions(+), 17 deletions(-)
diff --git a/drivers/md/dm-crypt.c b/drivers/md/dm-crypt.c
index 608b617fb817..86adb2f9b94c 100644
--- a/drivers/md/dm-crypt.c
+++ b/drivers/md/dm-crypt.c
@@ -9,12 +9,14 @@
*/
#include <linux/completion.h>
+#include <linux/crash_core.h>
#include <linux/err.h>
#include <linux/module.h>
#include <linux/hex.h>
#include <linux/init.h>
#include <linux/kernel.h>
#include <linux/key.h>
+#include <linux/list.h>
#include <linux/bio.h>
#include <linux/blkdev.h>
#include <linux/blk-integrity.h>
@@ -234,6 +236,11 @@ struct crypt_config {
struct mutex bio_alloc_lock;
u8 *authenc_key; /* space for keys in authenc() format (if used) */
+
+#ifdef CONFIG_CRASH_WIPE_SECRETS
+ struct list_head wipe_list;
+#endif
+
u8 key[] __counted_by(key_size);
};
@@ -243,6 +250,12 @@ struct crypt_config {
static DEFINE_SPINLOCK(dm_crypt_clients_lock);
static unsigned int dm_crypt_clients_n;
+
+#ifdef CONFIG_CRASH_WIPE_SECRETS
+static LIST_HEAD(dm_crypt_wipe_list);
+static DEFINE_SPINLOCK(dm_crypt_wipe_list_lock);
+#endif
+
static volatile unsigned long dm_crypt_pages_per_client;
#define DM_CRYPT_MEMORY_PERCENT 2
#define DM_CRYPT_MIN_PAGES_PER_CLIENT (BIO_MAX_VECS * 16)
@@ -460,8 +473,7 @@ static void crypt_iv_lmk_dtr(struct crypt_config *cc)
{
struct iv_lmk_private *lmk = &cc->iv_gen_private.lmk;
- kfree_sensitive(lmk->seed);
- lmk->seed = NULL;
+ kfree_sensitive(xchg(&lmk->seed, NULL));
}
static int crypt_iv_lmk_ctr(struct crypt_config *cc, struct dm_target *ti,
@@ -582,10 +594,8 @@ static void crypt_iv_tcw_dtr(struct crypt_config *cc)
{
struct iv_tcw_private *tcw = &cc->iv_gen_private.tcw;
- kfree_sensitive(tcw->iv_seed);
- tcw->iv_seed = NULL;
- kfree_sensitive(tcw->whitening);
- tcw->whitening = NULL;
+ kfree_sensitive(xchg(&tcw->iv_seed, NULL));
+ kfree_sensitive(xchg(&tcw->whitening, NULL));
}
static int crypt_iv_tcw_ctr(struct crypt_config *cc, struct dm_target *ti,
@@ -761,8 +771,7 @@ static void crypt_iv_elephant_dtr(struct crypt_config *cc)
{
struct iv_elephant_private *elephant = &cc->iv_gen_private.elephant;
- kfree_sensitive(elephant->key);
- elephant->key = NULL;
+ kfree_sensitive(xchg(&elephant->key, NULL));
}
static int crypt_iv_elephant_ctr(struct crypt_config *cc, struct dm_target *ti,
@@ -2547,8 +2556,7 @@ static int crypt_set_keyring_key(struct crypt_config *cc, const char *key_string
goto free_new_key_string;
set_bit(DM_CRYPT_KEY_VALID, &cc->flags);
- kfree_sensitive(cc->key_string);
- cc->key_string = new_key_string;
+ kfree_sensitive(xchg(&cc->key_string, new_key_string));
return 0;
free_new_key_string:
@@ -2612,8 +2620,7 @@ static int crypt_set_key(struct crypt_config *cc, char *key)
clear_bit(DM_CRYPT_KEY_VALID, &cc->flags);
/* wipe references to any kernel keyring key */
- kfree_sensitive(cc->key_string);
- cc->key_string = NULL;
+ kfree_sensitive(xchg(&cc->key_string, NULL));
/* Decode key from its hex representation. */
if (cc->key_size && hex2bin(cc->key, key, cc->key_size) < 0)
@@ -2641,14 +2648,97 @@ static int crypt_wipe_key(struct crypt_config *cc)
if (cc->iv_gen_ops && cc->iv_gen_ops->wipe)
cc->iv_gen_ops->wipe(cc);
- kfree_sensitive(cc->key_string);
- cc->key_string = NULL;
+ kfree_sensitive(xchg(&cc->key_string, NULL));
r = crypt_setkey(cc);
memset(&cc->key, 0, cc->key_size * sizeof(u8));
return r;
}
+#ifdef CONFIG_CRASH_WIPE_SECRETS
+static void crypt_crash_wipe_iv(struct crypt_config *cc)
+{
+ if (cc->iv_gen_ops == &crypt_iv_lmk_ops) {
+ struct iv_lmk_private *lmk = &cc->iv_gen_private.lmk;
+
+ if (lmk->seed)
+ crash_wipe_memzero(lmk->seed, LMK_SEED_SIZE);
+ } else if (cc->iv_gen_ops == &crypt_iv_tcw_ops) {
+ struct iv_tcw_private *tcw = &cc->iv_gen_private.tcw;
+
+ if (tcw->iv_seed)
+ crash_wipe_memzero(tcw->iv_seed, cc->iv_size);
+ if (tcw->whitening)
+ crash_wipe_memzero(tcw->whitening, TCW_WHITENING_SIZE);
+ } else if (cc->iv_gen_ops == &crypt_iv_elephant_ops) {
+ struct iv_elephant_private *elephant =
+ &cc->iv_gen_private.elephant;
+
+ if (elephant->key)
+ crash_wipe_memzero(elephant->key,
+ sizeof(*elephant->key));
+ }
+}
+
+static int crypt_crash_wipe(struct notifier_block *nb, unsigned long action,
+ void *data)
+{
+ struct crypt_config *cc;
+
+ /* No locking: all other CPUs are stopped, and a cc is unlinked before
+ * it is freed, so the forward walk can't reach freed memory.
+ */
+ list_for_each_entry(cc, &dm_crypt_wipe_list, wipe_list) {
+ crash_wipe_memzero(cc->key, cc->key_size);
+
+ if (cc->authenc_key)
+ crash_wipe_memzero(cc->authenc_key,
+ crypt_authenckey_size(cc));
+
+ if (cc->key_string)
+ crash_wipe_memzero(cc->key_string,
+ strlen(cc->key_string));
+
+ crypt_crash_wipe_iv(cc);
+ }
+
+ return NOTIFY_DONE;
+}
+
+static struct notifier_block crypt_crash_wipe_nb = {
+ .notifier_call = crypt_crash_wipe
+};
+
+static void crypt_track_cc(struct crypt_config *cc)
+{
+ spin_lock(&dm_crypt_wipe_list_lock);
+ list_add(&cc->wipe_list, &dm_crypt_wipe_list);
+ spin_unlock(&dm_crypt_wipe_list_lock);
+}
+
+static void crypt_untrack_cc(struct crypt_config *cc)
+{
+ spin_lock(&dm_crypt_wipe_list_lock);
+ list_del(&cc->wipe_list);
+ spin_unlock(&dm_crypt_wipe_list_lock);
+}
+
+static void crypt_crash_wipe_init(void)
+{
+ crash_wipe_secrets_register(&crypt_crash_wipe_nb);
+}
+
+static void crypt_crash_wipe_exit(void)
+{
+ crash_wipe_secrets_unregister(&crypt_crash_wipe_nb);
+}
+#else
+static void crypt_track_cc(struct crypt_config *cc) { }
+static void crypt_untrack_cc(struct crypt_config *cc) { }
+static void crypt_crash_wipe_init(void) { }
+static void crypt_crash_wipe_exit(void) { }
+#endif /* CONFIG_CRASH_WIPE_SECRETS */
+
static void crypt_calculate_pages_per_client(void)
{
unsigned long pages = (totalram_pages() - totalhigh_pages()) * DM_CRYPT_MEMORY_PERCENT / 100;
@@ -2729,12 +2819,15 @@ static void crypt_dtr(struct dm_target *ti)
dm_put_device(ti, cc->dev);
kfree_sensitive(cc->cipher_string);
- kfree_sensitive(cc->key_string);
+ kfree_sensitive(xchg(&cc->key_string, NULL));
kfree_sensitive(cc->cipher_auth);
- kfree_sensitive(cc->authenc_key);
+ kfree_sensitive(xchg(&cc->authenc_key, NULL));
mutex_destroy(&cc->bio_alloc_lock);
+ memzero_explicit(cc->key, cc->key_size);
+ crypt_untrack_cc(cc);
+
/* Must zero key material before freeing */
kfree_sensitive(cc);
@@ -3210,6 +3303,8 @@ static int crypt_ctr(struct dm_target *ti, unsigned int argc, char **argv)
ti->private = cc;
+ crypt_track_cc(cc);
+
spin_lock(&dm_crypt_clients_lock);
dm_crypt_clients_n++;
crypt_calculate_pages_per_client();
@@ -3716,7 +3811,29 @@ static struct target_type crypt_target = {
.iterate_devices = crypt_iterate_devices,
.io_hints = crypt_io_hints,
};
-module_dm(crypt);
+
+static int __init dm_crypt_init(void)
+{
+ int r;
+
+ crypt_crash_wipe_init();
+
+ r = dm_register_target(&crypt_target);
+ if (r) {
+ crypt_crash_wipe_exit();
+ return r;
+ }
+
+ return 0;
+}
+module_init(dm_crypt_init);
+
+static void __exit dm_crypt_exit(void)
+{
+ crypt_crash_wipe_exit();
+ dm_unregister_target(&crypt_target);
+}
+module_exit(dm_crypt_exit);
MODULE_AUTHOR("Jana Saout <jana@saout.de>");
MODULE_DESCRIPTION(DM_NAME " target for transparent encryption / decryption");
--
2.53.0
^ permalink raw reply related [flat|nested] 15+ messages in thread
* Re: [PATCH v2 00/13] CRASH_WIPE_SECRETS: Wipe secrets before kdump (was: CRASH_ZEROIZE)
2026-08-11 17:52 [PATCH v2 00/13] CRASH_WIPE_SECRETS: Wipe secrets before kdump (was: CRASH_ZEROIZE) Jan Sebastian Götte
` (12 preceding siblings ...)
2026-08-11 17:53 ` [PATCH v2 13/13] dm crypt: wipe key material " Jan Sebastian Götte
@ 2026-08-11 18:20 ` Eric Biggers
13 siblings, 0 replies; 15+ messages in thread
From: Eric Biggers @ 2026-08-11 18:20 UTC (permalink / raw)
To: Jan Sebastian Götte
Cc: Andrew Morton, Baoquan He, Mike Rapoport, Pasha Tatashin,
Pratyush Yadav, Dave Young, Catalin Marinas, Will Deacon,
David Howells, Jarkko Sakkinen, Jonathan Corbet, Shuah Khan,
Paul Moore, James Morris, Serge E. Hallyn, Lukas Wunner,
Ignat Korchagin, Herbert Xu, David S. Miller, Keith Busch,
Jens Axboe, Christoph Hellwig, Sagi Grimberg, Trond Myklebust,
Anna Schumaker, Mimi Zohar, James Bottomley, Marc Dionne,
Eric Dumazet, Jakub Kicinski, Paolo Abeni, Simon Horman,
Theodore Y. Ts'o, Jaegeuk Kim, Alexander Viro,
Christian Brauner, Jan Kara, Alasdair Kergon, Mike Snitzer,
Mikulas Patocka, Benjamin Marzinski, kexec, linux-kernel,
linux-arm-kernel, linux-mm, keyrings, linux-doc,
linux-security-module, linux-crypto, linux-nvme, linux-nfs,
linux-integrity, linux-afs, netdev, linux-fscrypt, linux-fsdevel,
dm-devel
On Tue, Aug 11, 2026 at 07:52:49PM +0200, Jan Sebastian Götte wrote:
> I'm using linux on an embedded target in a Hardware Security Module-like
> application. One requirement is that I want the system to be able to
> quickly erase its memory when it detects physical tampering. I'm
> approaching that by using kdump to load into a small payload that
> instead of dumping RAM, erases RAM from start to end. However, writing
> all of RAM, especially on an embedded target, is rather slow. For this
> reason, I propose the mechanism in this patch series:
>
> Add CONFIG_CRASH_ZEROIZE (default off), which when enabled makes various
> subsystems handling secret data do a quick, targeted wipe of these
> secrets before kdump. This behavior might also be interesting in cases
> where you run a normal kdump kernel but you still want to keep things
> like fde crypto keys out of these dumps.
>
> CONFIG_CRASH_ZEROIZE is a best effort, defense in depth solution. There
> are circumstances, such as when a panic is triggered after memory
> corruption, or when a panic interrupts some operation that mutates data
> structures under locks, when the kernel cannot safely wipe some memory
> areas. The handlers proposed in this series will just print a warning
> and skip the affected areas in this case.
>
> This series introduces handlers for the major locations I found where
> having this sort of thing makes sense. Notable omissions right now are
> the Ceph and CIFS subsystems. I have WIP patches for these, but since I
> can't easily test them right now, I omitted them from this patch set for
> now. Currently included locations are:
>
> * various key types in security/keys
> * rxrpc
> * fscrypt
> * dm-crypt
> * crypto tfm instances
> * secretmem (which I'm going to start using in my application)
>
> I've verified this patch series on an ARM64 target using the helper code
> at https://codeberg.org/yasec/crash-wipe-test . This code stuffs the
> affected kernel subsystems with keys and secret data, then crashes the
> system, takes a RAM dump and verifies the dump is clean of secrets. Note
> that the helper code is partially LLM-generated, so read with care. It
> passes a positive control test with the config option disabled.
>
> The patch series applies on top of linux-next but should work on 7.0.0,
> too. I've tested the patches on a Arduino uno Q (Qualcomm QRB2210,
> ARM64) embedded target.
Can we not? It's already hard enough to zeroize keys and data at the
normal end of their lifetime in the kernel: that's something that is
always a struggle, with fix patches regularly going by for many years
and many subsystems never fixed at all. If we can barely even do that,
we aren't going to be able to correctly and completely implement and
maintain separate zeroization code for every kernel subsystem that runs
only on kernel panics and has special constraints, like not being able
to take locks.
This is never going to be done either, with the scope always wanting to
grow to include other keys and data. You may think this series covers
"almost everything" but it's actually not even close.
Could you perhaps narrow the scope to one or two things that actually
are useful and can reasonably be supported for the application? For
example, secretmem seems to be the only thing you mentioned that you're
actually planning to use.
- Eric
^ permalink raw reply [flat|nested] 15+ messages in thread
end of thread, other threads:[~2026-08-11 18:20 UTC | newest]
Thread overview: 15+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2026-08-11 17:52 [PATCH v2 00/13] CRASH_WIPE_SECRETS: Wipe secrets before kdump (was: CRASH_ZEROIZE) Jan Sebastian Götte
2026-08-11 17:52 ` [PATCH v2 01/13] kexec: add CRASH_WIPE_SECRETS to wipe secrets before kdump Jan Sebastian Götte
2026-08-11 17:52 ` [PATCH v2 02/13] crash-core: Flush caches on CRASH_WIPE_SECRETS Jan Sebastian Götte
2026-08-11 17:52 ` [PATCH v2 03/13] arm64/mm: add set_direct_map_default_nosplit() Jan Sebastian Götte
2026-08-11 17:52 ` [PATCH v2 04/13] mm/secretmem: wipe secret pages before kdump Jan Sebastian Götte
2026-08-11 17:52 ` [PATCH v2 05/13] security/keys: wipe key payloads " Jan Sebastian Götte
2026-08-11 17:52 ` [PATCH v2 06/13] security/keys: implement wipe op for user-type keys Jan Sebastian Götte
2026-08-11 17:52 ` [PATCH v2 07/13] security/keys: implement wipe op for big_key Jan Sebastian Götte
2026-08-11 17:52 ` [PATCH v2 08/13] security/keys: implement wipe op for trusted and encrypted keys Jan Sebastian Götte
2026-08-11 17:52 ` [PATCH v2 09/13] security/keys: implement wipe op for asymmetric keys Jan Sebastian Götte
2026-08-11 17:52 ` [PATCH v2 10/13] rxrpc: implement wipe op for rxrpc keys Jan Sebastian Götte
2026-08-11 17:53 ` [PATCH v2 11/13] fscrypt: wipe master keys before kdump Jan Sebastian Götte
2026-08-11 17:53 ` [PATCH v2 12/13] crypto: api - wipe tfm contexts " Jan Sebastian Götte
2026-08-11 17:53 ` [PATCH v2 13/13] dm crypt: wipe key material " Jan Sebastian Götte
2026-08-11 18:20 ` [PATCH v2 00/13] CRASH_WIPE_SECRETS: Wipe secrets before kdump (was: CRASH_ZEROIZE) Eric Biggers
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox