1 /* $NetBSD: kern_crashme.c,v 1.5 2021/11/27 14:11:14 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 67 #define CMNODE(name, lname, func) \ 68 { \ 69 .cn_name = name, \ 70 .cn_longname = lname, \ 71 .cn_fn = func, \ 72 } 73 74 static crashme_node nodes[] = { 75 CMNODE("panic", "plain old panic", crashme_panic), 76 CMNODE("null_deref", "null dereference", crashme_null_deref), 77 CMNODE("null_jump", "jump to null", crashme_null_jump), 78 #ifdef DDB 79 CMNODE("ddb", "enter ddb directly", crashme_ddb), 80 #endif 81 }; 82 static crashme_node *first_node; 83 static kmutex_t crashme_lock; 84 static const struct sysctlnode *crashme_root = NULL; 85 static bool crashme_enable = 0; 86 87 /* 88 * add a crashme node dynamically. return -1 on failure, 0 on success. 89 */ 90 int 91 crashme_add(crashme_node *ncn) 92 { 93 int rv = -1; 94 crashme_node *cn; 95 crashme_node *last = NULL; 96 97 if (crashme_root == NULL) 98 return -1; 99 100 mutex_enter(&crashme_lock); 101 for (cn = first_node; cn; last = cn, cn = cn->cn_next) { 102 if (strcmp(cn->cn_name, ncn->cn_name) == 0) 103 break; 104 } 105 if (!cn) { 106 ncn->cn_next = NULL; 107 108 rv = sysctl_createv(NULL, 0, 109 &crashme_root, &ncn->cn_sysctl, 110 CTLFLAG_PERMANENT | CTLFLAG_READWRITE, 111 CTLTYPE_INT, ncn->cn_name, 112 SYSCTL_DESCR(ncn->cn_longname), 113 crashme_sysctl_forwarder, 0, 114 NULL, 0, 115 CTL_CREATE, CTL_EOL); 116 117 /* don't insert upon failure */ 118 if (rv == 0) { 119 if (last) 120 last->cn_next = ncn; 121 if (first_node == NULL) 122 first_node = ncn; 123 } 124 } 125 mutex_exit(&crashme_lock); 126 127 return rv; 128 } 129 130 /* 131 * remove a crashme node. return -1 on failure, 0 on success. 132 */ 133 int 134 crashme_remove(crashme_node *rcn) 135 { 136 crashme_node *cn, *prev = NULL; 137 138 mutex_enter(&crashme_lock); 139 for (cn = first_node; cn; prev = cn, cn = cn->cn_next) { 140 int rv; 141 142 if (cn != rcn) 143 continue; 144 145 if (cn == first_node) 146 first_node = cn->cn_next; 147 if (prev) 148 prev->cn_next = cn->cn_next; 149 150 if ((rv = sysctl_destroyv(NULL, CTL_DEBUG, crashme_root, 151 cn->cn_name, CTL_EOL)) == 0) 152 printf("%s: unable to remove %s from sysctl\n", 153 __func__, cn->cn_name); 154 break; 155 } 156 mutex_exit(&crashme_lock); 157 158 if (cn == NULL) 159 return -1; 160 161 return 0; 162 } 163 164 /* 165 * list or execute a crashme node 166 */ 167 static int 168 crashme_sysctl_forwarder(SYSCTLFN_ARGS) 169 { 170 struct sysctlnode node; 171 crashme_node *cn; 172 int error, arg = 0; 173 174 for (cn = first_node; cn; cn = cn->cn_next) { 175 if (cn->cn_sysctl == rnode) 176 break; 177 } 178 if (!cn) { 179 return EINVAL; 180 } 181 182 node = *rnode; 183 node.sysctl_data = &arg; 184 error = sysctl_lookup(SYSCTLFN_CALL(&node)); 185 if (error || newp == NULL) 186 return (error); 187 188 if (!crashme_enable) 189 return EACCES; 190 191 DPRINTF("invoking \"%s\" (%s)", cn->cn_name, cn->cn_longname); 192 if ((*cn->cn_fn)(arg) != 0) 193 panic("crashme on %s failed!\n", cn->cn_name); 194 return 0; 195 } 196 197 /* 198 * register the various nodes with sysctl. 199 */ 200 SYSCTL_SETUP(selfdebug_crashme, "sysctl crashme setup") 201 { 202 int rv; 203 size_t n; 204 205 mutex_init(&crashme_lock, MUTEX_DEFAULT, IPL_NONE); 206 207 rv = sysctl_createv(NULL, 0, NULL, &crashme_root, 208 CTLFLAG_PERMANENT, 209 CTLTYPE_NODE, "crashme", 210 SYSCTL_DESCR("Crashme options"), 211 NULL, 0, NULL, 0, 212 CTL_DEBUG, CTL_CREATE, CTL_EOL); 213 214 if (rv != 0 || crashme_root == NULL) 215 return; 216 217 rv = sysctl_createv(NULL, 0, NULL, NULL, 218 CTLFLAG_PERMANENT|CTLFLAG_READWRITE, 219 CTLTYPE_BOOL, "crashme_enable", 220 SYSCTL_DESCR("Enable crashme"), 221 NULL, 0, &crashme_enable, 0, 222 CTL_DEBUG, CTL_CREATE, CTL_EOL); 223 224 for (n = 0; n < __arraycount(nodes); n++) { 225 crashme_node *cn = &nodes[n]; 226 227 rv = crashme_add(cn); 228 229 /* don't insert */ 230 if (rv != 0) 231 continue; 232 } 233 } 234 235 /* 236 * actual panic functions below 237 */ 238 static int 239 crashme_panic(int flags) 240 { 241 242 panic("crashme plain old panic"); 243 return -1; 244 } 245 246 static int 247 crashme_null_deref(int flags) 248 { 249 250 *(volatile char *)0 = 0; 251 return -1; 252 } 253 254 static int 255 crashme_null_jump(int flags) 256 { 257 void (*volatile f)(int) = NULL; 258 259 (*f)(flags); 260 /* make sure to have a nontrivial return address here */ 261 return -1; 262 } 263 264 #ifdef DDB 265 static int 266 crashme_ddb(int flags) 267 { 268 269 Debugger(); 270 return 0; 271 } 272 #endif 273