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