[PATCH] lib/test_crash: test module to trigger kernel crashes

Calvin Owens posted 1 patch 1 week, 4 days ago
lib/Kconfig.debug |  18 +++++
lib/Makefile      |   1 +
lib/test_crash.c  | 176 ++++++++++++++++++++++++++++++++++++++++++++++
3 files changed, 195 insertions(+)
create mode 100644 lib/test_crash.c
[PATCH] lib/test_crash: test module to trigger kernel crashes
Posted by Calvin Owens 1 week, 4 days ago
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
Re: [PATCH] lib/test_crash: test module to trigger kernel crashes
Posted by Andrew Morton 1 week, 4 days ago
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"
>
> ...
>
Re: [PATCH] lib/test_crash: test module to trigger kernel crashes
Posted by Calvin Owens 1 week, 4 days ago
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"
> >
> > ...
> >