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