1 /* $NetBSD: kern_crashme.c,v 1.11 2023/07/07 12:34:26 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 #include <sys/sysctl.h> 45 #include <sys/systm.h> 46 #include <sys/kthread.h> 47 #include <sys/kmem.h> 48 #include <sys/mutex.h> 49 #include <sys/crashme.h> 50 #include <sys/intr.h> 51 52 #ifdef DDB 53 #include <ddb/ddb.h> 54 #endif 55 56 #define DPRINTF(fmt, ...) \ 57 printf("%s:%d: " fmt "\n", __func__, __LINE__, ## __VA_ARGS__) 58 59 static int crashme_sysctl_forwarder(SYSCTLFN_PROTO); 60 61 static int crashme_panic(int); 62 static int crashme_null_deref(int); 63 static int crashme_null_jump(int); 64 #ifdef DDB 65 static int crashme_ddb(int); 66 #endif 67 #ifdef LOCKDEBUG 68 static int crashme_kernel_lock_spinout(int); 69 #endif 70 static int crashme_mutex_recursion(int); 71 static int crashme_spl_spinout(int); 72 static int crashme_kpreempt_spinout(int); 73 74 #define CMNODE(name, lname, func) \ 75 { \ 76 .cn_name = name, \ 77 .cn_longname = lname, \ 78 .cn_fn = func, \ 79 } 80 81 static crashme_node nodes[] = { 82 CMNODE("panic", "plain old panic", crashme_panic), 83 CMNODE("null_deref", "null dereference", crashme_null_deref), 84 CMNODE("null_jump", "jump to null", crashme_null_jump), 85 #ifdef DDB 86 CMNODE("ddb", "enter ddb directly", crashme_ddb), 87 #endif 88 #ifdef LOCKDEBUG 89 CMNODE("kernel_lock_spinout", "infinite loop under kernel lock", 90 crashme_kernel_lock_spinout), 91 #endif 92 CMNODE("mutex_recursion", "enter the same mutex twice", 93 crashme_mutex_recursion), 94 CMNODE("spl_spinout", "infinite loop at raised spl", 95 crashme_spl_spinout), 96 CMNODE("kpreempt_spinout", "infinite loop with kpreempt disabled", 97 crashme_kpreempt_spinout), 98 }; 99 static crashme_node *first_node; 100 static kmutex_t crashme_lock; 101 static int crashme_root = -1; 102 static bool crashme_enable = 0; 103 104 /* 105 * add a crashme node dynamically. return -1 on failure, 0 on success. 106 */ 107 int 108 crashme_add(crashme_node *ncn) 109 { 110 int rv = -1; 111 crashme_node *cn; 112 crashme_node *last = NULL; 113 const struct sysctlnode *cnode; 114 115 if (crashme_root == -1) 116 return -1; 117 118 mutex_enter(&crashme_lock); 119 for (cn = first_node; cn; last = cn, cn = cn->cn_next) { 120 if (strcmp(cn->cn_name, ncn->cn_name) == 0) 121 break; 122 } 123 if (!cn) { 124 ncn->cn_next = NULL; 125 126 rv = sysctl_createv(NULL, 0, NULL, &cnode, 127 CTLFLAG_PERMANENT | CTLFLAG_READWRITE, 128 CTLTYPE_INT, ncn->cn_name, 129 SYSCTL_DESCR(ncn->cn_longname), 130 crashme_sysctl_forwarder, 0, NULL, 0, 131 CTL_DEBUG, crashme_root, CTL_CREATE, CTL_EOL); 132 133 /* don't insert upon failure */ 134 if (rv == 0) { 135 ncn->cn_sysctl = cnode->sysctl_num; 136 if (last) 137 last->cn_next = ncn; 138 if (first_node == NULL) 139 first_node = ncn; 140 } 141 } 142 mutex_exit(&crashme_lock); 143 144 return rv == 0 ? 0 : -1; 145 } 146 147 /* 148 * remove a crashme node. return -1 on failure, 0 on success. 149 */ 150 int 151 crashme_remove(crashme_node *rcn) 152 { 153 crashme_node *cn, *prev = NULL; 154 155 mutex_enter(&crashme_lock); 156 for (cn = first_node; cn; prev = cn, cn = cn->cn_next) { 157 int rv; 158 159 if (cn != rcn) 160 continue; 161 162 if (cn == first_node) 163 first_node = cn->cn_next; 164 if (prev) 165 prev->cn_next = cn->cn_next; 166 167 if ((rv = sysctl_destroyv(NULL, CTL_DEBUG, crashme_root, 168 cn->cn_sysctl, CTL_EOL)) == 0) 169 printf("%s: unable to remove %s from sysctl\n", 170 __func__, cn->cn_name); 171 break; 172 } 173 mutex_exit(&crashme_lock); 174 175 if (cn == NULL) 176 return -1; 177 178 return 0; 179 } 180 181 /* 182 * list or execute a crashme node 183 */ 184 static int 185 crashme_sysctl_forwarder(SYSCTLFN_ARGS) 186 { 187 struct sysctlnode node; 188 crashme_node *cn; 189 int error, arg = 0; 190 191 for (cn = first_node; cn; cn = cn->cn_next) { 192 if (cn->cn_sysctl == rnode->sysctl_num) 193 break; 194 } 195 if (!cn) { 196 return EINVAL; 197 } 198 199 node = *rnode; 200 node.sysctl_data = &arg; 201 error = sysctl_lookup(SYSCTLFN_CALL(&node)); 202 if (error || newp == NULL) 203 return (error); 204 205 if (!crashme_enable) 206 return EACCES; 207 208 DPRINTF("invoking \"%s\" (%s)", cn->cn_name, cn->cn_longname); 209 if ((*cn->cn_fn)(arg) != 0) 210 panic("crashme on %s failed!\n", cn->cn_name); 211 return 0; 212 } 213 214 /* 215 * register the various nodes with sysctl. 216 */ 217 SYSCTL_SETUP(selfdebug_crashme, "sysctl crashme setup") 218 { 219 const struct sysctlnode *rnode; 220 int rv; 221 size_t n; 222 223 mutex_init(&crashme_lock, MUTEX_DEFAULT, IPL_NONE); 224 225 KASSERT(crashme_root == -1); 226 227 rv = sysctl_createv(NULL, 0, NULL, &rnode, 228 CTLFLAG_PERMANENT, 229 CTLTYPE_NODE, "crashme", 230 SYSCTL_DESCR("Crashme options"), 231 NULL, 0, NULL, 0, 232 CTL_DEBUG, CTL_CREATE, CTL_EOL); 233 234 if (rv != 0) { 235 printf("%s: failed to create sysctl debug.crashme: %d\n", 236 __func__, rv); 237 return; 238 } 239 crashme_root = rnode->sysctl_num; 240 241 rv = sysctl_createv(NULL, 0, NULL, NULL, 242 CTLFLAG_PERMANENT|CTLFLAG_READWRITE, 243 CTLTYPE_BOOL, "crashme_enable", 244 SYSCTL_DESCR("Enable crashme"), 245 NULL, 0, &crashme_enable, 0, 246 CTL_DEBUG, CTL_CREATE, CTL_EOL); 247 if (rv != 0) 248 printf("%s: failed to create sysctl debug.crashme_enable:" 249 " %d\n", __func__, rv); 250 251 for (n = 0; n < __arraycount(nodes); n++) { 252 if (crashme_add(&nodes[n])) 253 printf("%s: failed to create sysctl" 254 " debug.crashme.%s\n", __func__, nodes[n].cn_name); 255 } 256 } 257 258 /* 259 * actual panic functions below 260 */ 261 static int 262 crashme_panic(int flags) 263 { 264 265 panic("crashme plain old panic"); 266 return -1; 267 } 268 269 static int 270 crashme_null_deref(int flags) 271 { 272 273 *(volatile char *)0 = 0; 274 return -1; 275 } 276 277 static int 278 crashme_null_jump(int flags) 279 { 280 void (*volatile f)(int) = NULL; 281 282 (*f)(flags); 283 /* make sure to have a nontrivial return address here */ 284 return -1; 285 } 286 287 #ifdef DDB 288 static int 289 crashme_ddb(int flags) 290 { 291 292 Debugger(); 293 return 0; 294 } 295 #endif 296 297 #ifdef LOCKDEBUG 298 static int 299 crashme_kernel_lock_spinout(int flags) 300 { 301 302 KERNEL_LOCK(1, NULL); 303 for (;;) 304 __insn_barrier(); 305 KERNEL_UNLOCK_ONE(NULL); 306 return 0; 307 } 308 #endif 309 310 static int 311 crashme_mutex_recursion(int flags) 312 { 313 kmutex_t crashme_spinlock; 314 315 switch (flags) { 316 case 0: 317 return 0; 318 case 1: 319 default: 320 /* 321 * printf makes the return address of the first 322 * mutex_enter call a little more obvious, so the line 323 * number of the _return address_ for the first 324 * mutex_enter doesn't confusingly point at the second 325 * mutex_enter. 326 */ 327 mutex_enter(&crashme_lock); 328 printf("%s: locked once\n", __func__); 329 mutex_enter(&crashme_lock); 330 printf("%s: locked twice\n", __func__); 331 return -1; 332 case 2: 333 mutex_init(&crashme_spinlock, MUTEX_DEFAULT, IPL_VM); 334 printf("%s: initialized\n", __func__); 335 mutex_enter(&crashme_spinlock); 336 printf("%s: locked once\n", __func__); 337 mutex_enter(&crashme_spinlock); 338 printf("%s: locked twice\n", __func__); 339 return -1; 340 } 341 } 342 343 static int 344 crashme_spl_spinout(int flags) 345 { 346 int s; 347 348 printf("%s: raising ipl to %d\n", __func__, flags); 349 s = splraiseipl(makeiplcookie(flags)); 350 printf("%s: raised ipl to %d, s=%d\n", __func__, flags, s); 351 for (;;) 352 __insn_barrier(); 353 printf("%s: exited infinite loop!?\n", __func__); 354 splx(s); 355 printf("%s: lowered ipl to s=%d\n", __func__, s); 356 357 return 0; 358 } 359 360 static int 361 crashme_kpreempt_spinout(int flags) 362 { 363 364 kpreempt_disable(); 365 printf("%s: disabled kpreemption\n", __func__); 366 for (;;) 367 __insn_barrier(); 368 printf("%s: exited infinite loop!?\n", __func__); 369 kpreempt_enable(); 370 printf("%s: re-enabled kpreemption\n", __func__); 371 372 return 0; 373 } 374