blob: e3e46dd6cf77b590e05c52e853078ba3d7d8cb1f [file] [log] [blame] [raw]
/*-
* Copyright 2015-2017 Rivoreo
*
* 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
* in this position and unchanged.
* 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.
*
* THIS SOFTWARE IS PROVIDED BY THE AUTHORS ``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.
*
*/
#include <sys/cdefs.h>
#include <sys/param.h>
#include <sys/systm.h>
#include <sys/conf.h>
#include <sys/uio.h>
#include <sys/kernel.h>
#include <sys/malloc.h>
#include <sys/module.h>
#include <sys/filio.h>
#include <sys/lock.h>
#include <sys/mutex.h>
#include <sys/sysctl.h>
#define IOCTL_MYFAULT_DEADLOCK _IO('m', 0x20)
#define IOCTL_MYFAULT_HANG _IO('m', 0x21)
#define IOCTL_MYFAULT_STACK_CORRUPT _IO('m', 0x22)
#define IOCTL_MYFAULT_TRASH_FUNCTION _IO('m', 0x23)
#define IOCTL_MYFAULT_BUFFER_OVERFLOW _IO('m', 0x24)
#define IOCTL_MYFAULT_MEMORY_LEAK _IOW('m', 0x25, unsigned long int)
#define IOCTL_MYFAULT_STACK_OVERFLOW _IO('m', 0x26)
#define IOCTL_MYFAULT_DOUBLE_FREE _IO('m', 0x27)
#define IOCTL_MYFAULT_BREAKPOINT _IO('m', 0x28)
#define IOCTL_MYFAULT_PANIC _IOW('m', 0x29, const char *)
static char *_strchr(const char *p, int ch) {
union {
const char *cp;
char *p;
} u;
u.cp = p;
while(1) {
if (*u.p == ch)
return(u.p);
if (*u.p == '\0')
return(NULL);
u.p++;
}
}
#define strchr _strchr
MALLOC_DEFINE(M_MYFAULT, "faultbuffers", "Leaks, overflows and double frees are expected in this buffer");
/* For use with destroy_dev(9). */
static struct cdev *myfault_dev;
static d_write_t myfault_write;
static d_ioctl_t myfault_ioctl;
static d_read_t myfault_read;
static struct cdevsw myfault_cdevsw = {
.d_version = D_VERSION,
.d_read = myfault_read,
.d_write = myfault_write,
.d_ioctl = myfault_ioctl,
.d_name = "myfault",
//.d_flags = D_MMAP_ANON,
};
static struct mtx mutex;
static void deadlock() {
mtx_init(&mutex, "myfault mutex", "", MTX_DEF);
mtx_lock(&mutex);
mtx_lock(&mutex);
}
static void hang() {
static void *chan = &chan;
tsleep(chan, 0, "HANG", 0);
}
static void stack_corrupt() {
volatile char buffer[256];
memset((char *)buffer + (sizeof buffer / 2), 1, sizeof buffer * 2);
}
static void trash_function() {
*(unsigned int *)sysctl_find_oid = 0xcccccccc;
}
static void buffer_overflow() {
char *buffer = malloc(1024, M_MYFAULT, M_WAITOK);
memset(buffer, 2, 1080);
strcpy(buffer + 1080, "Overflow");
}
static void **leaked;
static void memory_leak(unsigned long int size) {
void **buffer = malloc(size, M_MYFAULT, M_WAITOK);
*buffer = leaked;
leaked = buffer;
}
static void free_leaked() {
while(leaked) {
void *n = *leaked;
free(leaked, M_MYFAULT);
leaked = n;
}
}
static void stack_overflow() {
stack_overflow();
}
static void double_free() {
void *p = malloc(1024, M_MYFAULT, M_WAITOK);
free(p, M_MYFAULT);
free(p, M_MYFAULT);
}
static int myfault_write(struct cdev *dev __unused, struct uio *uio, int flags __unused)
{
char buffer[64];
int len = MIN(uio->uio_resid, sizeof buffer);
int error = uiomove(buffer, len, uio);
if(error) return error;
//uio->uio_resid = 0;
buffer[len] = 0;
char *newline = strchr(buffer, '\n');
if(newline) *newline = 0;
if(!*buffer) return 0;
if(strcmp(buffer, "deadlock") == 0) deadlock();
else if(strcmp(buffer, "hang") == 0) hang();
else if(strcmp(buffer, "stack corrupt") == 0) stack_corrupt();
else if(strcmp(buffer, "trash function") == 0) trash_function();
else if(strcmp(buffer, "buffer overflow") == 0) buffer_overflow();
else if(strncmp(buffer, "leak ", 5) == 0) memory_leak(strtoul(buffer + 5, NULL, 0));
else if(strcmp(buffer, "stack overflow") == 0) stack_overflow();
else if(strcmp(buffer, "double free") == 0) double_free();
else if(strcmp(buffer, "breakpoint") == 0) breakpoint();
else if(strncmp(buffer, "panic ", 6) == 0) panic("%s", buffer + 6);
else printf("myfault: command '%s' not recognized\n", buffer);
uio->uio_resid = 0;
return 0;
}
static int myfault_ioctl(struct cdev *dev __unused, u_long cmd, caddr_t data, int flags __unused, struct thread *td) {
char *buffer;
int error;
switch (cmd) {
case FIONBIO:
break;
case FIOASYNC:
if (*(int *)data != 0) return EINVAL;
break;
case IOCTL_MYFAULT_DEADLOCK:
deadlock();
break;
case IOCTL_MYFAULT_HANG:
hang();
break;
case IOCTL_MYFAULT_STACK_CORRUPT:
stack_corrupt();
break;
case IOCTL_MYFAULT_TRASH_FUNCTION:
trash_function();
break;
case IOCTL_MYFAULT_BUFFER_OVERFLOW:
buffer_overflow();
break;
case IOCTL_MYFAULT_MEMORY_LEAK:
memory_leak(*(unsigned long int *)data);
break;
case IOCTL_MYFAULT_STACK_OVERFLOW:
stack_overflow();
break;
case IOCTL_MYFAULT_DOUBLE_FREE:
double_free();
break;
case IOCTL_MYFAULT_BREAKPOINT:
breakpoint();
break;
case IOCTL_MYFAULT_PANIC:
buffer = malloc(1024, M_MYFAULT, M_WAITOK);
error = copyinstr(*(void **)data, buffer, 1024, NULL);
if(error) snprintf(buffer, 1024, "copyinstr: error %d", error);
panic("%s", buffer);
break;
default:
return ENOIOCTL;
}
return 0;
}
static char *usage;
static int usage_len;
static int myfault_read(struct cdev *dev __unused, struct uio *uio, int flags __unused) {
int error;
if(!usage) {
usage = malloc(1024, M_TEMP, M_WAITOK);
usage_len = snprintf(usage, 1024,
"Welcome to the MyFault device\n\n"
"Usage:\n\n"
"write(2) to the device, following strings are supported as commands:\n"
"deadlock\n"
"hang\n"
"stack corrupt\n"
"trash function\n"
"buffer overflow\n"
"leak <size>\n"
"stack overflow\n"
"double free\n"
"breakpoint\n"
"panic <message>\n\n"
"ioctl(2) the device, supported commands:\n"
"IOCTL_MYFAULT_DEADLOCK = 0x%lx\n"
"IOCTL_MYFAULT_HANG = 0x%lx\n"
"IOCTL_MYFAULT_STACK_CORRUPT = 0x%lx\n"
"IOCTL_MYFAULT_TRASH_FUNCTION = 0x%lx\n"
"IOCTL_MYFAULT_BUFFER_OVERFLOW = 0x%lx\n"
"IOCTL_MYFAULT_MEMORY_LEAK = 0x%lx (with argument 'unsigned long int size')\n"
"IOCTL_MYFAULT_STACK_OVERFLOW = 0x%lx\n"
"IOCTL_MYFAULT_DOUBLE_FREE = 0x%lx\n"
"IOCTL_MYFAULT_BREAKPOINT = 0x%lx\n"
"IOCTL_MYFAULT_PANIC = 0x%lx (with argument 'const char *message')\n\n",
IOCTL_MYFAULT_DEADLOCK,
IOCTL_MYFAULT_HANG,
IOCTL_MYFAULT_STACK_CORRUPT,
IOCTL_MYFAULT_TRASH_FUNCTION,
IOCTL_MYFAULT_BUFFER_OVERFLOW,
IOCTL_MYFAULT_MEMORY_LEAK,
IOCTL_MYFAULT_STACK_OVERFLOW,
IOCTL_MYFAULT_DOUBLE_FREE,
IOCTL_MYFAULT_BREAKPOINT,
IOCTL_MYFAULT_PANIC);
if(usage_len < 0) panic("snprintf failed");
}
KASSERT(uio->uio_rw == UIO_READ,
("Can't be in %s for write", __func__));
if(uio->uio_offset >= usage_len) return 0;
error = uiomove(usage + uio->uio_offset, MIN(uio->uio_resid, usage_len), uio);
uio->uio_resid = 0;
return (error);
}
static int myfault_modevent(module_t mod __unused, int type, void *data __unused)
{
switch(type) {
case MOD_LOAD:
//myfault_dev = make_dev_credf(MAKEDEV_ETERNAL_KLD, &myfault_cdevsw, 0,
// NULL, UID_ROOT, GID_WHEEL, 0666, "myfault");
myfault_dev = make_dev(&myfault_cdevsw, 0, UID_ROOT, GID_WHEEL, 0600, "myfault");
break;
case MOD_UNLOAD:
free(usage, M_TEMP);
free_leaked(); // Free those memory when unloading should be fine
destroy_dev(myfault_dev);
break;
case MOD_SHUTDOWN:
break;
default:
return (EOPNOTSUPP);
}
return (0);
}
DEV_MODULE(myfault, myfault_modevent, NULL);
MODULE_VERSION(myfault, 1);