CRASH_WIPE_SECRETS: Wipe secrets before kdump (was: CRASH_ZEROIZE)
Subsystems (22)
v2 cover CRASH_WIPE_SECRETS: Wipe secrets before kdump (was: CRASH_ZEROIZE)
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.
---
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/[email protected]
To: Andrew Morton <[email protected]>
To: Baoquan He <[email protected]>
To: Mike Rapoport <[email protected]>
To: Pasha Tatashin <[email protected]>
To: Pratyush Yadav <[email protected]>
To: Dave Young <[email protected]>
To: Catalin Marinas <[email protected]>
To: Will Deacon <[email protected]>
To: David Howells <[email protected]>
To: Jarkko Sakkinen <[email protected]>
To: Jonathan Corbet <[email protected]>
To: Shuah Khan <[email protected]>
To: Paul Moore <[email protected]>
To: James Morris <[email protected]>
To: "Serge E. Hallyn" <[email protected]>
To: Lukas Wunner <[email protected]>
To: Ignat Korchagin <[email protected]>
To: Herbert Xu <[email protected]>
To: "David S. Miller" <[email protected]>
To: Keith Busch <[email protected]>
To: Jens Axboe <[email protected]>
To: Christoph Hellwig <[email protected]>
To: Sagi Grimberg <[email protected]>
To: Trond Myklebust <[email protected]>
To: Anna Schumaker <[email protected]>
To: Mimi Zohar <[email protected]>
To: James Bottomley <[email protected]>
To: Marc Dionne <[email protected]>
To: Eric Dumazet <[email protected]>
To: Jakub Kicinski <[email protected]>
To: Paolo Abeni <[email protected]>
To: Simon Horman <[email protected]>
To: Eric Biggers <[email protected]>
To: "Theodore Y. Ts'o" <[email protected]>
To: Jaegeuk Kim <[email protected]>
To: Alexander Viro <[email protected]>
To: Christian Brauner <[email protected]>
To: Jan Kara <[email protected]>
To: Alasdair Kergon <[email protected]>
To: Mike Snitzer <[email protected]>
To: Mikulas Patocka <[email protected]>
To: Benjamin Marzinski <[email protected]>
Cc: [email protected]
Cc: [email protected]
Cc: [email protected]
Cc: [email protected]
Cc: [email protected]
Cc: [email protected]
Cc: [email protected]
Cc: [email protected]
Cc: [email protected]
Cc: [email protected]
Cc: [email protected]
Cc: [email protected]
Cc: [email protected]
Cc: [email protected]
Cc: [email protected]
Cc: [email protected]
---
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,
Signature · 1 line
Jan Sebastian Götte <[email protected]>
PATCH v2 1/13 kexec: add CRASH_WIPE_SECRETS to wipe secrets before kdump
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.
--- include/linux/crash_core.h | 16 ++++++++++++++++ kernel/Kconfig.kexec | 16 ++++++++++++++++ kernel/crash_core.c | 29 +++++++++++++++++++++++++++++ 3 files changed, 61 insertions(+)
include/linux/crash_core.h +16 -0
@@ -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_eventstatic inline void arch_crash_handle_hotplug_event (struct kimage *image,void *arg) { }#endif
kernel/Kconfig.kexec +16 -0
@@ -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
kernel/crash_core.c +29 -0
@@ -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 ();
Signature · 1 line
2.53.0
Baoquan He
maintainerRe: [PATCH v2 01/13] kexec: add CRASH_WIPE_SECRETS to wipe secrets before kdump
On 08/11/26 at 07:52pm, Jan Sebastian Götte wrote:
.....snip...
kernel/crash_core.c void __noclone __crash_kexec(struct pt_regs *regs) · line 173
Quoted 5 lines
kernel/crash_core.c +1
-0@@ -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 ();
I remember when crash_cma added this, we argued a lot about the risk, finally it's added in with an added note to tell it's an opt-in feature and risky. I don't know if it opens a Pandora box, I think anything wanted being here need prove anything else has been tried, and the thing is very very important. If we don't guard the door well, one day one guy cold come and said I need ask Oracle database to do some transactions before juming into kdump kernel because the actions will save billions of dollar.
And what does security people say about this wiping? Is it very important information and wiping is mandotary? There's no any other way to wipe it or pollute it, no way in kdump kernel? I noticed it's happening on en embedded target, is it possible you do this on your embedded kernel tree, but not mainline tree?
Anyone who wants to do thing like this, please prepare answers for them, and be ready to accept failure.
Thanks
Baoquan
Quoted 7 lines
kernel/crash_core.c +0
-0machine_kexec (kexec_crash_image); }kexec_unlock ();Signature · 2 lines
-- 2.53.0
Re: [PATCH v2 01/13] kexec: add CRASH_WIPE_SECRETS to wipe secrets before kdump
Quoted 16 lines · Baoquan He
On 08/11/26 at 07:52pm, Jan Sebastian Götte wrote:
.....snip...kernel/crash_core.c void __noclone __crash_kexec(struct pt_regs *regs) · line 173
Quoted 5 lines
kernel/crash_core.c +1
-0@@ -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 ();I remember when
crash_cmaadded this, we argued a lot about the risk, finally it's added in with an added note to tell it's an opt-in feature and risky. I don't know if it opens a Pandora box, I think anything wanted being here need prove anything else has been tried, and the thing is very very important. If we don't guard the door well, one day one guy cold come and said I need ask Oracle database to do some transactions before juming into kdump kernel because the actions will save billions of dollar.
I see your point.
Quoted 3 lines
And what does security people say about this wiping? Is it very important information and wiping is mandotary? There's no any other way to wipe it or pollute it, no way in kdump kernel?
I'm doing both - my kdump kernel does a linear memzero of all of DRAM. However, that's too slow - it takes several hundred milliseconds, and I want these cryptographic keys gone within no more than 10 ms.
Doing this in the original kernel is cleanest I think, because to do a targeted erase in the kdump kernel, that kernel would have to know all the original kernel's symbols, and it would have to forensically reconstruct it's memory content similar to the crash utility. I think that's too complex and error-prone to do in this use case.
I can imagine one alternative way to approach this, tell me what you think: Instead of registering wipe handlers that memzero places on panic, I could put an optional registry of (addr, len) descriptors into crashkernel memory that the original kernel populates with the PAs of buffers to clear ahead of time. Then the kdump kernel could do the actual memzero. This would remove all code from the actual kdump path here. The registry could be made per-core to avoid locks.
Quoted 3 lines
I noticed it's
happening on en embedded target, is it possible you do this on your embedded kernel tree, but not mainline tree?
I could keep it in a vendor tree, but I think the functionality is useful beyond my particular use case.
Lukas Wunner
maintainerRe: [PATCH v2 01/13] kexec: add CRASH_WIPE_SECRETS to wipe secrets before kdump
Quoted 7 lines · Jan Sebastian Götte
I can imagine one alternative way to approach this, tell me what you think:
Instead of registering wipe handlers that memzero places on panic, I could put an optional registry of (addr, len) descriptors into crashkernel memory that the original kernel populates with the PAs of buffers to clear ahead of time. Then the kdump kernel could do the actual memzero. This would remove all code from the actual kdump path here. The registry could be made per-core to avoid locks.
Or create a dedicated mempool from which all sensitive data is allocated, then call mempool_free_bulk() on kexec to poison everything in that pool?
Thanks,
Lukas
Baoquan He
maintainerRe: [PATCH v2 01/13] kexec: add CRASH_WIPE_SECRETS to wipe secrets before kdump
Quoted 8 lines · Lukas Wunner
Quoted 7 lines · Jan Sebastian Götte
I can imagine one alternative way to approach this, tell me what you think:
Instead of registering wipe handlers that memzero places on panic, I could put an optional registry of (addr, len) descriptors into crashkernel memory that the original kernel populates with the PAs of buffers to clear ahead of time. Then the kdump kernel could do the actual memzero. This would remove all code from the actual kdump path here. The registry could be made per-core to avoid locks.
Since there are a lot of security expert in CC, I am wondering what's the risk if we wipe secrets during dumping. System is located in user's place, and vmcore is dumped and collected in user's place. If we filter out secrets memory when we dumping, what could be leaked out?
Quoted 7 lines
Or create a dedicated mempool from which all sensitive data is allocated, then call
mempool_free_bulk()on kexec to poison everything in that pool?Thanks,
Lukas
Baoquan He
maintainerRe: [PATCH v2 01/13] kexec: add CRASH_WIPE_SECRETS to wipe secrets before kdump
Quoted 9 lines · Baoquan He
Quoted 8 lines · Lukas Wunner
Quoted 7 lines · Jan Sebastian Götte
I can imagine one alternative way to approach this, tell me what you think:
Instead of registering wipe handlers that memzero places on panic, I could put an optional registry of (addr, len) descriptors into crashkernel memory that the original kernel populates with the PAs of buffers to clear ahead of time. Then the kdump kernel could do the actual memzero. This would remove all code from the actual kdump path here. The registry could be made per-core to avoid locks.
If this has to be done in kernel, your (addr, len) registry idea is a better direction, and we can make it a bit more general.
Instead of a secrets-only wipe registry, consider a small "crash memory region" table with an action per entry:
enum crash_region_action {
CRASH_REG_WIPE, /* memzero, then dump normally (leave zeros) */
CRASH_REG_SKIP, /* do not include in the vmcore at all */
};
struct crash_region {
phys_addr_t addr;
size_t len;
enum crash_region_action action;
};void crash_reg_add(phys_addr_t addr, size_t len,
enum crash_region_action action);
void crash_reg_del(phys_addr_t addr, size_t len);
Two actions because the use cases differ:
- WIPE is what you need: zero the secret,
- SKIP covers things people do not want in the dump at all — Registering as SKIP reduce dump size. I have been considering how to shrink vmcore size on some big system with huge memory, the vmcore size usually is several or tens of GB.
If this is OK to you, you can start a regiestry table infrastructure with wiping secrets, we can add SKIP later and plus some driver used memory.
I am not sure if it's satisfactory, but you can give it a shot to show what it looks like.
Thanks
Baoquan
Quoted 13 lines
Since there are a lot of security expert in CC, I am wondering what's the risk if we wipe secrets during dumping. System is located in user's place, and vmcore is dumped and collected in user's place. If we filter out secrets memory when we dumping, what could be leaked out?
Quoted 7 lines
Or create a dedicated mempool from which all sensitive data is allocated, then call
mempool_free_bulk()on kexec to poison everything in that pool?Thanks,
Lukas
Re: [PATCH v2 01/13] kexec: add CRASH_WIPE_SECRETS to wipe secrets before kdump
Quoted 40 lines · Baoquan He
Quoted 9 lines · Baoquan He
Quoted 8 lines · Lukas Wunner
Quoted 7 lines · Jan Sebastian Götte
I can imagine one alternative way to approach this, tell me what you think:
Instead of registering wipe handlers that memzero places on panic, I could put an optional registry of (addr, len) descriptors into crashkernel memory that the original kernel populates with the PAs of buffers to clear ahead of time. Then the kdump kernel could do the actual memzero. This would remove all code from the actual kdump path here. The registry could be made per-core to avoid locks.If this has to be done in kernel, your (addr, len) registry idea is a better direction, and we can make it a bit more general.
Instead of a secrets-only wipe registry, consider a small "crash memory region" table with an action per entry:
enum
crash_region_action{
CRASH_REG_WIPE, /* memzero, then dump normally (leave zeros) */
CRASH_REG_SKIP, /* do not include in the vmcore at all */
};struct crash_region { phys_addr_t addr; size_t len; enum crash_region_action action; };void crash_reg_add(phys_addr_t addr,
size_tlen,enum crash_region_action action);void
crash_reg_del(phys_addr_t addr, size_t len);Two actions because the use cases differ:
- WIPE is what you need: zero the secret,
- SKIP covers things people do not want in the dump at all
— Registering as SKIP reduce dump size. I have been considering how
to shrink vmcore size on some big system with huge memory, the vmcore size usually is several or tens of GB.
Quoted 6 lines
If this is OK to you, you can start a regiestry table infrastructure with wiping secrets, we can add SKIP later and plus some driver used memory.
I am not sure if it's satisfactory, but you can give it a shot to show what it looks like.
I think that's a good idea. I'll post a patch adding that registry this or next week.
Thanks,
Jan
PATCH v2 2/13 crash-core: Flush caches on CRASH_WIPE_SECRETS
In reply to Jan Sebastian GötteReplace 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.
--- arch/arm64/kernel/machine_kexec.c | 20 ++++++++++++++++++++ include/linux/crash_core.h | 21 +++++++++++++++++++++ kernel/crash_core.c | 23 +++++++++++++++++++++++ 3 files changed, 64 insertions(+)
arch/arm64/kernel/machine_kexec.c +20 -0
@@ -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
include/linux/crash_core.h +21 -0
@@ -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);
kernel/crash_core.c +23 -0
@@ -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
Signature · 1 line
2.53.0
PATCH v2 3/13 arm64/mm: add set_direct_map_default_nosplit()
In reply to Jan Sebastian Götteset_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().
--- 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(-)
arch/arm64/include/asm/set_memory.h +2 -0
@@ -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_nosplitint set_direct_map_valid_noflush (struct page *page,unsigned nr,bool valid);bool kernel_page_present (struct page *page);
arch/arm64/mm/pageattr.c +21 -0
@@ -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)
include/linux/set_memory.h +16 -1
@@ -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.
Signature · 1 line
2.53.0
PATCH v2 4/13 mm/secretmem: wipe secret pages before kdump
In reply to Jan Sebastian GötteRegister 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.
--- mm/secretmem.c | 53 +++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 53 insertions(+)
mm/secretmem.c +53 -0
@@ -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);
Signature · 1 line
2.53.0
PATCH v2 5/13 security/keys: wipe key payloads before kdump
In reply to Jan Sebastian GötteWhen 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.
--- Documentation/security/keys/core.rst | 13 +++++++++++ include/linux/key-type.h | 9 ++++++++ security/keys/key.c | 43 ++++++++++++++++++++++++++++++++++++ 3 files changed, 65 insertions(+)
Documentation/security/keys/core.rst +13 -0
@@ -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
include/linux/key-type.h +9 -0
@@ -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);
security/keys/key.c +43 -0
59 lines not on this page · raw message
Signature · 1 line
2.53.0
PATCH v2 6/13 security/keys: implement wipe op for user-type keys
In reply to Jan Sebastian GötteWipe 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.
--- 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(+)
crypto/asymmetric_keys/pkcs7_key_type.c +1 -0
@@ -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, };
drivers/nvme/common/keyring.c +1 -0
@@ -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, };
fs/nfs/nfs4idmap.c +2 -0
@@ -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,
include/keys/user-type.h +1 -0
@@ -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);
security/keys/user_defined.c +15 -0
@@ -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 */
Signature · 1 line
2.53.0
PATCH v2 7/13 security/keys: implement wipe op for big_key
In reply to Jan Sebastian GötteFor 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.
--- security/keys/big_key.c | 17 +++++++++++++++++ 1 file changed, 17 insertions(+)
security/keys/big_key.c +17 -0
@@ -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 */
Signature · 1 line
2.53.0
PATCH v2 8/13 security/keys: implement wipe op for trusted and encrypted keys
In reply to Jan Sebastian GötteWipe the decrypted key material and the sealed blob held in the payload.
Used by CONFIG_CRASH_WIPE_SECRETS.
--- security/keys/encrypted-keys/encrypted.c | 13 +++++++++++++ security/keys/trusted-keys/trusted_core.c | 15 +++++++++++++++ 2 files changed, 28 insertions(+)
security/keys/encrypted-keys/encrypted.c +13 -0
@@ -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, };
security/keys/trusted-keys/trusted_core.c +15 -0
@@ -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, };
Signature · 1 line
2.53.0
PATCH v2 9/13 security/keys: implement wipe op for asymmetric keys
In reply to Jan Sebastian GötteAdd wipe op to the subtype and implement it for public_key.
Used by CONFIG_CRASH_WIPE_SECRETS.
--- 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(+)
crypto/asymmetric_keys/asymmetric_type.c +11 -0
@@ -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,
crypto/asymmetric_keys/public_key.c +15 -0
@@ -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,
include/keys/asymmetric-subtype.h +5 -0
@@ -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);
Signature · 1 line
2.53.0
PATCH v2 10/13 rxrpc: implement wipe op for rxrpc keys
In reply to Jan Sebastian GötteWipe 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.
--- 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(+)
net/rxrpc/ar-internal.h +5 -0
@@ -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 *);
net/rxrpc/key.c +34 -0
@@ -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 */
net/rxrpc/rxgk.c +11 -0
@@ -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,
net/rxrpc/rxkad.c +14 -0
@@ -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,
net/rxrpc/server_key.c +11 -0
@@ -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 ];
Signature · 1 line
2.53.0
PATCH v2 11/13 fscrypt: wipe master keys before kdump
In reply to Jan Sebastian Göttefscrypt 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.
--- 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(-)
fs/crypto/block.c +10 -0
@@ -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) {
fs/crypto/fscrypt_private.h +15 -0
@@ -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);
fs/crypto/keyring.c +67 -1
103 lines not on this page · raw message
fs/crypto/keysetup_v1.c +15 -0
@@ -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
fs/super.c +29 -0
@@ -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
include/linux/fs.h +4 -0
@@ -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)))
Signature · 1 line
2.53.0
PATCH v2 12/13 crypto: api - wipe tfm contexts before kdump
In reply to Jan Sebastian Göttetfm 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.
--- crypto/api.c | 78 ++++++++++++++++++++++++++++++++++++++++++++------ include/linux/crypto.h | 11 +++++++ 2 files changed, 81 insertions(+), 8 deletions(-)
crypto/api.c +70 -8
@@ -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);
include/linux/crypto.h +11 -0
@@ -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; };
Signature · 1 line
2.53.0
PATCH v2 13/13 dm crypt: wipe key material before kdump
In reply to Jan Sebastian GötteWipe 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.
--- drivers/md/dm-crypt.c | 151 ++++++++++++++++++++++++++++++++++++++++++++------ 1 file changed, 134 insertions(+), 17 deletions(-)
drivers/md/dm-crypt.c +134 -17
@@ -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 <[email protected]>" );MODULE_DESCRIPTION (DM_NAME " target for transparent encryption / decryption" );
Signature · 1 line
2.53.0
Re: [PATCH v2 13/13] dm crypt: wipe key material before kdump
Quoted 8 lines · 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_configinstances whenCONFIG_CRASH_WIPE_SECRETSis 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.
Well, dm-crypt has crypt_wipe_key(), which can be called through a device-mapper message. It also sets keys to zero in the crypto API.
Why do we need yet another way to wipe keys here, reimplementing everything twice?
I can imagine an emergency wrapper callback that will suspend dm-crypt and call existing code.
Quoted 6 lines
Use custom wipe handlers even for things like ivs that have existing wipe functions elsewhere because we need to use
crash_wipe_memzeroinstead ofmemzero_explicit. Thecrash_wipehelper 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.
Please no. It looks to me like you are trying to fix this on the wrong layer. This way everyone will need their own memzero...
Dunno, but I really do not like dm-crypt becoming completely bloated with code that has nothing to do with the original purpose of this driver.
Milan
Quoted 6 lines
Signed-off-by Jan Sebastian Götte <[email protected]>--- drivers/md/dm-crypt.c | 151 ++++++++++++++++++++++++++++++++++++++++++++------ 1 file changed, 134 insertions(+), 17 deletions(-)
Re: [PATCH v2 13/13] dm crypt: wipe key material before kdump
Quoted 10 lines · Milan Broz
Quoted 8 lines · 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_configinstances whenCONFIG_CRASH_WIPE_SECRETSis 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.
Quoted 6 lines
Well, dm-crypt has
crypt_wipe_key(), which can be called through a device-mapper message. It also sets keys to zero in the crypto API.Why do we need yet another way to wipe keys here, reimplementing everything twice?
I can imagine an emergency wrapper callback that will suspend dm-crypt and call existing code.
I originally decided I'd keep these function separate since you can't rely on memory allocation/freeing to work during panic. crypt_wipe_key currently calls kfree_sensitive, and inside crypto_*_setkey there's also kalloc/kfree calls hiding.
I could rework the patch to call into crypt_wipe_key, but I'd have to make that avoid memory allocation/freeing. The direct kfree_sensitive call can be replaced with a memzero_explicit, but I think I'd have to add a dedicated "wipe without allocations" function to the crypto backends as an alternative to setkey with a zero key.
Quoted 9 lines
Quoted 6 lines
Use custom wipe handlers even for things like ivs that have existing wipe functions elsewhere because we need to use
crash_wipe_memzeroinstead ofmemzero_explicit. Thecrash_wipehelper 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.Please no. It looks to me like you are trying to fix this on the wrong layer. This way everyone will need their own memzero...
You're probably right. I'll remove this from the next version and make sure the caches are flushed properly during kexec instead.
Quoted 2 lines
Dunno, but I really do not like dm-crypt becoming completely bloated > with code that has nothing to do with the original purpose of this driver.
I feel like "delete key quick" is a pretty normal function for crypto code.
Eric Biggers
maintainerQuoted 41 lines · 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_ZEROIZEis 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.
Quoted 3 lines
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
Quoted 46 lines · Eric Biggers
Quoted 41 lines · 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_ZEROIZEis 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.
Quoted 3 lines
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.
Quoted 8 lines
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.
These crash wipe code paths are less critical than places where you'd use memzero_explicit or kfree_sensitive. They are "only" a defense in depth / hardening measure. For most of these, if the crash happens at the exact time when data structures are being modified or when things are being intialized/deinitialized, they can skip things so they can never be 100% reliable anyway.
It's not realistic to clean *all* sensitive data in the first place, since that would include plaintext as well, which would mean cleaning the entire page cache which would be too complex, take too long, and destroy forensic evidence that people might want to keep intact for regular kdump uses.
Quoted 3 lines
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.
I'm sure there's always more. In my particular application, I'm using this not for complete coverage (for that I have another full memory wipe afterwards), but for speed - I want the obviously sensitive stuff like keys gone as fast as possible, with the full wipe cleaning up whatever the fast wipe here missed.
Quoted 4 lines
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.
I'm open to whittling down the scope of this series. In my use case I need at least secretmem as well as dm-crypt, which then pulls in the crypto tfm stuff. Personally, I could live without the other bits, I just thought they'd be easy enough to include.
- Jan
Eric Biggers
maintainerQuoted 9 lines · Jan Sebastian Götte
Quoted 4 lines
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.
I'm open to whittling down the scope of this series. In my use case I need at least secretmem as well as dm-crypt, which then pulls in the crypto tfm stuff. Personally, I could live without the other bits, I just thought they'd be easy enough to include.
Please do reduce the scope.
Note that even if dm-crypt is in scope, "crypto: api - wipe tfm contexts before kdump" is neither required nor sufficient. For one, dm-crypt has its own copy of the key. The wiping should be done by the kernel feature(s) that need it, as they know what actually needs to be wiped, and they can do so without the bottleneck of a new global list.
- Eric