1 /* $NetBSD: kern_crashme.c,v 1.3 2020/04/23 03:15:47 rin 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 #include <sys/param.h> 40 #include <sys/sysctl.h> 41 #include <sys/systm.h> 42 #include <sys/kthread.h> 43 #include <sys/kmem.h> 44 #include <sys/mutex.h> 45 #include <sys/crashme.h> 46 47 #define DPRINTF(fmt, ...) \ 48 printf("%s:%d: " fmt "\n", __func__, __LINE__, ## __VA_ARGS__) 49 50 static int crashme_sysctl_forwarder(SYSCTLFN_PROTO); 51 52 static int crashme_panic(int); 53 static int crashme_null_deref(int); 54 55 #define CMNODE(name, lname, func) \ 56 { \ 57 .cn_name = name, \ 58 .cn_longname = lname, \ 59 .cn_fn = func, \ 60 } 61 62 static crashme_node nodes[] = { 63 CMNODE("panic", "plain old panic", crashme_panic), 64 CMNODE("null_deref", "null dereference", crashme_null_deref), 65 }; 66 static crashme_node *first_node; 67 static kmutex_t crashme_lock; 68 static const struct sysctlnode *crashme_root = NULL; 69 static bool crashme_enable = 0; 70 71 /* 72 * add a crashme node dynamically. return -1 on failure, 0 on success. 73 */ 74 int 75 crashme_add(crashme_node *ncn) 76 { 77 int rv = -1; 78 crashme_node *cn; 79 crashme_node *last = NULL; 80 81 if (crashme_root == NULL) 82 return -1; 83 84 mutex_enter(&crashme_lock); 85 for (cn = first_node; cn; last = cn, cn = cn->cn_next) { 86 if (strcmp(cn->cn_name, ncn->cn_name) == 0) 87 break; 88 } 89 if (!cn) { 90 ncn->cn_next = NULL; 91 92 rv = sysctl_createv(NULL, 0, 93 &crashme_root, &ncn->cn_sysctl, 94 CTLFLAG_PERMANENT | CTLFLAG_READWRITE, 95 CTLTYPE_INT, ncn->cn_name, 96 SYSCTL_DESCR(ncn->cn_longname), 97 crashme_sysctl_forwarder, 0, 98 NULL, 0, 99 CTL_CREATE, CTL_EOL); 100 101 /* don't insert upon failure */ 102 if (rv == 0) { 103 if (last) 104 last->cn_next = ncn; 105 if (first_node == NULL) 106 first_node = ncn; 107 } 108 } 109 mutex_exit(&crashme_lock); 110 111 return rv; 112 } 113 114 /* 115 * remove a crashme node. return -1 on failure, 0 on success. 116 */ 117 int 118 crashme_remove(crashme_node *rcn) 119 { 120 crashme_node *cn, *prev = NULL; 121 122 mutex_enter(&crashme_lock); 123 for (cn = first_node; cn; prev = cn, cn = cn->cn_next) { 124 int rv; 125 126 if (cn != rcn) 127 continue; 128 129 if (cn == first_node) 130 first_node = cn->cn_next; 131 if (prev) 132 prev->cn_next = cn->cn_next; 133 134 if ((rv = sysctl_destroyv(NULL, CTL_DEBUG, crashme_root, 135 cn->cn_name, CTL_EOL)) == 0) 136 printf("%s: unable to remove %s from sysctl\n", 137 __func__, cn->cn_name); 138 break; 139 } 140 mutex_exit(&crashme_lock); 141 142 if (cn == NULL) 143 return -1; 144 145 return 0; 146 } 147 148 /* 149 * list or execute a crashme node 150 */ 151 static int 152 crashme_sysctl_forwarder(SYSCTLFN_ARGS) 153 { 154 struct sysctlnode node; 155 crashme_node *cn; 156 int error, arg = 0; 157 158 for (cn = first_node; cn; cn = cn->cn_next) { 159 if (cn->cn_sysctl == rnode) 160 break; 161 } 162 if (!cn) { 163 return EINVAL; 164 } 165 166 node = *rnode; 167 node.sysctl_data = &arg; 168 error = sysctl_lookup(SYSCTLFN_CALL(&node)); 169 if (error || newp == NULL) 170 return (error); 171 172 if (!crashme_enable) 173 return EACCES; 174 175 DPRINTF("invoking \"%s\" (%s)", cn->cn_name, cn->cn_longname); 176 if ((*cn->cn_fn)(arg) != 0) 177 panic("crashme on %s failed!\n", cn->cn_name); 178 return 0; 179 } 180 181 /* 182 * register the various nodes with sysctl. 183 */ 184 SYSCTL_SETUP(selfdebug_crashme, "sysctl crashme setup") 185 { 186 int rv; 187 size_t n; 188 189 mutex_init(&crashme_lock, MUTEX_DEFAULT, IPL_NONE); 190 191 rv = sysctl_createv(NULL, 0, NULL, &crashme_root, 192 CTLFLAG_PERMANENT, 193 CTLTYPE_NODE, "crashme", 194 SYSCTL_DESCR("Crashme options"), 195 NULL, 0, NULL, 0, 196 CTL_DEBUG, CTL_CREATE, CTL_EOL); 197 198 if (rv != 0 || crashme_root == NULL) 199 return; 200 201 rv = sysctl_createv(NULL, 0, NULL, NULL, 202 CTLFLAG_PERMANENT|CTLFLAG_READWRITE, 203 CTLTYPE_BOOL, "crashme_enable", 204 SYSCTL_DESCR("Enable crashme"), 205 NULL, 0, &crashme_enable, 0, 206 CTL_DEBUG, CTL_CREATE, CTL_EOL); 207 208 for (n = 0; n < __arraycount(nodes); n++) { 209 crashme_node *cn = &nodes[n]; 210 211 rv = crashme_add(cn); 212 213 /* don't insert */ 214 if (rv != 0) 215 continue; 216 } 217 } 218 219 /* 220 * actual panic functions below 221 */ 222 static int 223 crashme_panic(int flags) 224 { 225 226 panic("crashme plain old panic"); 227 return -1; 228 } 229 230 static int 231 crashme_null_deref(int flags) 232 { 233 234 *(volatile char *)0 = 0; 235 return -1; 236 } 237