crashme: a framework to test kernel faults. so far, only a basic panic() and null deref nodes are added. with options DEBUG and DEBUG_CRASHME, one can now use: # sysctl -w kern.crashme_enable=1 # sysctl -w kern.crashme.panic=1 # sysctl -w kern.crashme.null_deref=1 to trigger a crash. crashme_enable must be set to 1 before any of the nodes will be writeable. supports dynamic additional/removal of crashme nodes. Index: share/man/man9/crashme.9 =================================================================== RCS file: share/man/man9/crashme.9 diff -N share/man/man9/crashme.9 --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ share/man/man9/crashme.9 8 Jan 2019 04:40:02 -0000 @@ -0,0 +1,104 @@ +.\" $NetBSD$ +.\" +.\" Copyright (c) 2019 Matthew R. Green +.\" All rights reserved. +.\" +.\" Redistribution and use in source and binary forms, with or without +.\" modification, are permitted provided that the following conditions +.\" are met: +.\" 1. Redistributions of source code must retain the above copyright +.\" notice, this list of conditions and the following disclaimer. +.\" 2. Redistributions in binary form must reproduce the above copyright +.\" notice, this list of conditions and the following disclaimer in the +.\" documentation and/or other materials provided with the distribution. +.\" 3. The name of the author may not be used to endorse or promote products +.\" derived from this software without specific prior written permission. +.\" +.\" THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR +.\" IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES +.\" OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. +.\" IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, +.\" INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, +.\" BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +.\" LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED +.\" AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, +.\" OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY +.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF +.\" SUCH DAMAGE. +.\" +.Dd January 7, 2019 +.Dt CRASHME 9 +.Os +.Sh NAME +.Nm crashme , +.Nm crashme_add , +.Nm crashme_remove +.Nd in-kernel testing of crash handling +.Sh SYNOPSIS +.In sys/crashme.h +.Ft int +.Fn crashme_add "crashme_node *cn" +.Ft int +.Fn crashme_remove "crashme_node *cn" +.Sh DESCRIPTION +The +.Nm +functions provide access to dynamically add and remove crashme nodes. +These nodes are simply named callbacks that are expected to cause the +system to crash. +.Pp +The crashme functionality is only available in kernels with the +.Xr options 4 +.Dv DEBUG +option set. +.Pp +Each crashme node is maintained in a crashme_node structure which +has the following public members: +.Bd -literal +typedef int (*crashme_fn)(int); + +typedef struct crashme_node { + const char *cn_name; + const char *cn_longname; + crashme_fn cn_fn; +} crashme_node; +.Ed +.Pp +The +caller must fill in the +.Fa cn_name , +.Fa cn_longname , +and +.Fa cn_fn +members. +.Pp +The +.Ar flags +parameter is passed from sysctl. +The return value is 0 upon success or non zero for failure. +.Sh SYSCTL SUPPORT +The following +.Xr sysctl 8 +variables are provided by the +.Nm +subsystem: +.Bl -tag -width "123456" -offset indent +.It Ic debug.crashme_enable +Must be set to 1 for any +.Nm +node to be executed. +.It Ic debug.crashme.panic +Basic panic node. +.It Ic debug.crashme.null_deref +Derefence NULL node. +.Sh SEE ALSO +.Xr options 4 , +.Xr panic 9 +.Sh HISTORY +The +.Nm +driver +appeared in +.Nx 9.0 . +.Sh AUTHORS +.An Matthew R. Green . Index: sys/kern/files.kern =================================================================== RCS file: /cvsroot/src/sys/kern/files.kern,v retrieving revision 1.29 diff -p -u -u -r1.29 files.kern --- sys/kern/files.kern 24 Dec 2018 16:58:54 -0000 1.29 +++ sys/kern/files.kern 8 Jan 2019 04:40:04 -0000 @@ -77,6 +77,7 @@ file kern/kern_rwlock.c kern file kern/kern_rwlock_obj.c kern file kern/kern_scdebug.c kern file kern/kern_sdt.c kdtrace_hooks +file kern/kern_crashme.c debug file kern/kern_sig.c kern file kern/kern_sleepq.c kern file kern/kern_softint.c kern Index: sys/kern/kern_crashme.c =================================================================== RCS file: sys/kern/kern_crashme.c diff -N sys/kern/kern_crashme.c --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ sys/kern/kern_crashme.c 8 Jan 2019 04:40:04 -0000 @@ -0,0 +1,236 @@ +/* $NetBSD$ */ + +/* + * Copyright (c) 2018, 2019 Matthew R. Green + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED + * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +/* + * kern_crashme.c: special debugging routines only enabled in DEBUG + * enabled kernels, designed for debugging kernel crashes. + * + * supports crashme sysctl nodes, to test various ways the system can + * panic or crash. you can add and remove nodes. + */ + +#include +#include +#include +#include +#include +#include +#include + +#define DPRINTF(fmt, ...) \ + printf("%s:%d: " fmt "\n", __func__, __LINE__, ## __VA_ARGS__) + +static int crashme_sysctl_forwarder(SYSCTLFN_PROTO); + +static int crashme_panic(int); +static int crashme_null_deref(int); + +#define CMNODE(name, lname, func) \ + { \ + .cn_name = name, \ + .cn_longname = lname, \ + .cn_fn = func, \ + } + +static crashme_node nodes[] = { + CMNODE("panic", "plain old panic", crashme_panic), + CMNODE("null_deref", "null dereference", crashme_null_deref), +}; +static crashme_node *first_node; +static kmutex_t crashme_lock; +static const struct sysctlnode *crashme_root = NULL; +static bool crashme_enable = 0; + +/* + * add a crashme node dynamically. return -1 on failure, 0 on success. + */ +int +crashme_add(crashme_node *ncn) +{ + int rv = -1; + crashme_node *cn; + crashme_node *last = NULL; + + if (crashme_root == NULL) + return -1; + + mutex_enter(&crashme_lock); + for (cn = first_node; cn; last = cn, cn = cn->cn_next) { + if (strcmp(cn->cn_name, ncn->cn_name) == 0) + break; + } + if (!cn) { + ncn->cn_next = NULL; + + rv = sysctl_createv(NULL, 0, + &crashme_root, &ncn->cn_sysctl, + CTLFLAG_PERMANENT | CTLFLAG_READWRITE, + CTLTYPE_INT, ncn->cn_name, + SYSCTL_DESCR(ncn->cn_longname), + crashme_sysctl_forwarder, 0, + NULL, 0, + CTL_CREATE, CTL_EOL); + + /* don't insert upon failure */ + if (rv == 0) { + if (last) + last->cn_next = ncn; + if (first_node == NULL) + first_node = ncn; + } + } + mutex_exit(&crashme_lock); + + return rv; +} + +/* + * remove a crashme node. return -1 on failure, 0 on success. + */ +int +crashme_remove(crashme_node *rcn) +{ + crashme_node *cn, *prev = NULL; + + mutex_enter(&crashme_lock); + for (cn = first_node; cn; prev = cn, cn = cn->cn_next) { + int rv; + + if (cn != rcn) + continue; + + if (cn == first_node) + first_node = cn->cn_next; + if (prev) + prev->cn_next = cn->cn_next; + + if ((rv = sysctl_destroyv(NULL, CTL_DEBUG, crashme_root, + cn->cn_name, CTL_EOL)) == 0) + printf("%s: unable to remove %s from sysctl\n", + __func__, cn->cn_name); + break; + } + mutex_exit(&crashme_lock); + + if (cn == NULL) + return -1; + + return 0; +} + +/* + * list or execute a crashme node + */ +static int +crashme_sysctl_forwarder(SYSCTLFN_ARGS) +{ + struct sysctlnode node; + crashme_node *cn; + int error, arg = 0; + + for (cn = first_node; cn; cn = cn->cn_next) { + if (cn->cn_sysctl == rnode) + break; + } + if (!cn) { + return EINVAL; + } + + node = *rnode; + node.sysctl_data = &arg; + error = sysctl_lookup(SYSCTLFN_CALL(&node)); + if (error || newp == NULL) + return (error); + + if (!crashme_enable) + return EACCES; + + DPRINTF("invoking \"%s\" (%s)", cn->cn_name, cn->cn_longname); + if ((*cn->cn_fn)(arg) != 0) + panic("crashme on %s failed!\n", cn->cn_name); + return 0; +} + +/* + * register the various nodes with sysctl. + */ +SYSCTL_SETUP(selfdebug_crashme, "sysctl crashme setup") +{ + int rv; + size_t n; + + mutex_init(&crashme_lock, MUTEX_DEFAULT, IPL_NONE); + + rv = sysctl_createv(NULL, 0, NULL, &crashme_root, + CTLFLAG_PERMANENT, + CTLTYPE_NODE, "crashme", + SYSCTL_DESCR("Crashme options"), + NULL, 0, NULL, 0, + CTL_DEBUG, CTL_CREATE, CTL_EOL); + + if (rv != 0 || crashme_root == NULL) + return; + + rv = sysctl_createv(NULL, 0, NULL, NULL, + CTLFLAG_PERMANENT|CTLFLAG_READWRITE, + CTLTYPE_BOOL, "crashme_enable", + SYSCTL_DESCR("Enable crashme"), + NULL, 0, &crashme_enable, 0, + CTL_DEBUG, CTL_CREATE, CTL_EOL); + + for (n = 0; n < __arraycount(nodes); n++) { + crashme_node *cn = &nodes[n]; + + rv = crashme_add(cn); + + /* don't insert */ + if (rv != 0) + continue; + } +} + +/* + * actual panic functions below + */ +static int +crashme_panic(int flags) +{ + + panic("crashme plain old panic"); + return -1; +} + +static int +crashme_null_deref(int flags) +{ + + *(char *)0 = 0; + return -1; +} Index: sys/sys/crashme.h =================================================================== RCS file: sys/sys/crashme.h diff -N sys/sys/crashme.h --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ sys/sys/crashme.h 8 Jan 2019 04:40:05 -0000 @@ -0,0 +1,57 @@ +/* $NetBSD$ */ + +/* + * Copyright (c) 2018, 2019 Matthew R. Green + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED + * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +/* header for kernel crashme node handling. */ + +#ifndef _SYS_CRASHME_H_ +#define _SYS_CRASHME_H_ + +/* + * don't mark this __dead. crashme nodes failures should return to + * the caller so it can be handled. + * + * return zero if successful, and should return to user (may be part + * of the setup needed.) return non-zero otherwise (will call plain + * panic().) + */ +typedef int (*crashme_fn)(int); + +typedef struct crashme_node { + const char *cn_name; + const char *cn_longname; + crashme_fn cn_fn; + const struct sysctlnode *cn_sysctl; + struct crashme_node *cn_next; +} crashme_node; + +int crashme_add(crashme_node *ncn); +int crashme_remove(crashme_node *ncn); + +#endif /* _SYS_CRASHME_H_ */