lib/Kconfig.debug | 18 +++++ lib/Makefile | 1 + lib/test_crash.c | 176 ++++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 195 insertions(+) create mode 100644 lib/test_crash.c
This test calls panic() from various execution contexts in the kernel,
so the user can see if a crash trace is successfully emitted.
This is useful for testing console drivers, and can help rule out issues
with the console itself when silent reboots or hangs are observed on a
particular system.
Signed-off-by: Calvin Owens <calvin@wbinvd.org>
---
I shared this upthread in [1] where it turned out to be useful for
netconsole. Petr suggested it might be useful for others, so I've
applied his feedback and cleaned everything up a bit more too.
It's a superset of what 'echo c > /proc/sysrq-trigger' does, but
extending that interface doesn't seem practical.
The write() implementation requries the entire string be written in a
single syscall: this is pretty common for these debugfs interfaces, so
it didn't seem worth the complexity to handle partial writes. But I'm
happy to do so if somebody cares :)
[1] https://lore.kernel.org/lkml/aMGenGUNcBbRUUf9@pathway.suse.cz/
lib/Kconfig.debug | 18 +++++
lib/Makefile | 1 +
lib/test_crash.c | 176 ++++++++++++++++++++++++++++++++++++++++++++++
3 files changed, 195 insertions(+)
create mode 100644 lib/test_crash.c
diff --git a/lib/Kconfig.debug b/lib/Kconfig.debug
index 8ff5adcfe1e0..f3c3ab90fc0c 100644
--- a/lib/Kconfig.debug
+++ b/lib/Kconfig.debug
@@ -1385,6 +1385,24 @@ config TEST_LOCKUP
If unsure, say N.
+config TEST_CRASH
+ tristate "Test module to trigger crashes"
+ help
+ Expose a file in debugfs which triggers a panic() call in
+ various kernel execution contexts.
+
+ $ cat /sys/kernel/debug/test_crash
+ irq
+ [...]
+ $ echo "irq" > /sys/kernel/debug/test_crash
+ Kernel panic - not syncing: User triggered crash in context irq
+
+ This is useful for testing console drivers, and can help rule
+ out issues with the console itself when silent reboots or
+ hangs are observed on a particular system.
+
+ If unsure, say N.
+
endmenu # "Debug lockups and hangs"
menu "Scheduler Debugging"
diff --git a/lib/Makefile b/lib/Makefile
index f33a24bf1c19..e2ac56887dd6 100644
--- a/lib/Makefile
+++ b/lib/Makefile
@@ -100,6 +100,7 @@ obj-$(CONFIG_TEST_MEMCAT_P) += test_memcat_p.o
obj-$(CONFIG_TEST_OBJAGG) += test_objagg.o
obj-$(CONFIG_TEST_MEMINIT) += test_meminit.o
obj-$(CONFIG_TEST_LOCKUP) += test_lockup.o
+obj-$(CONFIG_TEST_CRASH) += test_crash.o
obj-$(CONFIG_TEST_HMM) += test_hmm.o
obj-$(CONFIG_TEST_FREE_PAGES) += test_free_pages.o
obj-$(CONFIG_TEST_REF_TRACKER) += test_ref_tracker.o
diff --git a/lib/test_crash.c b/lib/test_crash.c
new file mode 100644
index 000000000000..a72284fbb062
--- /dev/null
+++ b/lib/test_crash.c
@@ -0,0 +1,176 @@
+// SPDX-License-Identifier: GPL-2.0
+
+#include <linux/console.h>
+#include <linux/debugfs.h>
+#include <linux/irq_work.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/rtnetlink.h>
+#include <linux/string.h>
+
+static void crash(const char *context)
+{
+ panic("User triggered crash in context %s", context);
+}
+
+#define CRASH_TESTCASE_LIST \
+ X(irq) \
+ X(bh) \
+ X(user) \
+ X(user_nobh) \
+ X(user_noirq) \
+ X(user_nopreempt) \
+ X(user_rculock) \
+ X(user_conlock) \
+ X(user_rtnllock)
+
+static void crash_irq_work(struct irq_work *work)
+{
+ crash("irq");
+}
+
+static struct irq_work irq_crash_work;
+
+static void crash_irq(void)
+{
+ if (!irq_work_queue(&irq_crash_work))
+ return;
+
+ irq_work_sync(&irq_crash_work);
+}
+
+static void crash_bh_work(struct work_struct *work)
+{
+ crash("bh");
+}
+
+static struct work_struct bh_crash_work;
+
+static void crash_bh(void)
+{
+ if (!queue_work(system_bh_wq, &bh_crash_work))
+ return;
+
+ flush_work(&bh_crash_work);
+}
+
+static void crash_user(void)
+{
+ crash("user");
+}
+
+static void crash_user_nobh(void)
+{
+ local_bh_disable();
+ crash("user with bh disabled");
+}
+
+static void crash_user_noirq(void)
+{
+ local_irq_disable();
+ crash("user with irqs disabled");
+}
+
+static void crash_user_nopreempt(void)
+{
+ preempt_disable();
+ crash("user with preemption disabled");
+}
+
+static void crash_user_rculock(void)
+{
+ rcu_read_lock();
+ crash("user in RCU critical section");
+}
+
+static void crash_user_conlock(void)
+{
+ console_lock();
+ crash("user with console_lock held");
+}
+
+static void crash_user_rtnllock(void)
+{
+ rtnl_lock();
+ crash("user with rtnl_lock held");
+}
+
+struct testcase {
+ void (*fn)(void);
+ const char *str;
+};
+
+#define X(name) (struct testcase){.fn = crash_##name, .str = #name "\n" },
+static const struct testcase testcases[] = {
+ CRASH_TESTCASE_LIST
+};
+#undef X
+
+static ssize_t test_crash_write(struct file *file, const char __user *buf,
+ size_t count, loff_t *pos)
+{
+ char tmp[16] = {0};
+ int i;
+
+ if (copy_from_user(tmp, buf, min(count, sizeof(tmp) - 1)) != 0)
+ return -EFAULT;
+
+ for (i = 0; i < ARRAY_SIZE(testcases); i++) {
+ const struct testcase *ctx = testcases + i;
+
+ if (!strcmp(ctx->str, tmp))
+ ctx->fn();
+ }
+
+ return -EINVAL;
+}
+
+static ssize_t test_crash_read(struct file *file, char __user *buf,
+ size_t count, loff_t *pos)
+{
+ #define X(name) #name "\n"
+ static const char help_string[] = CRASH_TESTCASE_LIST;
+ #undef X
+ static const int help_string_len = sizeof(help_string) - 1;
+ int off, len, done;
+
+ off = clamp(*pos, 0, help_string_len);
+ if (off == help_string_len)
+ return 0;
+
+ len = min(count, help_string_len - off);
+ done = len - copy_to_user(buf, help_string + off, len);
+ *pos = off + done;
+
+ return done ?: -EFAULT;
+}
+
+static const struct file_operations test_crash_fops = {
+ .write = test_crash_write,
+ .read = test_crash_read,
+};
+
+static struct dentry *test_crash_dentry;
+
+static int __init setup_test_crash(void)
+{
+ INIT_WORK(&bh_crash_work, crash_bh_work);
+ init_irq_work(&irq_crash_work, crash_irq_work);
+ test_crash_dentry = debugfs_create_file("test_crash", 0600, NULL, NULL,
+ &test_crash_fops);
+ if (IS_ERR(test_crash_dentry))
+ return PTR_ERR(test_crash_dentry);
+
+ return 0;
+}
+late_initcall(setup_test_crash);
+
+static void __exit cleanup_test_crash(void)
+{
+ debugfs_remove(test_crash_dentry);
+}
+module_exit(cleanup_test_crash);
+
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("Calvin Owens <calvin@wbinvd.org>");
+MODULE_DESCRIPTION("Test module to trigger crashes");
--
2.47.3
On Wed, 27 May 2026 09:56:44 -0700 Calvin Owens <calvin@wbinvd.org> wrote: > This test calls panic() from various execution contexts in the kernel, > so the user can see if a crash trace is successfully emitted. > > This is useful for testing console drivers, and can help rule out issues > with the console itself when silent reboots or hangs are observed on a > particular system. Well you aren't the first to write a patch which crashes the kernel ;) > --- > I shared this upthread in [1] where it turned out to be useful for > netconsole. Petr suggested it might be useful for others, so I've > applied his feedback and cleaned everything up a bit more too. > > It's a superset of what 'echo c > /proc/sysrq-trigger' does, but > extending that interface doesn't seem practical. Please fully expand on this? > The write() implementation requries the entire string be written in a > single syscall: this is pretty common for these debugfs interfaces, so > it didn't seem worth the complexity to handle partial writes. But I'm > happy to do so if somebody cares :) Some of the above is useful background and deserves to be above the --- separator. I'm not really liking the "crash" name. We already have one of those - a kexec crashdumping thing. Maybe "force_panic" or someting. > lib/Kconfig.debug | 18 +++++ > lib/Makefile | 1 + > lib/test_crash.c | 176 ++++++++++++++++++++++++++++++++++++++++++++++ Some Documentation would be nice. > --- a/lib/Kconfig.debug > +++ b/lib/Kconfig.debug > @@ -1385,6 +1385,24 @@ config TEST_LOCKUP > > If unsure, say N. > > +config TEST_CRASH > + tristate "Test module to trigger crashes" Some debugfs dependency needed? > + help > + Expose a file in debugfs which triggers a panic() call in > + various kernel execution contexts. > + > + $ cat /sys/kernel/debug/test_crash > + irq > + [...] > + $ echo "irq" > /sys/kernel/debug/test_crash > + Kernel panic - not syncing: User triggered crash in context irq > + > + This is useful for testing console drivers, and can help rule > + out issues with the console itself when silent reboots or > + hangs are observed on a particular system. > + > + If unsure, say N. > + > endmenu # "Debug lockups and hangs" > > menu "Scheduler Debugging" > > ... >
On Wednesday 05/27 at 11:40 -0700, Andrew Morton wrote: > On Wed, 27 May 2026 09:56:44 -0700 Calvin Owens <calvin@wbinvd.org> wrote: > > > This test calls panic() from various execution contexts in the kernel, > > so the user can see if a crash trace is successfully emitted. > > > > This is useful for testing console drivers, and can help rule out issues > > with the console itself when silent reboots or hangs are observed on a > > particular system. > > Well you aren't the first to write a patch which crashes the kernel ;) Thanks for taking a look Andrew. In answering some of your questions, I ran across drivers/misc/lkdtm/ which already does all of this and more... oops :/ So we can drop this, unless Petr sees some value to this new one and wants to chime in. Thanks, Calvin > > --- > > I shared this upthread in [1] where it turned out to be useful for > > netconsole. Petr suggested it might be useful for others, so I've > > applied his feedback and cleaned everything up a bit more too. > > > > It's a superset of what 'echo c > /proc/sysrq-trigger' does, but > > extending that interface doesn't seem practical. > > Please fully expand on this? > > > The write() implementation requries the entire string be written in a > > single syscall: this is pretty common for these debugfs interfaces, so > > it didn't seem worth the complexity to handle partial writes. But I'm > > happy to do so if somebody cares :) > > Some of the above is useful background and deserves to be above the --- > separator. > > I'm not really liking the "crash" name. We already have one of those - > a kexec crashdumping thing. Maybe "force_panic" or someting. > > > > lib/Kconfig.debug | 18 +++++ > > lib/Makefile | 1 + > > lib/test_crash.c | 176 ++++++++++++++++++++++++++++++++++++++++++++++ > > Some Documentation would be nice. > > > --- a/lib/Kconfig.debug > > +++ b/lib/Kconfig.debug > > @@ -1385,6 +1385,24 @@ config TEST_LOCKUP > > > > If unsure, say N. > > > > +config TEST_CRASH > > + tristate "Test module to trigger crashes" > > Some debugfs dependency needed? > > > + help > > + Expose a file in debugfs which triggers a panic() call in > > + various kernel execution contexts. > > + > > + $ cat /sys/kernel/debug/test_crash > > + irq > > + [...] > > + $ echo "irq" > /sys/kernel/debug/test_crash > > + Kernel panic - not syncing: User triggered crash in context irq > > + > > + This is useful for testing console drivers, and can help rule > > + out issues with the console itself when silent reboots or > > + hangs are observed on a particular system. > > + > > + If unsure, say N. > > + > > endmenu # "Debug lockups and hangs" > > > > menu "Scheduler Debugging" > > > > ... > >
© 2016 - 2026 Red Hat, Inc.