xref: /netbsd-src/sys/kern/kern_crashme.c (revision 6c8524389b18777f6684e3af05f3c24e3a636b8a)
1*6c852438Sriastradh /*	$NetBSD: kern_crashme.c,v 1.11 2023/07/07 12:34:26 riastradh Exp $	*/
2f40d2554Smrg 
3f40d2554Smrg /*
4f40d2554Smrg  * Copyright (c) 2018, 2019 Matthew R. Green
5f40d2554Smrg  * All rights reserved.
6f40d2554Smrg  *
7f40d2554Smrg  * Redistribution and use in source and binary forms, with or without
8f40d2554Smrg  * modification, are permitted provided that the following conditions
9f40d2554Smrg  * are met:
10f40d2554Smrg  * 1. Redistributions of source code must retain the above copyright
11f40d2554Smrg  *    notice, this list of conditions and the following disclaimer.
12f40d2554Smrg  * 2. Redistributions in binary form must reproduce the above copyright
13f40d2554Smrg  *    notice, this list of conditions and the following disclaimer in the
14f40d2554Smrg  *    documentation and/or other materials provided with the distribution.
15f40d2554Smrg  * 3. The name of the author may not be used to endorse or promote products
16f40d2554Smrg  *    derived from this software without specific prior written permission.
17f40d2554Smrg  *
18f40d2554Smrg  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
19f40d2554Smrg  * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
20f40d2554Smrg  * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
21f40d2554Smrg  * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
22f40d2554Smrg  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
23f40d2554Smrg  * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
24f40d2554Smrg  * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
25f40d2554Smrg  * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
26f40d2554Smrg  * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
27f40d2554Smrg  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
28f40d2554Smrg  * SUCH DAMAGE.
29f40d2554Smrg  */
30f40d2554Smrg 
31f40d2554Smrg /*
3264fa0879Srin  * kern_crashme.c:  special debugging routines, designed for debugging
3364fa0879Srin  * kernel crashes.
34f40d2554Smrg  *
35f40d2554Smrg  * supports crashme sysctl nodes, to test various ways the system can
36f40d2554Smrg  * panic or crash.  you can add and remove nodes.
37f40d2554Smrg  */
38f40d2554Smrg 
3977f5ecb0Sriastradh #ifdef _KERNEL_OPT
4077f5ecb0Sriastradh #include "opt_ddb.h"
4177f5ecb0Sriastradh #endif
4277f5ecb0Sriastradh 
43f40d2554Smrg #include <sys/param.h>
44f40d2554Smrg #include <sys/sysctl.h>
45f40d2554Smrg #include <sys/systm.h>
46f40d2554Smrg #include <sys/kthread.h>
47f40d2554Smrg #include <sys/kmem.h>
48f40d2554Smrg #include <sys/mutex.h>
49f40d2554Smrg #include <sys/crashme.h>
50*6c852438Sriastradh #include <sys/intr.h>
51f40d2554Smrg 
5277f5ecb0Sriastradh #ifdef DDB
5377f5ecb0Sriastradh #include <ddb/ddb.h>
5477f5ecb0Sriastradh #endif
5577f5ecb0Sriastradh 
56f40d2554Smrg #define DPRINTF(fmt, ...) \
57f40d2554Smrg 	printf("%s:%d: " fmt "\n", __func__, __LINE__, ## __VA_ARGS__)
58f40d2554Smrg 
59f40d2554Smrg static int crashme_sysctl_forwarder(SYSCTLFN_PROTO);
60f40d2554Smrg 
61f40d2554Smrg static int crashme_panic(int);
62f40d2554Smrg static int crashme_null_deref(int);
6355309548Sriastradh static int crashme_null_jump(int);
6477f5ecb0Sriastradh #ifdef DDB
6577f5ecb0Sriastradh static int crashme_ddb(int);
6677f5ecb0Sriastradh #endif
6796cfa1eeSriastradh #ifdef LOCKDEBUG
6896cfa1eeSriastradh static int crashme_kernel_lock_spinout(int);
6996cfa1eeSriastradh #endif
70f29b0caeSriastradh static int crashme_mutex_recursion(int);
71*6c852438Sriastradh static int crashme_spl_spinout(int);
72*6c852438Sriastradh static int crashme_kpreempt_spinout(int);
73f40d2554Smrg 
74f40d2554Smrg #define CMNODE(name, lname, func)	\
75f40d2554Smrg     {					\
76f40d2554Smrg 	.cn_name = name,		\
77f40d2554Smrg 	.cn_longname = lname,		\
78f40d2554Smrg 	.cn_fn = func,			\
79f40d2554Smrg     }
80f40d2554Smrg 
81f40d2554Smrg static crashme_node nodes[] = {
82f40d2554Smrg     CMNODE("panic", "plain old panic", crashme_panic),
83f40d2554Smrg     CMNODE("null_deref", "null dereference", crashme_null_deref),
8455309548Sriastradh     CMNODE("null_jump", "jump to null", crashme_null_jump),
8577f5ecb0Sriastradh #ifdef DDB
8677f5ecb0Sriastradh     CMNODE("ddb", "enter ddb directly", crashme_ddb),
8777f5ecb0Sriastradh #endif
8896cfa1eeSriastradh #ifdef LOCKDEBUG
89bd830cddSriastradh     CMNODE("kernel_lock_spinout", "infinite loop under kernel lock",
9096cfa1eeSriastradh 	crashme_kernel_lock_spinout),
9196cfa1eeSriastradh #endif
92f29b0caeSriastradh     CMNODE("mutex_recursion", "enter the same mutex twice",
93f29b0caeSriastradh 	crashme_mutex_recursion),
94*6c852438Sriastradh     CMNODE("spl_spinout", "infinite loop at raised spl",
95*6c852438Sriastradh 	crashme_spl_spinout),
96*6c852438Sriastradh     CMNODE("kpreempt_spinout", "infinite loop with kpreempt disabled",
97*6c852438Sriastradh 	crashme_kpreempt_spinout),
98f40d2554Smrg };
99f40d2554Smrg static crashme_node *first_node;
100f40d2554Smrg static kmutex_t crashme_lock;
101e992cceaSriastradh static int crashme_root = -1;
102f40d2554Smrg static bool crashme_enable = 0;
103f40d2554Smrg 
104f40d2554Smrg /*
105f40d2554Smrg  * add a crashme node dynamically.  return -1 on failure, 0 on success.
106f40d2554Smrg  */
107f40d2554Smrg int
crashme_add(crashme_node * ncn)108f40d2554Smrg crashme_add(crashme_node *ncn)
109f40d2554Smrg {
110f40d2554Smrg 	int rv = -1;
111f40d2554Smrg 	crashme_node *cn;
112f40d2554Smrg 	crashme_node *last = NULL;
113e992cceaSriastradh 	const struct sysctlnode *cnode;
114f40d2554Smrg 
115e992cceaSriastradh 	if (crashme_root == -1)
116f40d2554Smrg 		return -1;
117f40d2554Smrg 
118f40d2554Smrg 	mutex_enter(&crashme_lock);
119f40d2554Smrg 	for (cn = first_node; cn; last = cn, cn = cn->cn_next) {
120f40d2554Smrg 		if (strcmp(cn->cn_name, ncn->cn_name) == 0)
121f40d2554Smrg 			break;
122f40d2554Smrg 	}
123f40d2554Smrg 	if (!cn) {
124f40d2554Smrg 		ncn->cn_next = NULL;
125f40d2554Smrg 
126e992cceaSriastradh 		rv = sysctl_createv(NULL, 0, NULL, &cnode,
127f40d2554Smrg 		    CTLFLAG_PERMANENT | CTLFLAG_READWRITE,
128f40d2554Smrg 		    CTLTYPE_INT, ncn->cn_name,
129f40d2554Smrg 		    SYSCTL_DESCR(ncn->cn_longname),
130e992cceaSriastradh 		    crashme_sysctl_forwarder, 0, NULL, 0,
131e992cceaSriastradh 		    CTL_DEBUG, crashme_root, CTL_CREATE, CTL_EOL);
132f40d2554Smrg 
133f40d2554Smrg 		/* don't insert upon failure */
134f40d2554Smrg 		if (rv == 0) {
135e992cceaSriastradh 			ncn->cn_sysctl = cnode->sysctl_num;
136f40d2554Smrg 			if (last)
137f40d2554Smrg 				last->cn_next = ncn;
138f40d2554Smrg 			if (first_node == NULL)
139f40d2554Smrg 				first_node = ncn;
140f40d2554Smrg 		}
141f40d2554Smrg 	}
142f40d2554Smrg 	mutex_exit(&crashme_lock);
143f40d2554Smrg 
144e1ea08fcSriastradh 	return rv == 0 ? 0 : -1;
145f40d2554Smrg }
146f40d2554Smrg 
147f40d2554Smrg /*
148f40d2554Smrg  * remove a crashme node.  return -1 on failure, 0 on success.
149f40d2554Smrg  */
150f40d2554Smrg int
crashme_remove(crashme_node * rcn)151f40d2554Smrg crashme_remove(crashme_node *rcn)
152f40d2554Smrg {
153f40d2554Smrg 	crashme_node *cn, *prev = NULL;
154f40d2554Smrg 
155f40d2554Smrg 	mutex_enter(&crashme_lock);
156f40d2554Smrg 	for (cn = first_node; cn; prev = cn, cn = cn->cn_next) {
157f40d2554Smrg 		int rv;
158f40d2554Smrg 
159f40d2554Smrg 		if (cn != rcn)
160f40d2554Smrg 			continue;
161f40d2554Smrg 
162f40d2554Smrg 		if (cn == first_node)
163f40d2554Smrg 			first_node = cn->cn_next;
164f40d2554Smrg 		if (prev)
165f40d2554Smrg 			prev->cn_next = cn->cn_next;
166f40d2554Smrg 
167f40d2554Smrg 		if ((rv = sysctl_destroyv(NULL, CTL_DEBUG, crashme_root,
168e992cceaSriastradh 			    cn->cn_sysctl, CTL_EOL)) == 0)
169f40d2554Smrg 			printf("%s: unable to remove %s from sysctl\n",
170f40d2554Smrg 			    __func__, cn->cn_name);
171f40d2554Smrg 		break;
172f40d2554Smrg 	}
173f40d2554Smrg 	mutex_exit(&crashme_lock);
174f40d2554Smrg 
175f40d2554Smrg 	if (cn == NULL)
176f40d2554Smrg 		return -1;
177f40d2554Smrg 
178f40d2554Smrg 	return 0;
179f40d2554Smrg }
180f40d2554Smrg 
181f40d2554Smrg /*
182f40d2554Smrg  * list or execute a crashme node
183f40d2554Smrg  */
184f40d2554Smrg static int
crashme_sysctl_forwarder(SYSCTLFN_ARGS)185f40d2554Smrg crashme_sysctl_forwarder(SYSCTLFN_ARGS)
186f40d2554Smrg {
187f40d2554Smrg 	struct sysctlnode node;
188f40d2554Smrg 	crashme_node *cn;
189f40d2554Smrg 	int error, arg = 0;
190f40d2554Smrg 
191f40d2554Smrg 	for (cn = first_node; cn; cn = cn->cn_next) {
192e992cceaSriastradh 		if (cn->cn_sysctl == rnode->sysctl_num)
193f40d2554Smrg 			break;
194f40d2554Smrg 	}
195f40d2554Smrg 	if (!cn) {
196f40d2554Smrg 		return EINVAL;
197f40d2554Smrg 	}
198f40d2554Smrg 
199f40d2554Smrg 	node = *rnode;
200f40d2554Smrg 	node.sysctl_data = &arg;
201f40d2554Smrg 	error = sysctl_lookup(SYSCTLFN_CALL(&node));
202f40d2554Smrg 	if (error || newp == NULL)
203f40d2554Smrg 		return (error);
204f40d2554Smrg 
205f40d2554Smrg 	if (!crashme_enable)
206f40d2554Smrg 		return EACCES;
207f40d2554Smrg 
208f40d2554Smrg 	DPRINTF("invoking \"%s\" (%s)", cn->cn_name, cn->cn_longname);
209f40d2554Smrg 	if ((*cn->cn_fn)(arg) != 0)
210f40d2554Smrg 		panic("crashme on %s failed!\n", cn->cn_name);
211f40d2554Smrg 	return 0;
212f40d2554Smrg }
213f40d2554Smrg 
214f40d2554Smrg /*
215f40d2554Smrg  * register the various nodes with sysctl.
216f40d2554Smrg  */
217f40d2554Smrg SYSCTL_SETUP(selfdebug_crashme, "sysctl crashme setup")
218f40d2554Smrg {
219e992cceaSriastradh 	const struct sysctlnode *rnode;
220f40d2554Smrg 	int rv;
221f40d2554Smrg 	size_t n;
222f40d2554Smrg 
223f40d2554Smrg 	mutex_init(&crashme_lock, MUTEX_DEFAULT, IPL_NONE);
224f40d2554Smrg 
225e992cceaSriastradh 	KASSERT(crashme_root == -1);
226e992cceaSriastradh 
227e992cceaSriastradh 	rv = sysctl_createv(NULL, 0, NULL, &rnode,
228f40d2554Smrg 	    CTLFLAG_PERMANENT,
229f40d2554Smrg 	    CTLTYPE_NODE, "crashme",
230f40d2554Smrg 	    SYSCTL_DESCR("Crashme options"),
231f40d2554Smrg 	    NULL, 0, NULL, 0,
232f40d2554Smrg 	    CTL_DEBUG, CTL_CREATE, CTL_EOL);
233f40d2554Smrg 
234e992cceaSriastradh 	if (rv != 0) {
235e992cceaSriastradh 		printf("%s: failed to create sysctl debug.crashme: %d\n",
236e992cceaSriastradh 		    __func__, rv);
237f40d2554Smrg 		return;
238e992cceaSriastradh 	}
239e992cceaSriastradh 	crashme_root = rnode->sysctl_num;
240f40d2554Smrg 
241f40d2554Smrg 	rv = sysctl_createv(NULL, 0, NULL, NULL,
242f40d2554Smrg 	    CTLFLAG_PERMANENT|CTLFLAG_READWRITE,
243f40d2554Smrg 	    CTLTYPE_BOOL, "crashme_enable",
244f40d2554Smrg 	    SYSCTL_DESCR("Enable crashme"),
245f40d2554Smrg 	    NULL, 0, &crashme_enable, 0,
246f40d2554Smrg 	    CTL_DEBUG, CTL_CREATE, CTL_EOL);
247e992cceaSriastradh 	if (rv != 0)
248e992cceaSriastradh 		printf("%s: failed to create sysctl debug.crashme_enable:"
249e992cceaSriastradh 		    " %d\n", __func__, rv);
250f40d2554Smrg 
251f40d2554Smrg 	for (n = 0; n < __arraycount(nodes); n++) {
252e992cceaSriastradh 		if (crashme_add(&nodes[n]))
253e992cceaSriastradh 			printf("%s: failed to create sysctl"
254e992cceaSriastradh 			    " debug.crashme.%s\n", __func__, nodes[n].cn_name);
255f40d2554Smrg 	}
256f40d2554Smrg }
257f40d2554Smrg 
258f40d2554Smrg /*
259f40d2554Smrg  * actual panic functions below
260f40d2554Smrg  */
261f40d2554Smrg static int
crashme_panic(int flags)262f40d2554Smrg crashme_panic(int flags)
263f40d2554Smrg {
264f40d2554Smrg 
265f40d2554Smrg 	panic("crashme plain old panic");
266f40d2554Smrg 	return -1;
267f40d2554Smrg }
268f40d2554Smrg 
269f40d2554Smrg static int
crashme_null_deref(int flags)270f40d2554Smrg crashme_null_deref(int flags)
271f40d2554Smrg {
272f40d2554Smrg 
273beb239d2Stnn 	*(volatile char *)0 = 0;
274f40d2554Smrg 	return -1;
275f40d2554Smrg }
27677f5ecb0Sriastradh 
27755309548Sriastradh static int
crashme_null_jump(int flags)27855309548Sriastradh crashme_null_jump(int flags)
27955309548Sriastradh {
28055309548Sriastradh 	void (*volatile f)(int) = NULL;
28155309548Sriastradh 
28255309548Sriastradh 	(*f)(flags);
28355309548Sriastradh 	/* make sure to have a nontrivial return address here */
28455309548Sriastradh 	return -1;
28555309548Sriastradh }
28655309548Sriastradh 
28777f5ecb0Sriastradh #ifdef DDB
28877f5ecb0Sriastradh static int
crashme_ddb(int flags)28977f5ecb0Sriastradh crashme_ddb(int flags)
29077f5ecb0Sriastradh {
29177f5ecb0Sriastradh 
29277f5ecb0Sriastradh 	Debugger();
29377f5ecb0Sriastradh 	return 0;
29477f5ecb0Sriastradh }
29577f5ecb0Sriastradh #endif
29696cfa1eeSriastradh 
29796cfa1eeSriastradh #ifdef LOCKDEBUG
29896cfa1eeSriastradh static int
crashme_kernel_lock_spinout(int flags)29996cfa1eeSriastradh crashme_kernel_lock_spinout(int flags)
30096cfa1eeSriastradh {
30196cfa1eeSriastradh 
30296cfa1eeSriastradh 	KERNEL_LOCK(1, NULL);
30396cfa1eeSriastradh 	for (;;)
30496cfa1eeSriastradh 		__insn_barrier();
30596cfa1eeSriastradh 	KERNEL_UNLOCK_ONE(NULL);
30696cfa1eeSriastradh 	return 0;
30796cfa1eeSriastradh }
30896cfa1eeSriastradh #endif
309f29b0caeSriastradh 
310f29b0caeSriastradh static int
crashme_mutex_recursion(int flags)311f29b0caeSriastradh crashme_mutex_recursion(int flags)
312f29b0caeSriastradh {
313f29b0caeSriastradh 	kmutex_t crashme_spinlock;
314f29b0caeSriastradh 
315f29b0caeSriastradh 	switch (flags) {
316f29b0caeSriastradh 	case 0:
317f29b0caeSriastradh 		return 0;
318f29b0caeSriastradh 	case 1:
319f29b0caeSriastradh 	default:
320f29b0caeSriastradh 		/*
321f29b0caeSriastradh 		 * printf makes the return address of the first
322f29b0caeSriastradh 		 * mutex_enter call a little more obvious, so the line
323f29b0caeSriastradh 		 * number of the _return address_ for the first
324f29b0caeSriastradh 		 * mutex_enter doesn't confusingly point at the second
325f29b0caeSriastradh 		 * mutex_enter.
326f29b0caeSriastradh 		 */
327f29b0caeSriastradh 		mutex_enter(&crashme_lock);
328f29b0caeSriastradh 		printf("%s: locked once\n", __func__);
329f29b0caeSriastradh 		mutex_enter(&crashme_lock);
330f29b0caeSriastradh 		printf("%s: locked twice\n", __func__);
331f29b0caeSriastradh 		return -1;
332f29b0caeSriastradh 	case 2:
333f29b0caeSriastradh 		mutex_init(&crashme_spinlock, MUTEX_DEFAULT, IPL_VM);
334f29b0caeSriastradh 		printf("%s: initialized\n", __func__);
335f29b0caeSriastradh 		mutex_enter(&crashme_spinlock);
336f29b0caeSriastradh 		printf("%s: locked once\n", __func__);
337f29b0caeSriastradh 		mutex_enter(&crashme_spinlock);
338f29b0caeSriastradh 		printf("%s: locked twice\n", __func__);
339f29b0caeSriastradh 		return -1;
340f29b0caeSriastradh 	}
341f29b0caeSriastradh }
342*6c852438Sriastradh 
343*6c852438Sriastradh static int
crashme_spl_spinout(int flags)344*6c852438Sriastradh crashme_spl_spinout(int flags)
345*6c852438Sriastradh {
346*6c852438Sriastradh 	int s;
347*6c852438Sriastradh 
348*6c852438Sriastradh 	printf("%s: raising ipl to %d\n", __func__, flags);
349*6c852438Sriastradh 	s = splraiseipl(makeiplcookie(flags));
350*6c852438Sriastradh 	printf("%s: raised ipl to %d, s=%d\n", __func__, flags, s);
351*6c852438Sriastradh 	for (;;)
352*6c852438Sriastradh 		__insn_barrier();
353*6c852438Sriastradh 	printf("%s: exited infinite loop!?\n", __func__);
354*6c852438Sriastradh 	splx(s);
355*6c852438Sriastradh 	printf("%s: lowered ipl to s=%d\n", __func__, s);
356*6c852438Sriastradh 
357*6c852438Sriastradh 	return 0;
358*6c852438Sriastradh }
359*6c852438Sriastradh 
360*6c852438Sriastradh static int
crashme_kpreempt_spinout(int flags)361*6c852438Sriastradh crashme_kpreempt_spinout(int flags)
362*6c852438Sriastradh {
363*6c852438Sriastradh 
364*6c852438Sriastradh 	kpreempt_disable();
365*6c852438Sriastradh 	printf("%s: disabled kpreemption\n", __func__);
366*6c852438Sriastradh 	for (;;)
367*6c852438Sriastradh 		__insn_barrier();
368*6c852438Sriastradh 	printf("%s: exited infinite loop!?\n", __func__);
369*6c852438Sriastradh 	kpreempt_enable();
370*6c852438Sriastradh 	printf("%s: re-enabled kpreemption\n", __func__);
371*6c852438Sriastradh 
372*6c852438Sriastradh 	return 0;
373*6c852438Sriastradh }
374