| Seq | Timestamp | Type | Name | Duration |
|---|---|---|---|---|
| 0/0 | 2026/08/07 12:45 | flow | patch-triage |
0mArgs:null Results: {
"EnableConfigs": null,
"FocusSymbols": [],
"KMSANReasoning": "",
"NeedsKMSAN": false,
"Reasoning": "The patch introduces a mechanism to zeroize secrets (keys and secretmem) during a kernel crash or panic. This code is only executed from `__crash_kexec()` after a panic has already occurred. Fuzzers treat panics as bugs and halt execution to collect crash logs, meaning they cannot effectively explore or find bugs in post-panic code paths. Therefore, these changes do not have a functional impact that can be meaningfully fuzzed.",
"WorthFuzzing": false
} |
| 1/1 | 2026/08/07 12:45 | action | read-patch-diff |
0mArgs:null Results: {
"PatchDiff": "commit 760c8040e7eb804150fb0800595c90607649df33\nAuthor: syz-cluster \u003ctriage@syzkaller.com\u003e\nDate: Fri Aug 7 12:45:52 2026 +0000\n\n syz-cluster: applied patch under review\n\ndiff --git a/drivers/of/kexec.c b/drivers/of/kexec.c\nindex 029903b986cbd..36635f6cb9009 100644\n--- a/drivers/of/kexec.c\n+++ b/drivers/of/kexec.c\n@@ -24,7 +24,7 @@\n \n /*\n * Additional space needed for the FDT buffer so that we can add initrd,\n- * bootargs, kaslr-seed, rng-seed, useable-memory-range and elfcorehdr.\n+ * bootargs, kaslr-seed, rng-seed, usable-memory-range and elfcorehdr.\n */\n #define FDT_EXTRA_SPACE 0x1000\n \ndiff --git a/include/linux/crash_core.h b/include/linux/crash_core.h\nindex bc087124cd78e..5c7207c0bba14 100644\n--- a/include/linux/crash_core.h\n+++ b/include/linux/crash_core.h\n@@ -5,6 +5,7 @@\n #include \u003clinux/linkage.h\u003e\n #include \u003clinux/elfcore.h\u003e\n #include \u003clinux/elf.h\u003e\n+#include \u003clinux/notifier.h\u003e\n \n struct kimage;\n \n@@ -34,6 +35,10 @@ static inline void arch_kexec_protect_crashkres(void) { }\n static inline void arch_kexec_unprotect_crashkres(void) { }\n #endif\n \n+#ifdef CONFIG_CRASH_ZEROIZE\n+extern struct atomic_notifier_head crash_zeroize_notifier_list;\n+#endif\n+\n #ifndef arch_crash_handle_hotplug_event\n static inline void arch_crash_handle_hotplug_event(struct kimage *image, void *arg) { }\n #endif\ndiff --git a/include/linux/key-type.h b/include/linux/key-type.h\nindex bb97bd3e5af4f..ff0944ce368f9 100644\n--- a/include/linux/key-type.h\n+++ b/include/linux/key-type.h\n@@ -122,6 +122,15 @@ struct key_type {\n \t/* clear the data from a key (optional) */\n \tvoid (*destroy)(struct key *key);\n \n+\t/* scrub the key material without free'ing (optional)\n+\t * - used from CONFIG_CRASH_ZEROIZE during panic to keep keys out of\n+\t * crash dumps\n+\t * - called from the panic path with other CPUs stopped and preemption\n+\t * disabled\n+\t * - must not sleep, allocate, free or take locks\n+\t */\n+\tvoid (*zeroize)(struct key *key);\n+\n \t/* describe a key */\n \tvoid (*describe)(const struct key *key, struct seq_file *p);\n \ndiff --git a/kernel/Kconfig.kexec b/kernel/Kconfig.kexec\nindex 15632358bcf71..92ab0a69c8ec6 100644\n--- a/kernel/Kconfig.kexec\n+++ b/kernel/Kconfig.kexec\n@@ -179,4 +179,12 @@ config CRASH_MAX_MEMORY_RANGES\n \t the computation behind the value provided through the\n \t /sys/kernel/crash_elfcorehdr_size attribute.\n \n+config CRASH_ZEROIZE\n+\tbool \"Zeroize secrets on panic\"\n+\tdepends on CRASH_DUMP\n+\thelp\n+\t Wipe secrets (e.g. kernel keyring and memfd_secret pages) on crash or panic.\n+\n+\t If unsure, say N.\n+\n endmenu\ndiff --git a/kernel/crash_core.c b/kernel/crash_core.c\nindex 2b36aa9fade06..d3a7763e27599 100644\n--- a/kernel/crash_core.c\n+++ b/kernel/crash_core.c\n@@ -23,6 +23,7 @@\n #include \u003clinux/objtool.h\u003e\n #include \u003clinux/delay.h\u003e\n #include \u003clinux/panic.h\u003e\n+#include \u003clinux/timekeeping.h\u003e\n \n #include \u003casm/page.h\u003e\n #include \u003casm/sections.h\u003e\n@@ -33,6 +34,22 @@\n /* Per cpu memory for storing cpu states in case of system crash. */\n note_buf_t __percpu *crash_notes;\n \n+#ifdef CONFIG_CRASH_ZEROIZE\n+ATOMIC_NOTIFIER_HEAD(crash_zeroize_notifier_list);\n+EXPORT_SYMBOL_GPL(crash_zeroize_notifier_list);\n+\n+static void crash_zeroize(void)\n+{\n+\tktime_t zeroize_start = ktime_get();\n+\n+\tpr_info(\"Wiping sensitive secrets...\\n\");\n+\tatomic_notifier_call_chain(\u0026crash_zeroize_notifier_list, 0, NULL);\n+\tpr_info(\"Done in %lld us\\n\", ktime_us_delta(ktime_get(), zeroize_start));\n+}\n+#else\n+static inline void crash_zeroize(void) { }\n+#endif /* CONFIG_CRASH_ZEROIZE */\n+\n /* time to wait for possible DMA to finish before starting the kdump kernel\n * when a CMA reservation is used\n */\n@@ -142,6 +159,7 @@ void __noclone __crash_kexec(struct pt_regs *regs)\n \t\t\tcrash_save_vmcoreinfo();\n \t\t\tmachine_crash_shutdown(\u0026fixed_regs);\n \t\t\tcrash_cma_clear_pending_dma();\n+\t\t\tcrash_zeroize();\n \t\t\tmachine_kexec(kexec_crash_image);\n \t\t}\n \t\tkexec_unlock();\ndiff --git a/mm/secretmem.c b/mm/secretmem.c\nindex d29865075b6ea..53f629d6633c1 100644\n--- a/mm/secretmem.c\n+++ b/mm/secretmem.c\n@@ -13,9 +13,11 @@\n #include \u003clinux/bitops.h\u003e\n #include \u003clinux/printk.h\u003e\n #include \u003clinux/pagemap.h\u003e\n+#include \u003clinux/notifier.h\u003e\n #include \u003clinux/syscalls.h\u003e\n #include \u003clinux/pseudo_fs.h\u003e\n #include \u003clinux/secretmem.h\u003e\n+#include \u003clinux/crash_core.h\u003e\n #include \u003clinux/set_memory.h\u003e\n #include \u003clinux/sched/signal.h\u003e\n \n@@ -187,6 +189,50 @@ static const struct inode_operations secretmem_iops = {\n \n static struct vfsmount *secretmem_mnt;\n \n+#ifdef CONFIG_CRASH_ZEROIZE\n+/* Called far into vpanic from crash_core.c with other CPUs stopped and\n+ * preemption disabled\n+ */\n+static int secretmem_crash_zeroize(struct notifier_block *nb, unsigned long\n+\t\taction, void *data)\n+{\n+\tstruct super_block *sb;\n+\tstruct inode *inode;\n+\n+\tif (!secretmem_mnt)\n+\t\treturn NOTIFY_DONE;\n+\tsb = secretmem_mnt-\u003emnt_sb;\n+\n+\t/* If the list was modified in the exact moment we panic'ed, it might be\n+\t * in an inconsistent state that would be unsafe to iterate. If we can't\n+\t * get the lock, too bad, that's all we can do here.\n+\t */\n+\tif (!spin_trylock(\u0026sb-\u003es_inode_list_lock)) {\n+\t\tpr_crit(\"crash_zeroize: can't acquire secretmem superblock lock.\\n\"\n+\t\t\t \"crash_zeroize: skipping zeroizing secretmem.\\n\");\n+\t\treturn NOTIFY_DONE;\n+\t}\n+\n+\tlist_for_each_entry(inode, \u0026sb-\u003es_inodes, i_sb_list) {\n+\t\tXA_STATE(xas, \u0026inode-\u003ei_mapping-\u003ei_pages, 0);\n+\t\tstruct folio *folio;\n+\n+\t\t/* no need for locks if we're burning down the house :) */\n+\t\txas_for_each(\u0026xas, folio, ULONG_MAX) {\n+\t\t\tif (xas_retry(\u0026xas, folio) || xa_is_value(folio))\n+\t\t\t\tcontinue;\n+\t\t\tinode-\u003ei_mapping-\u003ea_ops-\u003efree_folio(folio);\n+\t\t}\n+\t}\n+\t/* off to kexec()! */\n+\treturn NOTIFY_DONE;\n+}\n+\n+static struct notifier_block secretmem_zeroize_nb = {\n+\t.notifier_call = secretmem_crash_zeroize\n+};\n+#endif /* CONFIG_CRASH_ZEROIZE */\n+\n static struct file *secretmem_file_create(unsigned long flags)\n {\n \tstruct file *file;\n@@ -263,6 +309,10 @@ static int __init secretmem_init(void)\n \tif (IS_ERR(secretmem_mnt))\n \t\treturn PTR_ERR(secretmem_mnt);\n \n+#ifdef CONFIG_CRASH_ZEROIZE\n+\tatomic_notifier_chain_register(\u0026crash_zeroize_notifier_list, \u0026secretmem_zeroize_nb);\n+#endif\n+\n \treturn 0;\n }\n fs_initcall(secretmem_init);\ndiff --git a/security/keys/big_key.c b/security/keys/big_key.c\nindex 268f702df380e..ad8537dda70fa 100644\n--- a/security/keys/big_key.c\n+++ b/security/keys/big_key.c\n@@ -35,6 +35,8 @@ struct big_key_payload {\n */\n #define BIG_KEY_FILE_THRESHOLD (sizeof(struct inode) + sizeof(struct dentry))\n \n+static void big_key_zeroize(struct key *key);\n+\n /*\n * big_key defined keys take an arbitrary string as the description and an\n * arbitrary blob of data as the payload\n@@ -46,6 +48,7 @@ struct key_type key_type_big_key = {\n \t.instantiate\t\t= generic_key_instantiate,\n \t.revoke\t\t\t= big_key_revoke,\n \t.destroy\t\t= big_key_destroy,\n+\t.zeroize\t\t= big_key_zeroize,\n \t.describe\t\t= big_key_describe,\n \t.read\t\t\t= big_key_read,\n \t.update\t\t\t= big_key_update,\n@@ -279,6 +282,18 @@ long big_key_read(const struct key *key, char *buffer, size_t buflen)\n \treturn ret;\n }\n \n+static void big_key_zeroize(struct key *key)\n+{\n+\tstruct big_key_payload *payload = to_big_key_payload(key-\u003epayload);\n+\n+\tif (payload-\u003edata) {\n+\t\tif (payload-\u003elength \u003e BIG_KEY_FILE_THRESHOLD)\n+\t\t\tmemzero_explicit(payload-\u003edata, CHACHA20POLY1305_KEY_SIZE);\n+\t\telse\n+\t\t\tmemzero_explicit(payload-\u003edata, payload-\u003elength);\n+\t}\n+}\n+\n /*\n * Register key type\n */\ndiff --git a/security/keys/encrypted-keys/encrypted.c b/security/keys/encrypted-keys/encrypted.c\nindex 59cb77b237b36..9f56fa9b4aaf4 100644\n--- a/security/keys/encrypted-keys/encrypted.c\n+++ b/security/keys/encrypted-keys/encrypted.c\n@@ -970,11 +970,23 @@ static void encrypted_destroy(struct key *key)\n \tkfree_sensitive(key-\u003epayload.data[0]);\n }\n \n+static void encrypted_zeroize(struct key *key)\n+{\n+\tstruct encrypted_key_payload *epayload = key-\u003epayload.data[0];\n+\n+\tif (!epayload)\n+\t\treturn;\n+\n+\tmemzero_explicit(epayload-\u003epayload_data,\n+\t\t\t epayload-\u003epayload_datalen + epayload-\u003edatablob_len);\n+}\n+\n struct key_type key_type_encrypted = {\n \t.name = \"encrypted\",\n \t.instantiate = encrypted_instantiate,\n \t.update = encrypted_update,\n \t.destroy = encrypted_destroy,\n+\t.zeroize = encrypted_zeroize,\n \t.describe = user_describe,\n \t.read = encrypted_read,\n };\ndiff --git a/security/keys/key.c b/security/keys/key.c\nindex b34a64d81d47a..5673dcc8c5d23 100644\n--- a/security/keys/key.c\n+++ b/security/keys/key.c\n@@ -12,6 +12,7 @@\n #include \u003clinux/slab.h\u003e\n #include \u003clinux/security.h\u003e\n #include \u003clinux/workqueue.h\u003e\n+#include \u003clinux/crash_core.h\u003e\n #include \u003clinux/random.h\u003e\n #include \u003clinux/err.h\u003e\n #include \"internal.h\"\n@@ -1268,6 +1269,44 @@ void unregister_key_type(struct key_type *ktype)\n }\n EXPORT_SYMBOL(unregister_key_type);\n \n+#ifdef CONFIG_CRASH_ZEROIZE\n+/* Called far into vpanic from crash_core.c with other CPUs stopped and\n+ * preemption disabled\n+ */\n+static int key_crash_zeroize(struct notifier_block *nb, unsigned long action,\n+\t\tvoid *data)\n+{\n+\tstruct rb_node *node;\n+\n+\t/* If we can't acquire the lock, the rbtree might be in an inconsistent\n+\t * state. That's all we can do then, as there's no point to waiting\n+\t * at this stage.\n+\t */\n+\tif (!spin_trylock(\u0026key_serial_lock)) {\n+\t\tpr_crit(\"crash_zeroize: can't acquire key_serial_lock. skipping keyrings.\\n\");\n+\t\treturn NOTIFY_DONE;\n+\t}\n+\n+\tfor (node = rb_first(\u0026key_serial_tree); node; node = rb_next(node)) {\n+\t\tstruct key *key = rb_entry(node, struct key, serial_node);\n+\n+\t\tif (key-\u003etype == \u0026key_type_keyring ||\n+\t\t key-\u003estate == KEY_IS_UNINSTANTIATED)\n+\t\t\tcontinue;\n+\n+\t\t/* custom zeroize since free'ing isn't safe at this point */\n+\t\tif (key-\u003etype-\u003ezeroize)\n+\t\t\tkey-\u003etype-\u003ezeroize(key);\n+\t}\n+\t/* off to kexec()! */\n+\treturn NOTIFY_DONE;\n+}\n+\n+static struct notifier_block key_crash_zeroize_nb = {\n+\t.notifier_call = key_crash_zeroize\n+};\n+#endif /* CONFIG_CRASH_ZEROIZE */\n+\n /*\n * Initialise the key management state.\n */\n@@ -1290,4 +1329,9 @@ void __init key_init(void)\n \n \trb_insert_color(\u0026root_key_user.node,\n \t\t\t\u0026key_user_tree);\n+\n+#ifdef CONFIG_CRASH_ZEROIZE\n+\tatomic_notifier_chain_register(\u0026crash_zeroize_notifier_list,\n+\t\t\t\u0026key_crash_zeroize_nb);\n+#endif\n }\ndiff --git a/security/keys/trusted-keys/trusted_core.c b/security/keys/trusted-keys/trusted_core.c\nindex 0509d9955f2a6..f159faeafe23a 100644\n--- a/security/keys/trusted-keys/trusted_core.c\n+++ b/security/keys/trusted-keys/trusted_core.c\n@@ -325,11 +325,25 @@ static void trusted_destroy(struct key *key)\n \tkfree_sensitive(key-\u003epayload.data[0]);\n }\n \n+static void trusted_zeroize(struct key *key)\n+{\n+\tstruct trusted_key_payload *p = key-\u003epayload.data[0];\n+\n+\tif (!p)\n+\t\treturn;\n+\n+\tmemzero_explicit(p-\u003ekey, sizeof(p-\u003ekey));\n+\tmemzero_explicit(p-\u003eblob, sizeof(p-\u003eblob));\n+\tp-\u003ekey_len = 0;\n+\tp-\u003eblob_len = 0;\n+}\n+\n struct key_type key_type_trusted = {\n \t.name = \"trusted\",\n \t.instantiate = trusted_instantiate,\n \t.update = trusted_update,\n \t.destroy = trusted_destroy,\n+\t.zeroize = trusted_zeroize,\n \t.describe = user_describe,\n \t.read = trusted_read,\n };\ndiff --git a/security/keys/user_defined.c b/security/keys/user_defined.c\nindex 6f88b507f9279..ade95dc2481df 100644\n--- a/security/keys/user_defined.c\n+++ b/security/keys/user_defined.c\n@@ -15,6 +15,7 @@\n #include \"internal.h\"\n \n static int logon_vet_description(const char *desc);\n+static void user_zeroize(struct key *key);\n \n /*\n * user defined keys take an arbitrary string as the description and an\n@@ -28,6 +29,7 @@ struct key_type key_type_user = {\n \t.update\t\t\t= user_update,\n \t.revoke\t\t\t= user_revoke,\n \t.destroy\t\t= user_destroy,\n+\t.zeroize\t\t= user_zeroize,\n \t.describe\t\t= user_describe,\n \t.read\t\t\t= user_read,\n };\n@@ -48,6 +50,7 @@ struct key_type key_type_logon = {\n \t.update\t\t\t= user_update,\n \t.revoke\t\t\t= user_revoke,\n \t.destroy\t\t= user_destroy,\n+\t.zeroize\t\t= user_zeroize,\n \t.describe\t\t= user_describe,\n \t.vet_description\t= logon_vet_description,\n };\n@@ -152,6 +155,14 @@ void user_destroy(struct key *key)\n \n EXPORT_SYMBOL_GPL(user_destroy);\n \n+static void user_zeroize(struct key *key)\n+{\n+\tstruct user_key_payload *upayload = key-\u003epayload.data[0];\n+\n+\tif (upayload)\n+\t\tmemzero_explicit(upayload-\u003edata, upayload-\u003edatalen);\n+}\n+\n /*\n * describe the user key\n */\n"
} |
| 2/1 | 2026/08/07 12:45 | agent | patch-evaluator |
0mModel:best-expensive Args: null Results: {
"EnableConfigs": null,
"FocusSymbols": [],
"Reasoning": "The patch introduces a mechanism to zeroize secrets (keys and secretmem) during a kernel crash or panic. This code is only executed from `__crash_kexec()` after a panic has already occurred. Fuzzers treat panics as bugs and halt execution to collect crash logs, meaning they cannot effectively explore or find bugs in post-panic code paths. Therefore, these changes do not have a functional impact that can be meaningfully fuzzed.",
"WorthFuzzing": false
}Instruction: You are an expert Linux kernel maintainer. Your job is to review a provided patch series and determine if it makes functional changes to the kernel that should be fuzzed. IMPORTANT: The changes have ALREADY been applied and committed as the HEAD commit in your workspace. Do NOT rely on your internal knowledge of the kernel. You must actively use your code access tools to examine the actual source code and confirm any assumptions. Return WorthFuzzing=false if the patch only contains: - Modifications to Documentation/, Kconfig files, or code comments. - Purely decorative changes, such as logging (e.g., pr_err, printk) or tracepoints. - Changes to numeric constants or macros that do not functionally alter execution flow. - Code paths that are impossible to reach in virtualized environments like GCE or QEMU, even when utilizing software-emulated hardware (e.g., usb gadget, mac80211_hwsim). - Code in vendor-specific PCIe switch, SmartNIC, or GPU drivers (e.g., mlxsw, pds_core, qed, ionic, amdgpu) that require physical PCIe hardware cards not emulated in standard QEMU. - Driver .remove, .shutdown, or pci_unregister_driver teardown callbacks (e.g., igb_remove) that are executed only during PCI hot-unplug or sysfs driver unbind operations. If it modifies reachable core kernel logic, drivers, or architectures, use your code search tools to verify the code can be executed, then return WorthFuzzing=true. When returning WorthFuzzing=true, you MUST ALSO: 1. Extract any specific kernel functions that should be heavily fuzzed into FocusSymbols. Avoid listing generic hot-path functions to prevent skewed test distributions. Prefer non-static, non-inlined API entrypoint functions over internal static helper functions (which are inlined by the compiler and do not have distinct symbol addresses). 2. Identify any specific CONFIG_ options required to properly test this new/modified feature. Go and look into the Kconfig files and check for ifdefs around the code, do not make assumptions. Also check "depends on" lines in Kconfig to include any non-standard parent subsystem configs needed for Kbuild to compile the code statically into vmlinux. List them in the EnableConfigs output array, and DO NOT add a 'CONFIG_' prefix (e.g., return "NET_IPV4" instead of "CONFIG_NET_IPV4"). Prefer calling several tools at the same time to save round-trips. Use set-results tool to provide results of the analysis. It must be called exactly once before the final reply. Ignore results of this tool. Prompt: For your convenience, here is the diff of the changes:
commit 760c8040e7eb804150fb0800595c90607649df33
Author: syz-cluster <triage@syzkaller.com>
Date: Fri Aug 7 12:45:52 2026 +0000
syz-cluster: applied patch under review
diff --git a/drivers/of/kexec.c b/drivers/of/kexec.c
index 029903b986cbd..36635f6cb9009 100644
--- a/drivers/of/kexec.c
+++ b/drivers/of/kexec.c
@@ -24,7 +24,7 @@
/*
* Additional space needed for the FDT buffer so that we can add initrd,
- * bootargs, kaslr-seed, rng-seed, useable-memory-range and elfcorehdr.
+ * bootargs, kaslr-seed, rng-seed, usable-memory-range and elfcorehdr.
*/
#define FDT_EXTRA_SPACE 0x1000
diff --git a/include/linux/crash_core.h b/include/linux/crash_core.h
index bc087124cd78e..5c7207c0bba14 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,10 @@ static inline void arch_kexec_protect_crashkres(void) { }
static inline void arch_kexec_unprotect_crashkres(void) { }
#endif
+#ifdef CONFIG_CRASH_ZEROIZE
+extern struct atomic_notifier_head crash_zeroize_notifier_list;
+#endif
+
#ifndef arch_crash_handle_hotplug_event
static inline void arch_crash_handle_hotplug_event(struct kimage *image, void *arg) { }
#endif
diff --git a/include/linux/key-type.h b/include/linux/key-type.h
index bb97bd3e5af4f..ff0944ce368f9 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);
+ /* scrub the key material without free'ing (optional)
+ * - used from CONFIG_CRASH_ZEROIZE 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 (*zeroize)(struct key *key);
+
/* describe a key */
void (*describe)(const struct key *key, struct seq_file *p);
diff --git a/kernel/Kconfig.kexec b/kernel/Kconfig.kexec
index 15632358bcf71..92ab0a69c8ec6 100644
--- a/kernel/Kconfig.kexec
+++ b/kernel/Kconfig.kexec
@@ -179,4 +179,12 @@ config CRASH_MAX_MEMORY_RANGES
the computation behind the value provided through the
/sys/kernel/crash_elfcorehdr_size attribute.
+config CRASH_ZEROIZE
+ bool "Zeroize secrets on panic"
+ depends on CRASH_DUMP
+ help
+ Wipe secrets (e.g. kernel keyring and memfd_secret pages) on crash or panic.
+
+ If unsure, say N.
+
endmenu
diff --git a/kernel/crash_core.c b/kernel/crash_core.c
index 2b36aa9fade06..d3a7763e27599 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,22 @@
/* Per cpu memory for storing cpu states in case of system crash. */
note_buf_t __percpu *crash_notes;
+#ifdef CONFIG_CRASH_ZEROIZE
+ATOMIC_NOTIFIER_HEAD(crash_zeroize_notifier_list);
+EXPORT_SYMBOL_GPL(crash_zeroize_notifier_list);
+
+static void crash_zeroize(void)
+{
+ ktime_t zeroize_start = ktime_get();
+
+ pr_info("Wiping sensitive secrets...\n");
+ atomic_notifier_call_chain(&crash_zeroize_notifier_list, 0, NULL);
+ pr_info("Done in %lld us\n", ktime_us_delta(ktime_get(), zeroize_start));
+}
+#else
+static inline void crash_zeroize(void) { }
+#endif /* CONFIG_CRASH_ZEROIZE */
+
/* time to wait for possible DMA to finish before starting the kdump kernel
* when a CMA reservation is used
*/
@@ -142,6 +159,7 @@ void __noclone __crash_kexec(struct pt_regs *regs)
crash_save_vmcoreinfo();
machine_crash_shutdown(&fixed_regs);
crash_cma_clear_pending_dma();
+ crash_zeroize();
machine_kexec(kexec_crash_image);
}
kexec_unlock();
diff --git a/mm/secretmem.c b/mm/secretmem.c
index d29865075b6ea..53f629d6633c1 100644
--- a/mm/secretmem.c
+++ b/mm/secretmem.c
@@ -13,9 +13,11 @@
#include <linux/bitops.h>
#include <linux/printk.h>
#include <linux/pagemap.h>
+#include <linux/notifier.h>
#include <linux/syscalls.h>
#include <linux/pseudo_fs.h>
#include <linux/secretmem.h>
+#include <linux/crash_core.h>
#include <linux/set_memory.h>
#include <linux/sched/signal.h>
@@ -187,6 +189,50 @@ static const struct inode_operations secretmem_iops = {
static struct vfsmount *secretmem_mnt;
+#ifdef CONFIG_CRASH_ZEROIZE
+/* Called far into vpanic from crash_core.c with other CPUs stopped and
+ * preemption disabled
+ */
+static int secretmem_crash_zeroize(struct notifier_block *nb, unsigned long
+ action, void *data)
+{
+ struct super_block *sb;
+ struct inode *inode;
+
+ if (!secretmem_mnt)
+ return NOTIFY_DONE;
+ sb = secretmem_mnt->mnt_sb;
+
+ /* If the list was modified in the exact moment we panic'ed, it might be
+ * in an inconsistent state that would be unsafe to iterate. If we can't
+ * get the lock, too bad, that's all we can do here.
+ */
+ if (!spin_trylock(&sb->s_inode_list_lock)) {
+ pr_crit("crash_zeroize: can't acquire secretmem superblock lock.\n"
+ "crash_zeroize: skipping zeroizing secretmem.\n");
+ return NOTIFY_DONE;
+ }
+
+ list_for_each_entry(inode, &sb->s_inodes, i_sb_list) {
+ XA_STATE(xas, &inode->i_mapping->i_pages, 0);
+ struct folio *folio;
+
+ /* no need for locks if we're burning down the house :) */
+ xas_for_each(&xas, folio, ULONG_MAX) {
+ if (xas_retry(&xas, folio) || xa_is_value(folio))
+ continue;
+ inode->i_mapping->a_ops->free_folio(folio);
+ }
+ }
+ /* off to kexec()! */
+ return NOTIFY_DONE;
+}
+
+static struct notifier_block secretmem_zeroize_nb = {
+ .notifier_call = secretmem_crash_zeroize
+};
+#endif /* CONFIG_CRASH_ZEROIZE */
+
static struct file *secretmem_file_create(unsigned long flags)
{
struct file *file;
@@ -263,6 +309,10 @@ static int __init secretmem_init(void)
if (IS_ERR(secretmem_mnt))
return PTR_ERR(secretmem_mnt);
+#ifdef CONFIG_CRASH_ZEROIZE
+ atomic_notifier_chain_register(&crash_zeroize_notifier_list, &secretmem_zeroize_nb);
+#endif
+
return 0;
}
fs_initcall(secretmem_init);
diff --git a/security/keys/big_key.c b/security/keys/big_key.c
index 268f702df380e..ad8537dda70fa 100644
--- a/security/keys/big_key.c
+++ b/security/keys/big_key.c
@@ -35,6 +35,8 @@ struct big_key_payload {
*/
#define BIG_KEY_FILE_THRESHOLD (sizeof(struct inode) + sizeof(struct dentry))
+static void big_key_zeroize(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 +48,7 @@ struct key_type key_type_big_key = {
.instantiate = generic_key_instantiate,
.revoke = big_key_revoke,
.destroy = big_key_destroy,
+ .zeroize = big_key_zeroize,
.describe = big_key_describe,
.read = big_key_read,
.update = big_key_update,
@@ -279,6 +282,18 @@ long big_key_read(const struct key *key, char *buffer, size_t buflen)
return ret;
}
+static void big_key_zeroize(struct key *key)
+{
+ struct big_key_payload *payload = to_big_key_payload(key->payload);
+
+ if (payload->data) {
+ if (payload->length > BIG_KEY_FILE_THRESHOLD)
+ memzero_explicit(payload->data, CHACHA20POLY1305_KEY_SIZE);
+ else
+ memzero_explicit(payload->data, payload->length);
+ }
+}
+
/*
* Register key type
*/
diff --git a/security/keys/encrypted-keys/encrypted.c b/security/keys/encrypted-keys/encrypted.c
index 59cb77b237b36..9f56fa9b4aaf4 100644
--- a/security/keys/encrypted-keys/encrypted.c
+++ b/security/keys/encrypted-keys/encrypted.c
@@ -970,11 +970,23 @@ static void encrypted_destroy(struct key *key)
kfree_sensitive(key->payload.data[0]);
}
+static void encrypted_zeroize(struct key *key)
+{
+ struct encrypted_key_payload *epayload = key->payload.data[0];
+
+ if (!epayload)
+ return;
+
+ memzero_explicit(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,
+ .zeroize = encrypted_zeroize,
.describe = user_describe,
.read = encrypted_read,
};
diff --git a/security/keys/key.c b/security/keys/key.c
index b34a64d81d47a..5673dcc8c5d23 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,44 @@ void unregister_key_type(struct key_type *ktype)
}
EXPORT_SYMBOL(unregister_key_type);
+#ifdef CONFIG_CRASH_ZEROIZE
+/* Called far into vpanic from crash_core.c with other CPUs stopped and
+ * preemption disabled
+ */
+static int key_crash_zeroize(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_zeroize: 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);
+
+ if (key->type == &key_type_keyring ||
+ key->state == KEY_IS_UNINSTANTIATED)
+ continue;
+
+ /* custom zeroize since free'ing isn't safe at this point */
+ if (key->type->zeroize)
+ key->type->zeroize(key);
+ }
+ /* off to kexec()! */
+ return NOTIFY_DONE;
+}
+
+static struct notifier_block key_crash_zeroize_nb = {
+ .notifier_call = key_crash_zeroize
+};
+#endif /* CONFIG_CRASH_ZEROIZE */
+
/*
* Initialise the key management state.
*/
@@ -1290,4 +1329,9 @@ void __init key_init(void)
rb_insert_color(&root_key_user.node,
&key_user_tree);
+
+#ifdef CONFIG_CRASH_ZEROIZE
+ atomic_notifier_chain_register(&crash_zeroize_notifier_list,
+ &key_crash_zeroize_nb);
+#endif
}
diff --git a/security/keys/trusted-keys/trusted_core.c b/security/keys/trusted-keys/trusted_core.c
index 0509d9955f2a6..f159faeafe23a 100644
--- a/security/keys/trusted-keys/trusted_core.c
+++ b/security/keys/trusted-keys/trusted_core.c
@@ -325,11 +325,25 @@ static void trusted_destroy(struct key *key)
kfree_sensitive(key->payload.data[0]);
}
+static void trusted_zeroize(struct key *key)
+{
+ struct trusted_key_payload *p = key->payload.data[0];
+
+ if (!p)
+ return;
+
+ memzero_explicit(p->key, sizeof(p->key));
+ memzero_explicit(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,
+ .zeroize = trusted_zeroize,
.describe = user_describe,
.read = trusted_read,
};
diff --git a/security/keys/user_defined.c b/security/keys/user_defined.c
index 6f88b507f9279..ade95dc2481df 100644
--- a/security/keys/user_defined.c
+++ b/security/keys/user_defined.c
@@ -15,6 +15,7 @@
#include "internal.h"
static int logon_vet_description(const char *desc);
+static void user_zeroize(struct key *key);
/*
* user defined keys take an arbitrary string as the description and an
@@ -28,6 +29,7 @@ struct key_type key_type_user = {
.update = user_update,
.revoke = user_revoke,
.destroy = user_destroy,
+ .zeroize = user_zeroize,
.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,
+ .zeroize = user_zeroize,
.describe = user_describe,
.vet_description = logon_vet_description,
};
@@ -152,6 +155,14 @@ void user_destroy(struct key *key)
EXPORT_SYMBOL_GPL(user_destroy);
+static void user_zeroize(struct key *key)
+{
+ struct user_key_payload *upayload = key->payload.data[0];
+
+ if (upayload)
+ memzero_explicit(upayload->data, upayload->datalen);
+}
+
/*
* describe the user key
*/
|
| 3/2 | 2026/08/07 12:45 | llm | patch-evaluator |
0mModel:gemini-3.1-pro-preview Args: null Results: null Tokens: input: 6511 output: 19 thoughts: 1223 Thoughts: **Fuzzing the Panic Handler? A Quick Evaluation** Okay, so I'm looking at this patch series and my initial thought is: does this introduce anything new we should be fuzzing? It looks like the main change is this `CONFIG_CRASH_ZEROIZE` feature, which adds a zeroize callback to `struct key_type` and implements it for several key types. It also handles the zeroization of `secretmem` pages. But hold on... this code, `__crash_kexec`, only executes during a kernel crash or panic. This is usually not the target of fuzzing. Fuzzers aim for reachable code during normal operation or error handling. Can a fuzzer *trigger* this code? Well, yes, by causing a panic. But the primary goal of the fuzzer is to find panics, not to examine what happens *after* a panic. If the fuzzer does trigger a panic, and kdump is configured, then `__crash_kexec` will run. But is it even worth the effort of fuzzing the zeroize functions themselves? They seem pretty straightforward: the code iterates through `big_key_zeroize`, `encrypted_zeroize`, `trusted_zeroize`, `user_zeroize`, `secretmem_crash_zeroize`, and `key_crash_zeroize`, which are called from `crash_zeroize()` called from `__crash_kexec()`. So, `__crash_kexec` *is* reachable – on panic. Are these zeroize functions worth the effort? They are actually quite simple, like this `user_zeroize` example. And in `secretmem_crash_zeroize`, it calls `free_folio`. Is it safe to call `free_folio` during a panic? The comment says "no need for locks if we're burning down the house :)". This makes me nervous though. If it crashes during `free_folio`, could we get a double fault, or worse? Is this something syzkaller should even be looking at? Syzkaller doesn't typically focus on post-panic code because a crash typically ends the fuzzing run and it doesn't continue on to finding bugs in the panic handler, unless it causes a secondary panic which could corrupt the crash log. Actually, wait! Syzkaller *does* parse crash logs, so a corrupted log due to a panic handler bug *is* a relevant bug to catch. But, can these zeroize functions be called from anywhere else? No, they are registered to a notifier list that only triggers on crash functions. Wait, the code in question is called by `__crash_kexec` and `crash_zeroize`. Is there any way to trigger this code without a real panic? No, `__crash_kexec` is called from `panic()`, `crash_kexec()`, or similar functions. We need a panic to reach it, which is the *end* of a fuzzing run. This code is only reached when the system is already dead, so a fuzzer can't meaningfully explore it. Wait, you *can* trigger `crash_kexec` via `sysrq-c` – which causes a panic. But that's still a panic. I should also check if there are any other functional changes. There's a change in the `key_type` struct size and notifier registration. Okay, is this worth fuzzing? The guidelines say to skip "Code paths that are impossible to reach in virtualized environments" and "Purely decorative changes". It doesn't explicitly mention "post-panic code," but fuzzing post-panic code is generally ineffective because the fuzzer stops at the first panic. Hold on, what about during a normal kexec? No, `crash_zeroize` is called from `__crash_kexec`, not `kernel_kexec`. Let's just double-check `__crash_kexec`. I'm thinking this is probably not worth it. |
| 4/2 | 2026/08/07 12:46 | tool | grepper |
0mArgs:{
"Expression": "__crash_kexec"
}Results: {
"Output": "arch/x86/hyperv/hv_crash.c=127=static void __noreturn hv_crash_handle(void)\n--\narch/x86/hyperv/hv_crash.c-132-\t/* we are now fully in devirtualized normal kernel mode */\narch/x86/hyperv/hv_crash.c:133:\t__crash_kexec(NULL);\narch/x86/hyperv/hv_crash.c-134-\n--\narch/x86/hyperv/hv_crash.c=332=static int hv_crash_nmi_local(unsigned int cmd, struct pt_regs *regs)\n--\narch/x86/hyperv/hv_crash.c-361- *\tBSP : NMI callback -\u003e DisableHv -\u003e hv_crash_asm32 -\u003e hv_crash_c_entry\narch/x86/hyperv/hv_crash.c:362: *\t\t -\u003e __crash_kexec -\u003e native_machine_crash_shutdown\narch/x86/hyperv/hv_crash.c-363- *\t\t -\u003e crash_smp_send_stop -\u003e smp_ops.crash_stop_other_cpus\n--\ninclude/linux/crash_core.h=72=struct kexec_segment;\n--\ninclude/linux/crash_core.h-80-\ninclude/linux/crash_core.h:81:extern void __crash_kexec(struct pt_regs *regs);\ninclude/linux/crash_core.h-82-extern void crash_kexec(struct pt_regs *regs);\n--\ninclude/linux/crash_core.h=97=struct kimage;\ninclude/linux/crash_core.h:98:static inline void __crash_kexec(struct pt_regs *regs) { }\ninclude/linux/crash_core.h-99-static inline void crash_kexec(struct pt_regs *regs) { }\n--\ninclude/linux/kexec.h=538=struct kimage;\ninclude/linux/kexec.h:539:static inline void __crash_kexec(struct pt_regs *regs) { }\ninclude/linux/kexec.h-540-static inline void crash_kexec(struct pt_regs *regs) { }\n--\nkernel/crash_core.c=131=static void crash_cma_clear_pending_dma(void)\n--\nkernel/crash_core.c-143- */\nkernel/crash_core.c:144:void __noclone __crash_kexec(struct pt_regs *regs)\nkernel/crash_core.c-145-{\n--\nkernel/crash_core.c-167-}\nkernel/crash_core.c:168:STACK_FRAME_NON_STANDARD(__crash_kexec);\nkernel/crash_core.c-169-\nkernel/crash_core.c=170=__bpf_kfunc void crash_kexec(struct pt_regs *regs)\n--\nkernel/crash_core.c-173-\t\t/* This is the 1st CPU which comes here, so go ahead. */\nkernel/crash_core.c:174:\t\t__crash_kexec(regs);\nkernel/crash_core.c-175-\n--\nkernel/kexec_internal.h=15=int kimage_is_destination_range(struct kimage *image,\n--\nkernel/kexec_internal.h-19- * Whatever is used to serialize accesses to the kexec_crash_image needs to be\nkernel/kexec_internal.h:20: * NMI safe, as __crash_kexec() can happen during nmi_panic(), so here we use a\nkernel/kexec_internal.h-21- * \"simple\" atomic variable that is acquired with a cmpxchg().\n--\nkernel/panic.c=577=void vpanic(const char *fmt, va_list args)\n--\nkernel/panic.c-667-\t *\nkernel/panic.c:668:\t * Bypass the panic_cpu check and call __crash_kexec directly.\nkernel/panic.c-669-\t */\nkernel/panic.c-670-\tif (!_crash_kexec_post_notifiers)\nkernel/panic.c:671:\t\t__crash_kexec(NULL);\nkernel/panic.c-672-\n--\nkernel/panic.c-693-\t *\nkernel/panic.c:694:\t * Bypass the panic_cpu check and call __crash_kexec directly.\nkernel/panic.c-695-\t */\nkernel/panic.c-696-\tif (_crash_kexec_post_notifiers)\nkernel/panic.c:697:\t\t__crash_kexec(NULL);\nkernel/panic.c-698-\n"
} |
| 5/2 | 2026/08/07 12:46 | llm | patch-evaluator |
0mModel:gemini-3.1-pro-preview Args: null Results: null Tokens: input: 9796 output: 17 thoughts: 399 Thoughts: **My Assessment of the Zeroize Function and Fuzzing Feasibility** Okay, so `__crash_kexec` is only triggered during a kernel panic or a crash. That much is clear. The problem is, fuzzing this directly is completely impractical. A fuzzer's whole purpose is to find panics – the moment it *does* find one, it's done its job and reboots the virtual machine to collect the crash log. It's not designed to delve into the code that's executed *after* the panic, because the panic itself signals the end of the current fuzzing run. Wait a second… could BPF (Berkeley Packet Filter) potentially trigger `crash_kexec`? Let's see… Ah, yes, there it is: `__bpf_kfunc void crash_kexec(struct pt_regs *regs)`. Interesting. However, even if BPF *could* call `crash_kexec`, the outcome is predictable. It would either crash the system outright (if kdump is loaded), or it would do nothing (if kdump isn't loaded). Either way, it ends the fuzzing run immediately, because a crash is just considered a bug and is immediately reported. So, realistically, a fuzzer won't be able to effectively explore the `zeroize` functions and identify bugs within them, because any execution of these functions is inherently tied to a system crash. The fuzzing process terminates before it can explore the post-crash behaviour. Now, hold on... Is there *any* other path to trigger the `zeroize` function? Let's check... Okay, `key->type->zeroize(key)` is only called from `key_crash_zeroize`, which is, in turn, registered to the `crash_zeroize_notifier_list`. And `crash_zeroize_notifier_list` is only called from `crash_zeroize()`. Finally, `crash_zeroize()` is only called from `__crash_kexec()`. Got it. So this entire feature is strictly post-panic. It's designed to run *after* a crash has already occurred. This confirms that a fuzzer is not a useful tool here. A fuzzer considers a panic to be an error and immediately halts the execution. It's not designed to try to maximize code coverage *after* a crash has happened. Therefore, this particular patch doesn't introduce any functional changes that are amenable to fuzzing. It's purely related to post-panic cleanup and is inherently outside the scope of effective fuzzing. Let me just double-check one more time, just to be absolutely sure... |
| 6/2 | 2026/08/07 12:46 | tool | grepper |
0mArgs:{
"Expression": "-\u003ezeroize"
}Results: {
"Output": "security/keys/key.c=1276=static int key_crash_zeroize(struct notifier_block *nb, unsigned long action,\n--\nsecurity/keys/key.c-1297-\t\t/* custom zeroize since free'ing isn't safe at this point */\nsecurity/keys/key.c:1298:\t\tif (key-\u003etype-\u003ezeroize)\nsecurity/keys/key.c:1299:\t\t\tkey-\u003etype-\u003ezeroize(key);\nsecurity/keys/key.c-1300-\t}\n"
} |
| 7/2 | 2026/08/07 12:46 | llm | patch-evaluator |
0mModel:gemini-3.1-pro-preview Args: null Results: null Tokens: input: 10923 output: 122 thoughts: 544 Thoughts: **Fuzzing Analysis: Crash Zeroization Patch** Okay, so this patch is *solely* concerned with zeroizing secrets, specifically keys and memory, upon a kernel panic. I've confirmed that the critical function is `key_crash_zeroize`, and `secretmem_crash_zeroize` is also registered, so that's the extent of the initial change. It's essentially just a bunch of `zeroize` callback implementations that support the same functionality. My analysis boils down to this: *this code is only ever reachable after a panic occurs.* Fuzzers, by definition, treat panics as terminal events. They halt execution immediately to capture the crash state and generate logs. They aren't designed to continue exploring code paths *after* a crash. Trying to fuzz post-panic code is going to be pointless. There's simply no functional impact here that a fuzzer can meaningfully exercise. I've double-checked all the changes in `drivers/of/kexec.c`, `include/linux/crash_core.h`, `include/linux/key-type.h`, `kernel/Kconfig.kexec`, `kernel/crash_core.c`, `mm/secretmem.c`, and the security key files. They all seem to be tightly coupled to this panic zeroization functionality, the `CRASH_ZEROIZE` feature, that is called during panic and not earlier. Therefore, my conclusion is clear. There's no value in trying to fuzz these changes. The `WorthFuzzing` flag will be set to `false`. |
| 8/2 | 2026/08/07 12:46 | tool | set-results |
0mArgs:{
"EnableConfigs": [],
"FocusSymbols": [],
"Reasoning": "The patch introduces a mechanism to zeroize secrets (keys and secretmem) during a kernel crash or panic. This code is only executed from `__crash_kexec()` after a panic has already occurred. Fuzzers treat panics as bugs and halt execution to collect crash logs, meaning they cannot effectively explore or find bugs in post-panic code paths. Therefore, these changes do not have a functional impact that can be meaningfully fuzzed.",
"WorthFuzzing": false
}Results: {
"EnableConfigs": null,
"FocusSymbols": [],
"Reasoning": "The patch introduces a mechanism to zeroize secrets (keys and secretmem) during a kernel crash or panic. This code is only executed from `__crash_kexec()` after a panic has already occurred. Fuzzers treat panics as bugs and halt execution to collect crash logs, meaning they cannot effectively explore or find bugs in post-panic code paths. Therefore, these changes do not have a functional impact that can be meaningfully fuzzed.",
"WorthFuzzing": false
} |
| 9/1 | 2026/08/07 12:46 | action | If |
0mArgs:{
"WorthFuzzing": false
}Results: null |
| Total Calls | Total Tokens | Avg Tokens | Total Duration (Seconds) | Avg Duration (Seconds) |
|---|
| Total Calls | Total Duration (Seconds) | Avg Duration (Seconds) |
|---|