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